From d7ce700605e1af0e455e31ec11f19ff21d26b525 Mon Sep 17 00:00:00 2001 From: darylm503 Date: Sat, 30 Jul 2011 00:30:44 +0000 Subject: Add Socket Libraries. Add Posix functions for porting compatibility. Fix compliance issues with ISO/IEC 9899:199409 New Functions: setenv(), fparseln(), GetFileNameFromPath(), rename(), realpath(), setprogname(), getprogname(), strlcat(), strlcpy(), strsep(), setitimer(), getitimer(), timegm(), getopt(), basename(), mkstemp(), ffs(), vsnprintf(), snprintf(), getpass(), usleep(), select(), writev(), strcasecmp(), getcwd(), chdir(), tcgetpgrp(), getpgrp(), gettimeofday(), bcopy(), git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12061 6f19259b-4bc3-4df7-8a09-765794883524 --- StdLib/EfiSocketLib/EfiSocketLib.inf | 56 + StdLib/EfiSocketLib/Init.c | 87 + StdLib/EfiSocketLib/Service.c | 529 +++++ StdLib/EfiSocketLib/Socket.c | 3091 +++++++++++++++++++++++++++++ StdLib/EfiSocketLib/Socket.h | 1337 +++++++++++++ StdLib/EfiSocketLib/Tcp4.c | 3415 +++++++++++++++++++++++++++++++++ StdLib/EfiSocketLib/Udp4.c | 2408 +++++++++++++++++++++++ StdLib/EfiSocketLib/UseEfiSocketLib.c | 242 +++ 8 files changed, 11165 insertions(+) create mode 100644 StdLib/EfiSocketLib/EfiSocketLib.inf create mode 100644 StdLib/EfiSocketLib/Init.c create mode 100644 StdLib/EfiSocketLib/Service.c create mode 100644 StdLib/EfiSocketLib/Socket.c create mode 100644 StdLib/EfiSocketLib/Socket.h create mode 100644 StdLib/EfiSocketLib/Tcp4.c create mode 100644 StdLib/EfiSocketLib/Udp4.c create mode 100644 StdLib/EfiSocketLib/UseEfiSocketLib.c (limited to 'StdLib/EfiSocketLib') diff --git a/StdLib/EfiSocketLib/EfiSocketLib.inf b/StdLib/EfiSocketLib/EfiSocketLib.inf new file mode 100644 index 0000000000..9a30193f84 --- /dev/null +++ b/StdLib/EfiSocketLib/EfiSocketLib.inf @@ -0,0 +1,56 @@ +#/** @file +# Component description file for the EFI socket library. +# +# This module implements the socket layer. +# Copyright (c) 2011, Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EfiSocketLib + FILE_GUID = C33E0B7C-9D0F-41df-BDFD-08F5E4C39EE8 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = EfiSocketLib + CONSTRUCTOR = EslConstructor + DESTRUCTOR = EslDestructor + +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + Init.c + Service.c + Socket.c + Tcp4.c + Udp4.c + UseEfiSocketLib.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + StdLib/StdLib.dec +# SocketPkg/SocketPkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + UefiBootServicesTableLib + UefiLib + +[Protocols] + gEfiTcp4ProtocolGuid + gEfiTcp4ServiceBindingProtocolGuid + gEfiUdp4ProtocolGuid + gEfiUdp4ServiceBindingProtocolGuid + gEfiSocketProtocolGuid + gEfiSocketServiceBindingProtocolGuid diff --git a/StdLib/EfiSocketLib/Init.c b/StdLib/EfiSocketLib/Init.c new file mode 100644 index 0000000000..7a1b89da87 --- /dev/null +++ b/StdLib/EfiSocketLib/Init.c @@ -0,0 +1,87 @@ +/** @file + Implement the constructor and destructor for the EFI socket library + + Copyright (c) 2011, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + + +/** + EFI Socket Library Constructor + + @retval EFI_SUCCESS The initialization was successful + + **/ +EFI_STATUS +EFIAPI +EslConstructor ( + VOID + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Call the image dependent constructor if available + // + if ( NULL != mpfnEslConstructor ) { + Status = mpfnEslConstructor ( ); + } + + // + // Return the constructor status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + EFI Socket Library Destructor + + @retval EFI_SUCCESS The shutdown was successful + + **/ +EFI_STATUS +EFIAPI +EslDestructor ( + VOID + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Call the image dependent destructor if available + // + if ( NULL != mpfnEslDestructor ) { + Status = mpfnEslDestructor ( ); + } + + // + // Return the constructor status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} diff --git a/StdLib/EfiSocketLib/Service.c b/StdLib/EfiSocketLib/Service.c new file mode 100644 index 0000000000..49c8884612 --- /dev/null +++ b/StdLib/EfiSocketLib/Service.c @@ -0,0 +1,529 @@ +/** @file + Connect to and disconnect from the various network layers + + Copyright (c) 2011, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Socket.h" + +EFI_TCP4_PROTOCOL * mpEfiTcpClose4 [ 1024 ]; + + +/** + Connect to the network service bindings + + Walk the network service protocols on the controller handle and + locate any that are not in use. Create service structures to + manage the service binding for the socket driver. + + @param [in] BindingHandle Handle for protocol binding. + @param [in] Controller Handle of device to work with. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +EslServiceConnect ( + IN EFI_HANDLE BindingHandle, + IN EFI_HANDLE Controller + ) +{ + BOOLEAN bInUse; + UINTN LengthInBytes; + CONST DT_SOCKET_BINDING * pEnd; + VOID * pJunk; + VOID * pInterface; + DT_SERVICE * pService; + CONST DT_SOCKET_BINDING * pSocketBinding; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume the list is empty + // + Status = EFI_UNSUPPORTED; + bInUse = FALSE; + + // + // Walk the list of network connection points + // + pSocketBinding = &cEslSocketBinding[0]; + pEnd = &pSocketBinding[ cEslSocketBindingEntries ]; + while ( pEnd > pSocketBinding ) { + // + // Determine if the controller supports the network protocol + // + Status = gBS->OpenProtocol ( + Controller, + pSocketBinding->pNetworkBinding, + &pInterface, + BindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if ( !EFI_ERROR ( Status )) { + // + // Determine if the socket layer is already connected + // + Status = gBS->OpenProtocol ( + Controller, + (EFI_GUID *)pSocketBinding->pTagGuid, + &pJunk, + BindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if ( EFI_UNSUPPORTED == Status ) { + // + // Allocate a service structure since the tag is not present + // + LengthInBytes = sizeof ( *pService ); + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + LengthInBytes, + (VOID **) &pService + ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INIT, + "0x%08x: Allocate pService, %d bytes\r\n", + pService, + LengthInBytes )); + + // + // Set the structure signature and service binding + // + ZeroMem ( pService, LengthInBytes ); + pService->Signature = SERVICE_SIGNATURE; + pService->pSocketBinding = pSocketBinding; + pService->Controller = Controller; + pService->pInterface = pInterface; + + // + // Mark the controller in use + // + if ( !bInUse ) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiCallerIdGuid, + NULL, + NULL + ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Installed: gEfiCallerIdGuid on 0x%08x\r\n", + Controller )); + bInUse = TRUE; + } + else { + if ( EFI_INVALID_PARAMETER == Status ) { + Status = EFI_SUCCESS; + } + } + } + if ( !EFI_ERROR ( Status )) { + // + // Mark the network service protocol in use + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + pSocketBinding->pTagGuid, + pService, + NULL + ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Installed: %s TagGuid on 0x%08x\r\n", + pSocketBinding->pName, + Controller )); + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Initialize the service + // + Status = pSocketBinding->pfnInitialize ( pService ); + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + + // + // Determine if the initialization was successful + // + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT, + "ERROR - Failed to initialize service %s on 0x%08x, Status: %r\r\n", + pSocketBinding->pName, + Controller, + Status )); + + // + // Free the network service binding if necessary + // + gBS->UninstallMultipleProtocolInterfaces ( + Controller, + pSocketBinding->pTagGuid, + pService, + NULL ); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Removed: %s TagGuid from 0x%08x\r\n", + pSocketBinding->pName, + Controller )); + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT, + "ERROR - Failed to install %s TagGuid on 0x%08x, Status: %r\r\n", + pSocketBinding->pName, + Controller, + Status )); + } + + if ( EFI_ERROR ( Status )) { + // + // The controller is no longer in use + // + if ( bInUse ) { + gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiCallerIdGuid, + NULL, + NULL ); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Removed: gEfiCallerIdGuid from 0x%08x\r\n", + Controller )); + } + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_INIT, + "ERROR - Failed to install gEfiCallerIdGuid on 0x%08x, Status: %r\r\n", + Controller, + Status )); + } + + // + // Release the service if necessary + // + if ( EFI_ERROR ( Status )) { + gBS->FreePool ( pService ); + DEBUG (( DEBUG_POOL | DEBUG_INIT, + "0x%08x: Free pService, %d bytes\r\n", + pService, + sizeof ( *pService ))); + pService = NULL; + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_INIT, + "ERROR - Failed service allocation, Status: %r\r\n", + Status )); + } + } + } + + // + // Set the next network protocol + // + pSocketBinding += 1; + } + + // + // Display the driver start status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Shutdown the network connections to this controller by removing + NetworkInterfaceIdentifier protocol and closing the DevicePath + and PciIo protocols on Controller. + + @param [in] BindingHandle Handle for protocol binding. + @param [in] Controller Handle of device to stop driver on. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +EslServiceDisconnect ( + IN EFI_HANDLE BindingHandle, + IN EFI_HANDLE Controller + ) +{ + CONST DT_SOCKET_BINDING * pEnd; + DT_SERVICE * pService; + CONST DT_SOCKET_BINDING * pSocketBinding; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Walk the list of network connection points in reverse order + // + pEnd = &cEslSocketBinding[0]; + pSocketBinding = &pEnd[ cEslSocketBindingEntries ]; + while ( pEnd < pSocketBinding ) { + // + // Set the next network protocol + // + pSocketBinding -= 1; + + // + // Determine if the driver connected + // + Status = gBS->OpenProtocol ( + Controller, + (EFI_GUID *)pSocketBinding->pTagGuid, + (VOID **)&pService, + BindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if ( !EFI_ERROR ( Status )) { + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Shutdown the service + // + pSocketBinding->pfnShutdown ( pService ); + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + + // + // Break the driver connection + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + pSocketBinding->pTagGuid, + pService, + NULL ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INIT, + "Removed: %s TagGuid from 0x%08x\r\n", + pSocketBinding->pName, + Controller )); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT, + "ERROR - Failed to removed %s TagGuid from 0x%08x, Status: %r\r\n", + pSocketBinding->pName, + Controller, + Status )); + } + + // + // Free the service structure + // + Status = gBS->FreePool ( pService ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INIT, + "0x%08x: Free pService, %d bytes\r\n", + pService, + sizeof ( *pService ))); + } + else { + DEBUG (( DEBUG_POOL | DEBUG_INIT, + "ERROR - Failed to free pService 0x%08x, Status: %r\r\n", + pService, + Status )); + } + pService = NULL; + } + } + + // + // The controller is no longer in use + // + gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiCallerIdGuid, + NULL, + NULL ); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Removed: gEfiCallerIdGuid from 0x%08x\r\n", + Controller )); + + // + // The driver is disconnected from the network controller + // + Status = EFI_SUCCESS; + + // + // Display the driver start status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + + +/** +Install the socket service + +@param [in] pImageHandle Address of the image handle + +@retval EFI_SUCCESS Service installed successfully +**/ +EFI_STATUS +EFIAPI +EslServiceInstall ( + IN EFI_HANDLE * pImageHandle + ) +{ + EFI_STATUS Status; + + // + // Install the socket service binding protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + pImageHandle, + &gEfiSocketServiceBindingProtocolGuid, + &mEslLayer.ServiceBinding, + NULL + ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Installed: gEfiSocketServiceBindingProtocolGuid on 0x%08x\r\n", + *pImageHandle )); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT, + "ERROR - InstallMultipleProtocolInterfaces failed, Status: %r\r\n", + Status )); + } + + // + // Return the operation status + // + return Status; +} + + +/** +Initialize the service layer + +@param [in] ImageHandle Handle for the image. + +**/ +VOID +EFIAPI +EslServiceLoad ( + IN EFI_HANDLE ImageHandle + ) +{ + DT_LAYER * pLayer; + + // + // Save the image handle + // + pLayer = &mEslLayer; + pLayer->Signature = LAYER_SIGNATURE; + pLayer->ImageHandle = ImageHandle; + + // + // Initialize the TCP4 close + // + pLayer->TcpCloseMax4 = DIM ( mpEfiTcpClose4 ); + pLayer->ppTcpClose4 = mpEfiTcpClose4; + + // + // Connect the service binding protocol to the image handle + // + pLayer->ServiceBinding.CreateChild = EslSocketCreateChild; + pLayer->ServiceBinding.DestroyChild = EslSocketDestroyChild; +} + + +/** +Uninstall the socket service + +@param [in] ImageHandle Handle for the image. + +@retval EFI_SUCCESS Service installed successfully +**/ +EFI_STATUS +EFIAPI +EslServiceUninstall ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + + // + // Install the socket service binding protocol + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiSocketServiceBindingProtocolGuid, + &mEslLayer.ServiceBinding, + NULL + ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INIT, + "Removed: gEfiSocketServiceBindingProtocolGuid from 0x%08x\r\n", + ImageHandle )); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT, + "ERROR - Failed to remove gEfiSocketServiceBindingProtocolGuid from 0x%08x, Status: %r\r\n", + ImageHandle, + Status )); + } + + // + // Return the operation status + // + return Status; +} + + +/** + Shutdown the service layer + +**/ +VOID +EFIAPI +EslServiceUnload ( + VOID + ) +{ + DT_LAYER * pLayer; + + // + // Undo the work by ServiceLoad + // + pLayer = &mEslLayer; + pLayer->ImageHandle = NULL; + pLayer->ServiceBinding.CreateChild = NULL; + pLayer->ServiceBinding.DestroyChild = NULL; +} diff --git a/StdLib/EfiSocketLib/Socket.c b/StdLib/EfiSocketLib/Socket.c new file mode 100644 index 0000000000..bad888cdf5 --- /dev/null +++ b/StdLib/EfiSocketLib/Socket.c @@ -0,0 +1,3091 @@ +/** @file + Implement the socket support for the socket layer. + + Socket States: + * Bound - pSocket->PortList is not NULL + * Listen - AcceptWait event is not NULL + + Copyright (c) 2011, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Socket.h" + + +/** + Socket driver connection points + + List the network stack connection points for the socket driver. +**/ +CONST DT_SOCKET_BINDING cEslSocketBinding [] = { + { L"Tcp4", + &gEfiTcp4ServiceBindingProtocolGuid, + &mEslTcp4ServiceGuid, + EslTcpInitialize4, + EslTcpShutdown4 }, + { L"Udp4", + &gEfiUdp4ServiceBindingProtocolGuid, + &mEslUdp4ServiceGuid, + EslUdpInitialize4, + EslUdpShutdown4 } +}; + +CONST UINTN cEslSocketBindingEntries = DIM ( cEslSocketBinding ); + +DT_LAYER mEslLayer; + + +/** + Initialize an endpoint for network communication. + + The ::Socket routine initializes the communication endpoint by providing + the support for the socket library function ::socket. The + Linux, + POSIX + and Windows + documentation for the socket routine are available online for reference. + + @param [in] pSocketProtocol Address of the socket protocol structure. + @param [in] domain Select the family of protocols for the client or server + application. + + @param [in] type Specifies how to make the network connection. The following values + are supported: + + + @param [in] protocol Specifies the lower layer protocol to use. The following + values are supported: + + + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS - Socket successfully created + @retval EFI_INVALID_PARAMETER - Invalid domain value, errno = EAFNOSUPPORT + @retval EFI_INVALID_PARAMETER - Invalid type value, errno = EINVAL + @retval EFI_INVALID_PARAMETER - Invalid protocol value, errno = EINVAL + + **/ +EFI_STATUS +EslSocket ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN int domain, + IN int type, + IN int protocol, + IN int * pErrno + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + int errno; + + DBG_ENTER ( ); + + // + // Locate the socket + // + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + + // + // Set the default domain if necessary + // + if ( AF_UNSPEC == domain ) { + domain = AF_INET; + } + + // + // Assume success + // + errno = 0; + Status = EFI_SUCCESS; + + // + // Use break instead of goto + // + for ( ; ; ) { + // + // Validate the domain value + // + if (( AF_INET != domain ) + && ( AF_LOCAL != domain )) + { + DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, + "ERROR - Invalid domain value" )); + Status = EFI_INVALID_PARAMETER; + errno = EAFNOSUPPORT; + break; + } + + // + // Set the default type if necessary + // + if ( 0 == type ) { + type = SOCK_STREAM; + } + + // + // Validate the type value + // + if (( SOCK_STREAM == type ) + || ( SOCK_SEQPACKET == type )) { + // + // Set the default protocol if necessary + // + if ( 0 == protocol ) { + protocol = IPPROTO_TCP; + } + } + else if ( SOCK_DGRAM == type ) { + // + // Set the default protocol if necessary + // + if ( 0 == protocol ) { + protocol = IPPROTO_UDP; + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, + "ERROR - Invalid type value" )); + Status = EFI_INVALID_PARAMETER; + errno = EINVAL; + break; + } + + // + // Validate the protocol value + // + if (( IPPROTO_TCP != protocol ) + && ( IPPROTO_UDP != protocol )) { + DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, + "ERROR - Invalid protocol value" )); + Status = EFI_INVALID_PARAMETER; + errno = EINVAL; + break; + } + + // + // Save the socket attributes + // + pSocket->Domain = domain; + pSocket->Type = type; + pSocket->Protocol = protocol; + + // + // Done + // + break; + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + *pErrno = errno; + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Accept a network connection. + + The SocketAccept routine waits for a network connection to the socket. + It is able to return the remote network address to the caller if + requested. + + @param [in] pSocketProtocol Address of the socket protocol structure. + + @param [in] pSockAddr Address of a buffer to receive the remote + network address. + + @param [in, out] pSockAddrLength Length in bytes of the address buffer. + On output specifies the length of the + remote network address. + + @param [out] ppSocketProtocol Address of a buffer to receive the socket protocol + instance associated with the new socket. + + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS New connection successfully created + @retval EFI_NOT_READY No connection is available + + **/ +EFI_STATUS +EslSocketAccept ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN struct sockaddr * pSockAddr, + IN OUT socklen_t * pSockAddrLength, + IN EFI_SOCKET_PROTOCOL ** ppSocketProtocol, + IN int * pErrno + ) +{ + DT_SOCKET * pNewSocket; + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Validate the socket + // + pSocket = NULL; + pNewSocket = NULL; + if ( NULL != pSocketProtocol ) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + + // + // Validate the sockaddr + // + if (( NULL != pSockAddr ) + && ( NULL == pSockAddrLength )) { + DEBUG (( DEBUG_ACCEPT, + "ERROR - pSockAddr is NULL!\r\n" )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EFAULT; + } + else { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Verify that the socket is in the listen state + // + if ( SOCKET_STATE_LISTENING != pSocket->State ) { + DEBUG (( DEBUG_ACCEPT, + "ERROR - Socket is not listening!\r\n" )); + Status = EFI_NOT_STARTED; + pSocket->errno = EOPNOTSUPP; + } + else { + // + // Determine if a socket is available + // + if ( 0 == pSocket->FifoDepth ) { + // + // No connections available + // Determine if any ports are available + // + if ( NULL == pSocket->pPortList ) { + // + // No ports available + // + Status = EFI_DEVICE_ERROR; + pSocket->errno = EINVAL; + + // + // Update the socket state + // + pSocket->State = SOCKET_STATE_NO_PORTS; + } + else { + // + // Ports are available + // No connection requests at this time + // + Status = EFI_NOT_READY; + pSocket->errno = EAGAIN; + } + } + else { + + // + // Get the remote network address + // + pNewSocket = pSocket->pFifoHead; + ASSERT ( NULL != pNewSocket ); + switch ( pSocket->Domain ) { + default: + DEBUG (( DEBUG_ACCEPT, + "ERROR - Invalid socket address family: %d\r\n", + pSocket->Domain )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_ACCEPT, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + Status = EslTcpAccept4 ( pNewSocket, + pSockAddr, + pSockAddrLength ); + break; + + /* + case SOCK_DGRAM: + Status = UdpAccept4 ( pSocket ); + break; + */ + } + break; + } + if ( !EFI_ERROR ( Status )) { + // + // Remove the new socket from the list + // + pSocket->pFifoHead = pNewSocket->pNextConnection; + if ( NULL == pSocket->pFifoHead ) { + pSocket->pFifoTail = NULL; + } + + // + // Account for this socket + // + pSocket->FifoDepth -= 1; + + // + // Update the new socket's state + // + pNewSocket->State = SOCKET_STATE_CONNECTED; + pNewSocket->bConfigured = TRUE; + DEBUG (( DEBUG_ACCEPT, + "0x%08x: Socket connected\r\n", + pNewSocket )); + } + } + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + } + + // + // Return the new socket + // + if (( NULL != ppSocketProtocol ) + && ( NULL != pNewSocket )) { + *ppSocketProtocol = &pNewSocket->SocketProtocol; + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + if ( NULL != pSocket ) { + *pErrno = pSocket->errno; + } + else + { + Status = EFI_INVALID_PARAMETER; + *pErrno = EBADF; + } + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Allocate and initialize a DT_SOCKET structure. + + The ::SocketAllocate() function allocates a DT_SOCKET structure + and installs a protocol on ChildHandle. If pChildHandle is a + pointer to NULL, then a new handle is created and returned in + pChildHandle. If pChildHandle is not a pointer to NULL, then + the protocol installs on the existing pChildHandle. + + @param [in, out] pChildHandle Pointer to the handle of the child to create. + If it is NULL, then a new handle is created. + If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI + handle. + @param [in] DebugFlags Flags for debug messages + @param [in, out] ppSocket The buffer to receive the DT_SOCKET structure address. + + @retval EFI_SUCCESS The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child + @retval other The child handle was not created + +**/ +EFI_STATUS +EFIAPI +EslSocketAllocate ( + IN OUT EFI_HANDLE * pChildHandle, + IN UINTN DebugFlags, + IN OUT DT_SOCKET ** ppSocket + ) +{ + UINTN LengthInBytes; + DT_LAYER * pLayer; + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Create a socket structure + // + LengthInBytes = sizeof ( *pSocket ); + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + LengthInBytes, + (VOID **) &pSocket + ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, + "0x%08x: Allocate pSocket, %d bytes\r\n", + pSocket, + LengthInBytes )); + + // + // Initialize the socket protocol + // + ZeroMem ( pSocket, LengthInBytes ); + + pSocket->Signature = SOCKET_SIGNATURE; + pSocket->SocketProtocol.pfnAccept = EslSocketAccept; + pSocket->SocketProtocol.pfnBind = EslSocketBind; + pSocket->SocketProtocol.pfnClosePoll = EslSocketClosePoll; + pSocket->SocketProtocol.pfnCloseStart = EslSocketCloseStart; + pSocket->SocketProtocol.pfnConnect = EslSocketConnect; + pSocket->SocketProtocol.pfnGetLocal = EslSocketGetLocalAddress; + pSocket->SocketProtocol.pfnGetPeer = EslSocketGetPeerAddress; + pSocket->SocketProtocol.pfnListen = EslSocketListen; + pSocket->SocketProtocol.pfnOptionGet = EslSocketOptionGet; + pSocket->SocketProtocol.pfnOptionSet = EslSocketOptionSet; + pSocket->SocketProtocol.pfnPoll = EslSocketPoll; + pSocket->SocketProtocol.pfnReceive = EslSocketReceive; + pSocket->SocketProtocol.pfnSend = EslSocketTransmit; + pSocket->SocketProtocol.pfnShutdown = EslSocketShutdown; + pSocket->SocketProtocol.pfnSocket = EslSocket; + + pSocket->MaxRxBuf = MAX_RX_DATA; + pSocket->MaxTxBuf = MAX_TX_DATA; + + // + // Install the socket protocol on the specified handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + pChildHandle, + &gEfiSocketProtocolGuid, + &pSocket->SocketProtocol, + NULL + ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Installed: gEfiSocketProtocolGuid on 0x%08x\r\n", + *pChildHandle )); + pSocket->SocketProtocol.SocketHandle = *pChildHandle; + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Add this socket to the list + // + pLayer = &mEslLayer; + pSocket->pNext = pLayer->pSocketList; + pLayer->pSocketList = pSocket; + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + + // + // Return the socket structure address + // + *ppSocket = pSocket; + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL | DEBUG_INIT, + "ERROR - Failed to install gEfiSocketProtocolGuid on 0x%08x, Status: %r\r\n", + *pChildHandle, + Status )); + } + + // + // Release the socket if necessary + // + if ( EFI_ERROR ( Status )) { + gBS->FreePool ( pSocket ); + DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, + "0x%08x: Free pSocket, %d bytes\r\n", + pSocket, + sizeof ( *pSocket ))); + pSocket = NULL; + } + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL | DEBUG_INIT, + "ERROR - Failed socket allocation, Status: %r\r\n", + Status )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Bind a name to a socket. + + The ::SocketBind routine connects a name to a socket on the local machine. The + POSIX + documentation for the bind routine is available online for reference. + + @param [in] pSocketProtocol Address of the socket protocol structure. + + @param [in] pSockAddr Address of a sockaddr structure that contains the + connection point on the local machine. An IPv4 address + of INADDR_ANY specifies that the connection is made to + all of the network stacks on the platform. Specifying a + specific IPv4 address restricts the connection to the + network stack supporting that address. Specifying zero + for the port causes the network layer to assign a port + number from the dynamic range. Specifying a specific + port number causes the network layer to use that port. + + @param [in] SockAddrLen Specifies the length in bytes of the sockaddr structure. + + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslSocketBind ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN const struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength, + OUT int * pErrno + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Validate the socket + // + pSocket = NULL; + if ( NULL != pSocketProtocol ) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + + // + // Validate the structure pointer + // + if ( NULL == pSockAddr ) { + DEBUG (( DEBUG_BIND, + "ERROR - pSockAddr is NULL!\r\n" )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EFAULT; + } + else{ + // + // Validate the name length + // + if (( SockAddrLength < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data ))) + || ( pSockAddr->sa_len < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data )))) { + DEBUG (( DEBUG_BIND, + "ERROR - Invalid bind name length: %d, sa_len: %d\r\n", + SockAddrLength, + pSockAddr->sa_len )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + } + else { + // + // Set the socket address length + // + if ( SockAddrLength > pSockAddr->sa_len ) { + SockAddrLength = pSockAddr->sa_len; + } + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Validate the local address + // + switch ( pSockAddr->sa_family ) { + default: + DEBUG (( DEBUG_BIND, + "ERROR - Invalid bind address family: %d\r\n", + pSockAddr->sa_family )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_BIND, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + Status = EslTcpBind4 ( pSocket, + pSockAddr, + SockAddrLength ); + break; + + case SOCK_DGRAM: + Status = EslUdpBind4 ( pSocket, + pSockAddr, + SockAddrLength ); + break; + } + break; + } + + // + // Mark this socket as bound if successful + // + if ( !EFI_ERROR ( Status )) { + pSocket->State = SOCKET_STATE_BOUND; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + if ( NULL != pSocket ) { + *pErrno = pSocket->errno; + } + else + { + Status = EFI_INVALID_PARAMETER; + *pErrno = EBADF; + } + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Determine if the socket is closed + + Reverses the operations of the ::SocketAllocate() routine. + + @param [in] pSocketProtocol Address of the socket protocol structure. + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS Socket successfully closed + @retval EFI_NOT_READY Close still in progress + @retval EFI_ALREADY Close operation already in progress + @retval Other Failed to close the socket + +**/ +EFI_STATUS +EslSocketClosePoll ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN int * pErrno + ) +{ + int errno; + DT_LAYER * pLayer; + DT_SOCKET * pNextSocket; + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume success + // + errno = 0; + Status = EFI_SUCCESS; + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Locate the socket + // + pLayer = &mEslLayer; + pNextSocket = pLayer->pSocketList; + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + while ( NULL != pNextSocket ) { + if ( pNextSocket == pSocket ) { + // + // Determine if the socket is in the closing state + // + if ( SOCKET_STATE_CLOSED == pSocket->State ) { + // + // Walk the list of ports + // + if ( NULL == pSocket->pPortList ) { + // + // All the ports are closed + // Close the WaitAccept event if necessary + // + if ( NULL != pSocket->WaitAccept ) { + Status = gBS->CloseEvent ( pSocket->WaitAccept ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_SOCKET | DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Closed WaitAccept event\r\n", + pSocket->WaitAccept )); + // + // Return the transmit status + // + Status = pSocket->TxError; + if ( EFI_ERROR ( Status )) { + pSocket->errno = EIO; + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_SOCKET | DEBUG_CLOSE | DEBUG_POOL, + "ERROR - Failed to close the WaitAccept event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + } + else { + // + // At least one port is still open + // + Status = EFI_NOT_READY; + errno = EAGAIN; + } + } + else { + // + // SocketCloseStart was not called + // + Status = EFI_NOT_STARTED; + errno = EPERM; + } + break; + } + + // + // Set the next socket + // + pNextSocket = pNextSocket->pNext; + } + + // + // Handle the error case where the socket was already closed + // + if ( NULL == pSocket ) { + // + // Socket not found + // + Status = EFI_NOT_FOUND; + errno = ENOTSOCK; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + + // + // Return the operation status + // + if ( NULL != pErrno ) { + *pErrno = errno; + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Start the close operation on the socket + + Start closing the socket by closing all of the ports. Upon + completion, the ::SocketPoll() routine finishes closing the + socket. + + @param [in] pSocketProtocol Address of the socket protocol structure. + @param [in] bCloseNow Boolean to control close behavior + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS Socket successfully closed + @retval EFI_NOT_READY Close still in progress + @retval EFI_ALREADY Close operation already in progress + @retval Other Failed to close the socket + +**/ +EFI_STATUS +EslSocketCloseStart ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN BOOLEAN bCloseNow, + IN int * pErrno + ) +{ + int errno; + DT_PORT * pNextPort; + DT_PORT * pPort; + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + errno = 0; + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Determine if the socket is already closed + // + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + if ( SOCKET_STATE_CLOSED > pSocket->State ) { + // + // Update the socket state + // + pSocket->State = SOCKET_STATE_CLOSED; + + // + // Walk the list of ports + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Start closing the ports + // + pNextPort = pPort->pLinkSocket; + Status = pPort->pfnCloseStart ( pPort, + bCloseNow, + DEBUG_CLOSE | DEBUG_LISTEN | DEBUG_CONNECTION ); + if (( EFI_SUCCESS != Status ) + && ( EFI_NOT_READY != Status )) { + errno = EIO; + break; + } + + // + // Set the next port + // + pPort = pNextPort; + } + + // + // Attempt to finish closing the socket + // + if ( NULL == pPort ) { + Status = EslSocketClosePoll ( pSocketProtocol, &errno ); + } + } + else { + Status = EFI_ALREADY_STARTED; + errno = EALREADY; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + + // + // Return the operation status + // + if ( NULL != pErrno ) { + *pErrno = errno; + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Connect to a remote system via the network. + + The ::SocketConnect routine attempts to establish a connection to a + socket on the local or remote system using the specified address. + The POSIX + connect + documentation is available online. + + There are three states associated with a connection: + + In the "Not connected" state, calls to ::connect start the connection + processing and update the state to "Connection in progress". During + the "Connection in progress" state, connect polls for connection completion + and moves the state to "Connected" after the connection is established. + Note that these states are only visible when the file descriptor is marked + with O_NONBLOCK. Also, the POLL_WRITE bit is set when the connection + completes and may be used by poll or select as an indicator to call + connect again. + + @param [in] pSocketProtocol Address of the socket protocol structure. + + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. + + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslSocketConnect ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN const struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength, + IN int * pErrno + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DEBUG (( DEBUG_CONNECT, "Entering SocketConnect\r\n" )); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Validate the socket + // + pSocket = NULL; + if ( NULL != pSocketProtocol ) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + + // + // Validate the name length + // + if (( SockAddrLength < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data ))) + || ( pSockAddr->sa_len < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data )))) { + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid bind name length: %d, sa_len: %d\r\n", + SockAddrLength, + pSockAddr->sa_len )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + } + else { + // + // Assume success + // + pSocket->errno = 0; + + // + // Set the socket address length + // + if ( SockAddrLength > pSockAddr->sa_len ) { + SockAddrLength = pSockAddr->sa_len; + } + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Validate the socket state + // + switch ( pSocket->State ) { + default: + // + // Wrong socket state + // + pSocket->errno = EIO; + Status = EFI_DEVICE_ERROR; + break; + + case SOCKET_STATE_NOT_CONFIGURED: + case SOCKET_STATE_BOUND: + // + // Validate the local address + // + switch ( pSockAddr->sa_family ) { + default: + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid bind address family: %d\r\n", + pSockAddr->sa_family )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + // + // Start the connection processing + // + Status = EslTcpConnectStart4 ( pSocket, + pSockAddr, + SockAddrLength ); + + // + // Set the next state if connecting + // + if ( EFI_NOT_READY == Status ) { + pSocket->State = SOCKET_STATE_CONNECTING; + } + break; + + case SOCK_DGRAM: + Status = EslUdpConnect4 ( pSocket, + pSockAddr, + SockAddrLength ); + break; + } + break; + } + break; + + case SOCKET_STATE_CONNECTING: + // + // Validate the local address + // + switch ( pSockAddr->sa_family ) { + default: + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid bind address family: %d\r\n", + pSockAddr->sa_family )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + // + // Determine if the connection processing is completed + // + Status = EslTcpConnectPoll4 ( pSocket ); + + // + // Set the next state if connected + // + if ( EFI_NOT_READY != Status ) { + if ( !EFI_ERROR ( Status )) { + pSocket->State = SOCKET_STATE_CONNECTED; + } + else { + pSocket->State = SOCKET_STATE_BOUND; + } + } + break; + + case SOCK_DGRAM: + // + // Already connected + // + pSocket->errno = EISCONN; + Status = EFI_ALREADY_STARTED; + break; + } + break; + } + break; + + case SOCKET_STATE_CONNECTED: + // + // Already connected + // + pSocket->errno = EISCONN; + Status = EFI_ALREADY_STARTED; + break; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + if ( NULL != pSocket ) { + *pErrno = pSocket->errno; + } + else + { + // + // Bad socket protocol + // + DEBUG (( DEBUG_ERROR | DEBUG_CONNECT, + "ERROR - pSocketProtocol invalid!\r\n" )); + Status = EFI_INVALID_PARAMETER; + *pErrno = EBADF; + } + } + + // + // Return the operation status + // + DEBUG (( DEBUG_CONNECT, "Exiting SocketConnect, Status: %r\r\n", Status )); + return Status; +} + + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If pChildHandle is a pointer to NULL, then a new handle is created and returned in pChildHandle. + If pChildHandle is not a pointer to NULL, then the protocol installs on the existing pChildHandle. + + @param [in] pThis Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param [in] pChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCESS The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child + @retval other The child handle was not created + +**/ +EFI_STATUS +EFIAPI +EslSocketCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL * pThis, + IN OUT EFI_HANDLE * pChildHandle + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Create a socket structure + // + Status = EslSocketAllocate ( pChildHandle, + DEBUG_SOCKET, + &pSocket ); + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param [in] pThis Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param [in] ChildHandle Handle of the child to destroy + + @retval EFI_SUCCESS The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +EslSocketDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL * pThis, + IN EFI_HANDLE ChildHandle + ) +{ + DT_LAYER * pLayer; + DT_SOCKET * pSocket; + DT_SOCKET * pSocketPrevious; + EFI_SOCKET_PROTOCOL * pSocketProtocol; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Locate the socket control structure + // + pLayer = &mEslLayer; + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiSocketProtocolGuid, + (VOID **)&pSocketProtocol, + pLayer->ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if ( !EFI_ERROR ( Status )) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Walk the socket list + // + pSocketPrevious = pLayer->pSocketList; + if ( NULL != pSocketPrevious ) { + if ( pSocket == pSocketPrevious ) { + // + // Remove the socket from the head of the list + // + pLayer->pSocketList = pSocket->pNext; + } + else { + // + // Find the socket in the middle of the list + // + while (( NULL != pSocketPrevious ) + && ( pSocket != pSocketPrevious->pNext )) { + // + // Set the next socket + // + pSocketPrevious = pSocketPrevious->pNext; + } + if ( NULL != pSocketPrevious ) { + // + // Remove the socket from the middle of the list + // + pSocketPrevious = pSocket->pNext; + } + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_POOL, + "ERROR - Socket list is empty!\r\n" )); + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + + // + // Determine if the socket was found + // + if ( NULL != pSocketPrevious ) { + pSocket->pNext = NULL; + + // + // Remove the socket protocol + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiSocketProtocolGuid, + &pSocket->SocketProtocol, + NULL ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INFO, + "Removed: gEfiSocketProtocolGuid from 0x%08x\r\n", + ChildHandle )); + + // + // Free the socket structure + // + Status = gBS->FreePool ( pSocket ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL, + "0x%08x: Free pSocket, %d bytes\r\n", + pSocket, + sizeof ( *pSocket ))); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_POOL, + "ERROR - Failed to free pSocket 0x%08x, Status: %r\r\n", + pSocket, + Status )); + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INFO, + "ERROR - Failed to remove gEfiSocketProtocolGuid from 0x%08x, Status: %r\r\n", + ChildHandle, + Status )); + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_INFO, + "ERROR - The socket was not in the socket list!\r\n" )); + Status = EFI_NOT_FOUND; + } + } + else { + DEBUG (( DEBUG_ERROR, + "ERROR - Failed to open socket protocol on 0x%08x, Status; %r\r\n", + ChildHandle, + Status )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Get the local address. + + @param [in] pSocketProtocol Address of the socket protocol structure. + + @param [out] pAddress Network address to receive the local system address + + @param [in,out] pAddressLength Length of the local network address structure + + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS - Local address successfully returned + + **/ +EFI_STATUS +EslSocketGetLocalAddress ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength, + IN int * pErrno + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Validate the socket + // + pSocket = NULL; + if ( NULL != pSocketProtocol ) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + + // + // Verify the address buffer and length address + // + if (( NULL != pAddress ) && ( NULL != pAddressLength )) { + // + // Verify the socket state + // + if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Validate the local address + // + switch ( pSocket->Domain ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket address family: %d\r\n", + pSocket->Domain )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + // + // Get the local address + // + Status = EslTcpGetLocalAddress4 ( pSocket, + pAddress, + pAddressLength ); + break; + + case SOCK_DGRAM: + // + // Get the local address + // + Status = EslUdpGetLocalAddress4 ( pSocket, + pAddress, + pAddressLength ); + break; + } + break; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; + } + } + else { + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + if ( NULL != pSocket ) { + *pErrno = pSocket->errno; + } + else + { + Status = EFI_INVALID_PARAMETER; + *pErrno = EBADF; + } + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Get the peer address. + + @param [in] pSocketProtocol Address of the socket protocol structure. + + @param [out] pAddress Network address to receive the remote system address + + @param [in,out] pAddressLength Length of the remote network address structure + + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS - Remote address successfully returned + + **/ +EFI_STATUS +EslSocketGetPeerAddress ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength, + IN int * pErrno + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Validate the socket + // + pSocket = NULL; + if ( NULL != pSocketProtocol ) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + + // + // Verify the address buffer and length address + // + if (( NULL != pAddress ) && ( NULL != pAddressLength )) { + // + // Verify the socket state + // + if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Validate the local address + // + switch ( pSocket->Domain ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket address family: %d\r\n", + pSocket->Domain )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + // + // Verify the port state + // + Status = EslTcpGetRemoteAddress4 ( pSocket, + pAddress, + pAddressLength ); + break; + + case SOCK_DGRAM: + // + // Verify the port state + // + Status = EslUdpGetRemoteAddress4 ( pSocket, + pAddress, + pAddressLength ); + break; + } + break; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; + } + } + else { + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + if ( NULL != pSocket ) { + *pErrno = pSocket->errno; + } + else + { + Status = EFI_INVALID_PARAMETER; + *pErrno = EBADF; + } + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Establish the known port to listen for network connections. + + The ::SocketListen routine places the port into a state that enables connection + attempts. Connections are placed into FIFO order in a queue to be serviced + by the application. The application calls the ::SocketAccept routine to remove + the next connection from the queue and get the associated socket. The + POSIX + documentation for the listen routine is available online for reference. + + @param [in] pSocketProtocol Address of the socket protocol structure. + + @param [in] Backlog Backlog specifies the maximum FIFO depth for + the connections waiting for the application + to call accept. Connection attempts received + while the queue is full are refused. + + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS - Socket successfully created + @retval Other - Failed to enable the socket for listen + +**/ +EFI_STATUS +EslSocketListen ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN INT32 Backlog, + OUT int * pErrno + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_STATUS TempStatus; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Validate the socket + // + pSocket = NULL; + if ( NULL != pSocketProtocol ) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + + // + // Assume success + // + pSocket->Status = EFI_SUCCESS; + pSocket->errno = 0; + + // + // Verify that the bind operation was successful + // + if ( SOCKET_STATE_BOUND == pSocket->State ) { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Create the event for SocketAccept completion + // + Status = gBS->CreateEvent ( 0, + TplPrevious, + NULL, + NULL, + &pSocket->WaitAccept ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL, + "0x%08x: Created WaitAccept event\r\n", + pSocket->WaitAccept )); + // + // Set the maximum FIFO depth + // + if ( 0 >= Backlog ) { + Backlog = MAX_PENDING_CONNECTIONS; + } + else { + if ( SOMAXCONN < Backlog ) { + Backlog = SOMAXCONN; + } + else { + pSocket->MaxFifoDepth = Backlog; + } + } + + // + // Validate the local address + // + switch ( pSocket->Domain ) { + default: + DEBUG (( DEBUG_BIND, + "ERROR - Invalid socket address family: %d\r\n", + pSocket->Domain )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_BIND, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + Status = EslTcpListen4 ( pSocket ); + break; + +/* + case SOCK_DGRAM: + Status = UdpListen4 ( pSocket ); + break; +*/ + } + break; + } + + // + // Place the socket in the listen state if successful + // + if ( !EFI_ERROR ( Status )) { + pSocket->State = SOCKET_STATE_LISTENING; + } + else { + // + // Not waiting for SocketAccept to complete + // + TempStatus = gBS->CloseEvent ( pSocket->WaitAccept ); + if ( !EFI_ERROR ( TempStatus )) { + DEBUG (( DEBUG_POOL, + "0x%08x: Closed WaitAccept event\r\n", + pSocket->WaitAccept )); + pSocket->WaitAccept = NULL; + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_POOL, + "ERROR - Failed to close WaitAccept event, Status: %r\r\n", + TempStatus )); + ASSERT ( EFI_SUCCESS == TempStatus ); + } + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_LISTEN, + "ERROR - Failed to create the WaitAccept event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_LISTEN, + "ERROR - Bind operation must be performed first!\r\n" )); + pSocket->errno = EDESTADDRREQ; + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + if ( NULL != pSocket ) { + *pErrno = pSocket->errno; + } + else + { + Status = EFI_INVALID_PARAMETER; + *pErrno = EBADF; + } + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Get the socket options + + Retrieve the socket options one at a time by name. The + POSIX + documentation is available online. + + @param [in] pSocketProtocol Address of the socket protocol structure. + @param [in] level Option protocol level + @param [in] OptionName Name of the option + @param [out] pOptionValue Buffer to receive the option value + @param [in,out] pOptionLength Length of the buffer in bytes, + upon return length of the option value in bytes + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS - Socket data successfully received + + **/ +EFI_STATUS +EslSocketOptionGet ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN int level, + IN int OptionName, + OUT void * __restrict pOptionValue, + IN OUT socklen_t * __restrict pOptionLength, + IN int * pErrno + ) +{ + int errno; + socklen_t LengthInBytes; + socklen_t MaxBytes; + UINT8 * pOptionData; + DT_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume failure + // + errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + + // + // Validate the socket + // + pSocket = NULL; + if (( NULL != pSocketProtocol ) + && ( NULL != pOptionValue ) + && ( NULL != pOptionLength )) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + LengthInBytes = 0; + MaxBytes = *pOptionLength; + pOptionData = NULL; + switch ( level ) { + default: + // + // Protocol level not supported + // + errno = ENOTSUP; + Status = EFI_UNSUPPORTED; + break; + + case SOL_SOCKET: + switch ( OptionName ) { + default: + // + // Option not supported + // + errno = ENOTSUP; + Status = EFI_UNSUPPORTED; + break; + + case SO_RCVTIMEO: + // + // Return the receive timeout + // + pOptionData = (UINT8 *)&pSocket->RxTimeout; + LengthInBytes = sizeof ( pSocket->RxTimeout ); + break; + + case SO_RCVBUF: + // + // Return the maximum transmit buffer size + // + pOptionData = (UINT8 *)&pSocket->MaxRxBuf; + LengthInBytes = sizeof ( pSocket->MaxRxBuf ); + break; + + case SO_SNDBUF: + // + // Return the maximum transmit buffer size + // + pOptionData = (UINT8 *)&pSocket->MaxTxBuf; + LengthInBytes = sizeof ( pSocket->MaxTxBuf ); + break; + + case SO_TYPE: + // + // Return the socket type + // + pOptionData = (UINT8 *)&pSocket->Type; + LengthInBytes = sizeof ( pSocket->Type ); + break; + } + break; + } + + // + // Return the option length + // + *pOptionLength = LengthInBytes; + + // + // Return the option value + // + if ( NULL != pOptionData ) { + // + // Silently truncate the value length + // + if ( LengthInBytes > MaxBytes ) { + LengthInBytes = MaxBytes; + } + CopyMem ( pOptionValue, pOptionData, LengthInBytes ); + errno = 0; + Status = EFI_SUCCESS; + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + *pErrno = errno; + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Set the socket options + + Adjust the socket options one at a time by name. The + POSIX + documentation is available online. + + @param [in] pSocketProtocol Address of the socket protocol structure. + @param [in] level Option protocol level + @param [in] OptionName Name of the option + @param [in] pOptionValue Buffer containing the option value + @param [in] OptionLength Length of the buffer in bytes + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS - Socket data successfully received + + **/ +EFI_STATUS +EslSocketOptionSet ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN int level, + IN int OptionName, + IN CONST void * pOptionValue, + IN socklen_t OptionLength, + IN int * pErrno + ) +{ + int errno; + socklen_t LengthInBytes; + UINT8 * pOptionData; + DT_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume failure + // + errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + + // + // Validate the socket + // + pSocket = NULL; + if (( NULL != pSocketProtocol ) + && ( NULL != pOptionValue )) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + LengthInBytes = 0; + pOptionData = NULL; + switch ( level ) { + default: + // + // Protocol level not supported + // + errno = ENOTSUP; + Status = EFI_UNSUPPORTED; + break; + + case SOL_SOCKET: + switch ( OptionName ) { + default: + // + // Option not supported + // + errno = ENOTSUP; + Status = EFI_UNSUPPORTED; + break; + + case SO_RCVTIMEO: + // + // Return the receive timeout + // + pOptionData = (UINT8 *)&pSocket->RxTimeout; + LengthInBytes = sizeof ( pSocket->RxTimeout ); + break; + + case SO_RCVBUF: + // + // Return the maximum transmit buffer size + // + pOptionData = (UINT8 *)&pSocket->MaxRxBuf; + LengthInBytes = sizeof ( pSocket->MaxRxBuf ); + break; + + case SO_SNDBUF: + // + // Send buffer size + // + // + // Return the maximum transmit buffer size + // + pOptionData = (UINT8 *)&pSocket->MaxTxBuf; + LengthInBytes = sizeof ( pSocket->MaxTxBuf ); + break; + } + break; + } + + // + // Validate the option length + // + if ( LengthInBytes <= OptionLength ) { + // + // Set the option value + // + if ( NULL != pOptionData ) { + CopyMem ( pOptionData, pOptionValue, LengthInBytes ); + errno = 0; + Status = EFI_SUCCESS; + } + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + *pErrno = errno; + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Allocate a packet for a receive or transmit operation + + @param [in] ppPacket Address to receive the DT_PACKET structure + @param [in] LengthInBytes Length of the packet structure + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - The packet was allocated successfully + + **/ +EFI_STATUS +EslSocketPacketAllocate ( + IN DT_PACKET ** ppPacket, + IN size_t LengthInBytes, + IN UINTN DebugFlags + ) +{ + DT_PACKET * pPacket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Allocate a packet structure + // + LengthInBytes += sizeof ( *pPacket ) + - sizeof ( pPacket->Op ); + Status = gBS->AllocatePool ( EfiRuntimeServicesData, + LengthInBytes, + (VOID **)&pPacket ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, + "0x%08x: Allocate pPacket, %d bytes\r\n", + pPacket, + LengthInBytes )); + pPacket->PacketSize = LengthInBytes; + } + else { + DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INFO, + "ERROR - Packet allocation failed for %d bytes, Status: %r\r\n", + LengthInBytes, + Status )); + pPacket = NULL; + } + + // + // Return the packet + // + *ppPacket = pPacket; + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Free a packet used for receive or transmit operation + + @param [in] pPacket Address of the DT_PACKET structure + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - The packet was allocated successfully + + **/ +EFI_STATUS +EslSocketPacketFree ( + IN DT_PACKET * pPacket, + IN UINTN DebugFlags + ) +{ + UINTN LengthInBytes; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Allocate a packet structure + // + LengthInBytes = pPacket->PacketSize; + Status = gBS->FreePool ( pPacket ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Free pPacket, %d bytes\r\n", + pPacket, + LengthInBytes )); + } + else { + DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INFO, + "ERROR - Failed to free packet 0x%08x, Status: %r\r\n", + pPacket, + Status )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Poll a socket for pending activity. + + The SocketPoll routine checks a socket for pending activity associated + with the event mask. Activity is returned in the detected event buffer. + + @param [in] pSocketProtocol Address of the socket protocol structure. + + @param [in] Events Events of interest for this socket + + @param [in] pEvents Address to receive the detected events + + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS - Socket successfully polled + @retval EFI_INVALID_PARAMETER - When pEvents is NULL + + **/ +EFI_STATUS +EslSocketPoll ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN short Events, + IN short * pEvents, + IN int * pErrno + ) +{ + short DetectedEvents; + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + short ValidEvents; + + DEBUG (( DEBUG_POLL, "Entering SocketPoll\r\n" )); + + // + // Assume success + // + Status = EFI_SUCCESS; + DetectedEvents = 0; + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + pSocket->errno = 0; + + // + // Verify the socket state + // + if ( !pSocket->bConfigured ) { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Validate the local address + // + switch ( pSocket->Domain ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket address family: %d\r\n", + pSocket->Domain )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + // + // Verify the port state + // + Status = EslTcpSocketIsConfigured4 ( pSocket ); + break; + + case SOCK_DGRAM: + // + // Verify the port state + // + Status = EslUdpSocketIsConfigured4 ( pSocket ); + break; + } + break; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + if ( !EFI_ERROR ( Status )) { + // + // Check for invalid events + // + ValidEvents = POLLIN + | POLLPRI + | POLLOUT | POLLWRNORM + | POLLERR + | POLLHUP + | POLLNVAL + | POLLRDNORM + | POLLRDBAND + | POLLWRBAND ; + if ( 0 != ( Events & ( ~ValidEvents ))) { + DetectedEvents |= POLLNVAL; + DEBUG (( DEBUG_INFO | DEBUG_POLL, + "ERROR - Invalid event mask, Valid Events: 0x%04x, Invalid Events: 0x%04x\r\n", + Events & ValidEvents, + Events & ( ~ValidEvents ))); + } + else { + // + // Check for pending connections + // + if ( 0 != pSocket->FifoDepth ) { + // + // A connection is waiting for an accept call + // See posix connect documentation at + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.htm + // + DetectedEvents |= POLLIN | POLLRDNORM; + } + if ( pSocket->bConnected ) { + // + // A connection is present + // See posix connect documentation at + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.htm + // + DetectedEvents |= POLLOUT | POLLWRNORM; + } + + // + // The following bits are set based upon the POSIX poll documentation at + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html + // + + // + // Check for urgent receive data + // + if ( 0 < pSocket->RxOobBytes ) { + DetectedEvents |= POLLRDBAND | POLLPRI | POLLIN; + } + + // + // Check for normal receive data + // + if (( 0 < pSocket->RxBytes ) + || ( EFI_SUCCESS != pSocket->RxError )) { + DetectedEvents |= POLLRDNORM | POLLIN; + } + + // + // Handle the receive errors + // + if (( EFI_SUCCESS != pSocket->RxError ) + && ( 0 == ( DetectedEvents & POLLIN ))) { + DetectedEvents |= POLLERR | POLLIN | POLLRDNORM | POLLRDBAND; + } + + // + // Check for urgent transmit data buffer space + // + if (( MAX_TX_DATA > pSocket->TxOobBytes ) + || ( EFI_SUCCESS != pSocket->TxError )) { + DetectedEvents |= POLLWRBAND; + } + + // + // Check for normal transmit data buffer space + // + if (( MAX_TX_DATA > pSocket->TxBytes ) + || ( EFI_SUCCESS != pSocket->TxError )) { + DetectedEvents |= POLLWRNORM; + } + + // + // Handle the transmit error + // + if ( EFI_ERROR ( pSocket->TxError )) { + DetectedEvents |= POLLERR; + } + } + } + + // + // Return the detected events + // + *pEvents = DetectedEvents & ( Events + | POLLERR + | POLLHUP + | POLLNVAL ); + + // + // Return the operation status + // + DEBUG (( DEBUG_POLL, "Exiting SocketPoll, Status: %r\r\n", Status )); + return Status; +} + + +/** + Receive data from a network connection. + + + @param [in] pSocketProtocol Address of the socket protocol structure. + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [out] pAddress Network address to receive the remote system address + + @param [in,out] pAddressLength Length of the remote network address structure + + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS - Socket data successfully received + + **/ +EFI_STATUS +EslSocketReceive ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN INT32 Flags, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength, + IN int * pErrno + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Validate the socket + // + pSocket = NULL; + if ( NULL != pSocketProtocol ) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + + // + // Verify the socket state + // + if ( !pSocket->bConfigured ) { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Validate the local address + // + switch ( pSocket->Domain ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket address family: %d\r\n", + pSocket->Domain )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + // + // Verify the port state + // + Status = EslTcpSocketIsConfigured4 ( pSocket ); + break; + + case SOCK_DGRAM: + // + // Verify the port state + // + Status = EslUdpSocketIsConfigured4 ( pSocket ); + break; + } + break; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + + // + // Set errno if a failure occurs + // + if ( EFI_ERROR ( Status )) { + pSocket->errno = EADDRNOTAVAIL; + } + } + if ( !EFI_ERROR ( Status )) { + // + // Validate the buffer length + // + if (( NULL == pDataLength ) + && ( 0 > pDataLength ) + && ( NULL == pBuffer )) { + if ( NULL == pDataLength ) { + DEBUG (( DEBUG_RX, + "ERROR - pDataLength is NULL!\r\n" )); + } + else if ( NULL == pBuffer ) { + DEBUG (( DEBUG_RX, + "ERROR - pBuffer is NULL!\r\n" )); + } + else { + DEBUG (( DEBUG_RX, + "ERROR - Data length < 0!\r\n" )); + } + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EFAULT; + } + else{ + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Validate the local address + // + switch ( pSocket->Domain ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket address family: %d\r\n", + pSocket->Domain )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + Status = EslTcpReceive4 ( pSocket, + Flags, + BufferLength, + pBuffer, + pDataLength, + pAddress, + pAddressLength ); + break; + + case SOCK_DGRAM: + Status = EslUdpReceive4 ( pSocket, + Flags, + BufferLength, + pBuffer, + pDataLength, + pAddress, + pAddressLength); + break; + } + break; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + if ( NULL != pSocket ) { + *pErrno = pSocket->errno; + } + else + { + Status = EFI_INVALID_PARAMETER; + *pErrno = EBADF; + } + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Shutdown the socket receive and transmit operations + + The SocketShutdown routine stops the socket receive and transmit + operations. + + @param [in] pSocketProtocol Address of the socket protocol structure. + + @param [in] How Which operations to stop + + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS - Socket operations successfully shutdown + + **/ +EFI_STATUS +EslSocketShutdown ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN int How, + IN int * pErrno + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Validate the socket + // + pSocket = NULL; + if ( NULL != pSocketProtocol ) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + + // + // Verify that the socket is connected + // + if ( pSocket->bConnected ) { + // + // Validate the How value + // + if (( SHUT_RD <= How ) && ( SHUT_RDWR >= How )) { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Disable the receiver if requested + // + if (( SHUT_RD == How ) || ( SHUT_RDWR == How )) { + pSocket->bRxDisable = TRUE; + } + + // + // Disable the transmitter if requested + // + if (( SHUT_WR == How ) || ( SHUT_RDWR == How )) { + pSocket->bTxDisable = TRUE; + } + + // + // Validate the local address + // + switch ( pSocket->Domain ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket address family: %d\r\n", + pSocket->Domain )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + // + // Cancel the pending receive operation + // + Status = EslTcpRxCancel4 ( pSocket ); + break; + + case SOCK_DGRAM: + // + // Cancel the pending receive operation + // + Status = EslUdpRxCancel4 ( pSocket ); + break; + } + break; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + // + // The socket is not connected + // + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; + } + } + else { + // + // Invalid How value + // + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + if ( NULL != pSocket ) { + *pErrno = pSocket->errno; + } + else + { + Status = EFI_INVALID_PARAMETER; + *pErrno = EBADF; + } + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Send data using a network connection. + + The SocketTransmit routine queues the data for transmission to the + remote network connection. + + @param [in] pSocketProtocol Address of the socket protocol structure. + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer containing the data to send + + @param [in] pDataLength Address to receive the number of data bytes sent + + @param [in] pAddress Network address of the remote system address + + @param [in] AddressLength Length of the remote network address structure + + @param [out] pErrno Address to receive the errno value upon completion. + + @retval EFI_SUCCESS - Socket data successfully queued for transmit + + **/ +EFI_STATUS +EslSocketTransmit ( + IN EFI_SOCKET_PROTOCOL * pSocketProtocol, + IN int Flags, + IN size_t BufferLength, + IN CONST UINT8 * pBuffer, + OUT size_t * pDataLength, + IN const struct sockaddr * pAddress, + IN socklen_t AddressLength, + IN int * pErrno + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Validate the socket + // + pSocket = NULL; + if ( NULL != pSocketProtocol ) { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + + // + // Verify the socket state + // + if ( !pSocket->bConfigured ) { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Validate the local address + // + switch ( pSocket->Domain ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket address family: %d\r\n", + pSocket->Domain )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + // + // Verify the port state + // + Status = EslTcpSocketIsConfigured4 ( pSocket ); + break; + + case SOCK_DGRAM: + // + // Verify the port state + // + Status = EslUdpSocketIsConfigured4 ( pSocket ); + break; + } + break; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + + // + // Set errno if a failure occurs + // + if ( EFI_ERROR ( Status )) { + pSocket->errno = EADDRNOTAVAIL; + } + } + if ( !EFI_ERROR ( Status )) { + // + // Verify that transmit is still allowed + // + if ( !pSocket->bTxDisable ) { + // + // Validate the buffer length + // + if (( NULL == pDataLength ) + && ( 0 > pDataLength ) + && ( NULL == pBuffer )) { + if ( NULL == pDataLength ) { + DEBUG (( DEBUG_RX, + "ERROR - pDataLength is NULL!\r\n" )); + } + else if ( NULL == pBuffer ) { + DEBUG (( DEBUG_RX, + "ERROR - pBuffer is NULL!\r\n" )); + } + else { + DEBUG (( DEBUG_RX, + "ERROR - Data length < 0!\r\n" )); + } + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EFAULT; + } + else { + // + // Validate the remote network address + // + if (( NULL != pAddress ) + && ( AddressLength < pAddress->sa_len )) { + DEBUG (( DEBUG_TX, + "ERROR - Invalid sin_len field in address\r\n" )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EFAULT; + } + else { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Validate the local address + // + switch ( pSocket->Domain ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket address family: %d\r\n", + pSocket->Domain )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case AF_INET: + // + // Determine the connection point within the network stack + // + switch ( pSocket->Type ) { + default: + DEBUG (( DEBUG_RX, + "ERROR - Invalid socket type: %d\r\n", + pSocket->Type)); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EADDRNOTAVAIL; + break; + + case SOCK_STREAM: + case SOCK_SEQPACKET: + Status = EslTcpTxBuffer4 ( pSocket, + Flags, + BufferLength, + pBuffer, + pDataLength ); + break; + + case SOCK_DGRAM: + Status = EslUdpTxBuffer4 ( pSocket, + Flags, + BufferLength, + pBuffer, + pDataLength, + pAddress, + AddressLength ); + break; + } + break; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + } + } + else { + // + // The transmitter was shutdown + // + pSocket->errno = EPIPE; + Status = EFI_NOT_STARTED; + } + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + if ( NULL != pSocket ) { + *pErrno = pSocket->errno; + } + else + { + Status = EFI_INVALID_PARAMETER; + *pErrno = EBADF; + } + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Socket layer's service binding protocol delcaration. +**/ +EFI_SERVICE_BINDING_PROTOCOL mEfiServiceBinding = { + EslSocketCreateChild, + EslSocketDestroyChild +}; diff --git a/StdLib/EfiSocketLib/Socket.h b/StdLib/EfiSocketLib/Socket.h new file mode 100644 index 0000000000..42377eb29c --- /dev/null +++ b/StdLib/EfiSocketLib/Socket.h @@ -0,0 +1,1337 @@ +/** @file + Definitions for the Socket layer driver. + + Copyright (c) 2011, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#ifndef _SOCKET_H_ +#define _SOCKET_H_ + +#include + +//------------------------------------------------------------------------------ +// Constants +//------------------------------------------------------------------------------ + +#define DEBUG_SOCKET 0x20000000 ///< Display Socket related messages +#define DEBUG_BIND 0x10000000 ///< Display bind related messages +#define DEBUG_LISTEN 0x08000000 ///< Display listen related messages +#define DEBUG_CONNECTION 0x04000000 ///< Display connection list related messages +#define DEBUG_POLL 0x02000000 ///< Display poll messages +#define DEBUG_ACCEPT 0x01000000 ///< Display accept related messages +#define DEBUG_RX 0x00800000 ///< Display receive messages +#define DEBUG_TX 0x00400000 ///< Display transmit messages +#define DEBUG_CLOSE 0x00200000 ///< Display close messages +#define DEBUG_CONNECT 0x00100000 ///< Display connect messages + +#define MAX_PENDING_CONNECTIONS 1 ///< Maximum connection FIFO depth +#define MAX_RX_DATA 65536 ///< Maximum receive data size +#define MAX_TX_DATA ( MAX_RX_DATA * 2 ) +#define RX_PACKET_DATA 16384 ///< Maximum number of bytes in a RX packet + +#define LAYER_SIGNATURE SIGNATURE_32('S','k','t','L') ///< DT_LAYER memory signature +#define SERVICE_SIGNATURE SIGNATURE_32('S','k','t','S') ///< DT_SERVICE memory signature +#define SOCKET_SIGNATURE SIGNATURE_32('S','c','k','t') ///< DT_SOCKET memory signature +#define PORT_SIGNATURE SIGNATURE_32('P','o','r','t') ///< DT_PORT memory signature + +typedef enum +{ + NETWORK_TYPE_UNKNOWN = 0, + NETWORK_TYPE_RAW, + NETWORK_TYPE_TCP4, + NETWORK_TYPE_TCP6, + NETWORK_TYPE_UDP4, + NETWORK_TYPE_UDP6 +} NETWORK_TYPE; + +typedef enum +{ + SOCKET_STATE_NOT_CONFIGURED = 0, ///< socket call was successful + SOCKET_STATE_BOUND, ///< bind call was successful + SOCKET_STATE_LISTENING, ///< listen call was successful + SOCKET_STATE_NO_PORTS, ///< No ports available + SOCKET_STATE_IN_FIFO, ///< Socket on FIFO + SOCKET_STATE_CONNECTING, ///< Connecting to a remote system + SOCKET_STATE_CONNECTED, ///< Accept or connect call was successful + + // + // Close state must be the last in the list + // + SOCKET_STATE_CLOSED ///< Close call was successful +} SOCKET_STATE; + +typedef enum +{ + PORT_STATE_ALLOCATED = 0, ///< Port allocated + PORT_STATE_OPEN, ///< Port opened + PORT_STATE_RX_ERROR, ///< Receive error detected + + // + // Close state must be last in the list + // + PORT_STATE_CLOSE_STARTED, ///< Close started on port + PORT_STATE_CLOSE_TX_DONE, ///< Transmits shutdown + PORT_STATE_CLOSE_RX_DONE, ///< Receives shutdown + PORT_STATE_CLOSE_DONE ///< Port close operation complete +} PORT_STATE; + +//------------------------------------------------------------------------------ +// Data Types +//------------------------------------------------------------------------------ + +typedef struct _DT_PACKET DT_PACKET; ///< Forward declaration +typedef struct _DT_PORT DT_PORT; ///< Forward declaration +typedef struct _DT_SOCKET DT_SOCKET; ///< Forward declaration + +typedef struct +{ + EFI_TCP4_RECEIVE_DATA RxData; ///< Receive operation description + size_t ValidBytes; ///< Length of valid data in bytes + UINT8 * pBuffer; ///< Current data pointer + UINT8 Buffer [ RX_PACKET_DATA ]; ///< Data buffer +} DT_TCP4_RX_DATA; + +typedef struct +{ + EFI_TCP4_TRANSMIT_DATA TxData; ///< Transmit operation description + UINT8 Buffer [ 1 ]; ///< Data buffer +} DT_TCP4_TX_DATA; + +typedef struct +{ + EFI_UDP4_SESSION_DATA Session; ///< * Remote network address + EFI_UDP4_RECEIVE_DATA * pRxData; ///< * Receive operation description +} DT_UDP4_RX_DATA; + +typedef struct +{ + EFI_UDP4_SESSION_DATA Session; ///< Remote network address + EFI_UDP4_TRANSMIT_DATA TxData; ///< Transmit operation description + UINT8 Buffer [ 1 ]; ///< Data buffer +} DT_UDP4_TX_DATA; + +typedef struct _DT_PACKET { + DT_PACKET * pNext; ///< Next packet in the receive list + size_t PacketSize; ///< Size of this data structure + union { + DT_TCP4_RX_DATA Tcp4Rx; ///< Receive operation description + DT_TCP4_TX_DATA Tcp4Tx; ///< Transmit operation description + DT_UDP4_RX_DATA Udp4Rx; ///< Receive operation description + DT_UDP4_TX_DATA Udp4Tx; ///< Transmit operation description + } Op; +} GCC_DT_PACKET; + +/** + Service control structure + + The driver uses this structure to manage the network devices. +**/ +typedef struct _DT_SERVICE { + UINTN Signature; ///< Structure identification + + // + // Links + // + DT_SERVICE * pNext; ///< Next service in the service list + + // + // Service data + // + CONST DT_SOCKET_BINDING * pSocketBinding; ///< Name and shutdown routine + EFI_HANDLE Controller; ///< Controller for the service + VOID * pInterface; ///< Network layer service binding interface + + // + // Network data + // + NETWORK_TYPE NetworkType; ///< Type of network service + DT_PORT * pPortList; ///< List of ports using this service +}GCC_DT_SERVICE; + +/** + Start the close operation on a TCP4 port. + + @param [in] pPort Address of the port structure. + @param [in] bAbort Set TRUE to abort active transfers + @param [in] DebugFlags Flags for debug messages + +**/ +typedef +EFI_STATUS +PFN_PORT_CLOSE_START ( + IN DT_PORT * pPort, + IN BOOLEAN bAbort, + IN UINTN DebugFlags + ); + +/** + TCP4 context structure + + The driver uses this structure to manage the TCP4 connections. +**/ +typedef struct { + // + // TCP4 context + // + EFI_HANDLE Handle; ///< TCP4 port handle + EFI_TCP4_PROTOCOL * pProtocol; ///< TCP4 protocol pointer + EFI_TCP4_CONFIG_DATA ConfigData; ///< TCP4 configuration data + EFI_TCP4_OPTION Option; ///< TCP4 port options + BOOLEAN bConfigured; ///< TRUE if configuration was successful + + // + // Tokens + // + EFI_TCP4_LISTEN_TOKEN ListenToken; ///< Listen control + EFI_TCP4_CONNECTION_TOKEN ConnectToken; ///< Connection control + EFI_TCP4_CLOSE_TOKEN CloseToken; ///< Close control + + // + // Receive data management + // + EFI_TCP4_IO_TOKEN RxToken; ///< Receive token + DT_PACKET * pReceivePending; ///< Receive operation in progress + + // + // Transmit data management + // + EFI_TCP4_IO_TOKEN TxOobToken; ///< Urgent data token + DT_PACKET * pTxOobPacket; ///< Urgent data in progress + + EFI_TCP4_IO_TOKEN TxToken; ///< Normal data token + DT_PACKET * pTxPacket; ///< Normal transmit in progress +} DT_TCP4_CONTEXT; + +/** + UDP4 context structure + + The driver uses this structure to manage the UDP4 connections. +**/ +typedef struct { + // + // UDP4 context + // + EFI_HANDLE Handle; ///< UDP4 port handle + EFI_UDP4_PROTOCOL * pProtocol; ///< UDP4 protocol pointer + EFI_UDP4_CONFIG_DATA ConfigData; ///< UDP4 configuration data + BOOLEAN bConfigured; ///< TRUE if configuration was successful + + // + // Receive data management + // + EFI_UDP4_COMPLETION_TOKEN RxToken;///< Receive token + DT_PACKET * pReceivePending; ///< Receive operation in progress + + // + // Transmit data management + // + EFI_UDP4_COMPLETION_TOKEN TxToken;///< Transmit token + DT_PACKET * pTxPacket; ///< Transmit in progress +} DT_UDP4_CONTEXT; + + +/** + Port control structure + + The driver uses this structure to manager the socket's connection + with the network driver. +**/ +typedef struct _DT_PORT { + UINTN Signature; ///< Structure identification + + // + // List links + // + DT_PORT * pLinkService; ///< Link in service port list + DT_PORT * pLinkSocket; ///< Link in socket port list + + // + // Structures + // + DT_SERVICE * pService; ///< Service for this port + DT_SOCKET * pSocket; ///< Socket for this port +// PFN_CLOSE_PORT pfnClosePort; ///< Routine to immediately close the port + PFN_PORT_CLOSE_START * pfnCloseStart; ///< Routine to start closing the port + + // + // Protocol specific management data + // + PORT_STATE State; ///< State of the port + UINTN DebugFlags; ///< Debug flags used to close the port + BOOLEAN bCloseNow; ///< TRUE = Close the port immediately + + union { + DT_TCP4_CONTEXT Tcp4; ///< TCPv4 management data + DT_UDP4_CONTEXT Udp4; ///< UDPv4 management data + } Context; +}GCC_DT_PORT; + +/** + Socket control structure + + The driver uses this structure to manage the socket. +**/ +typedef struct _DT_SOCKET { + UINTN Signature; ///< Structure identification + + // + // Protocol binding + // + EFI_SOCKET_PROTOCOL SocketProtocol; ///< Socket protocol declaration + + // + // Socket management + // + DT_SOCKET * pNext; ///< Next socket in the list of sockets + int errno; ///< Error information for this socket + EFI_STATUS Status; ///< Asyncronous error information for this socket + SOCKET_STATE State; ///< Socket state + + // + // Socket data + // + int Domain; ///< Specifies family of protocols + int Type; ///< Specifies how to make network connection + int Protocol; ///< Specifies lower layer protocol to use + BOOLEAN bConfigured; ///< Set after the socket is configured + + BOOLEAN bRxDisable; ///< Receive disabled via shutdown + size_t RxBytes; ///< Total Rx bytes + size_t RxOobBytes; ///< Urgent Rx bytes + EFI_STATUS RxError; ///< Error during receive + + BOOLEAN bTxDisable; ///< Transmit disabled via shutdown + size_t TxBytes; ///< Normal Tx bytes + size_t TxOobBytes; ///< Urgent Tx bytes + EFI_STATUS TxError; ///< Error during transmit + + // + // Pending connection data + // + BOOLEAN bConnected; ///< Set when connected, cleared by poll + EFI_STATUS ConnectStatus; ///< Connection status + UINTN MaxFifoDepth; ///< Maximum FIFO depth + UINTN FifoDepth; ///< Number of sockets in the FIFO + DT_SOCKET * pFifoHead; ///< Head of the FIFO + DT_SOCKET * pFifoTail; ///< Tail of the FIFO + DT_SOCKET * pNextConnection; ///< Link in the FIFO + + // + // Network use + // + DT_PORT * pPortList; ///< List of ports managed by this socket + EFI_EVENT WaitAccept; ///< Wait for accept completion + + // + // Receive data management + // + UINT32 MaxRxBuf; ///< Maximum size of the receive buffer + struct timeval RxTimeout; ///< Receive timeout + DT_PACKET * pRxFree; ///< Free packet list + DT_PACKET * pRxOobPacketListHead; ///< Urgent data list head + DT_PACKET * pRxOobPacketListTail; ///< Urgent data list tail + DT_PACKET * pRxPacketListHead; ///< Normal data list head + DT_PACKET * pRxPacketListTail; ///< Normal data list tail + + // + // Transmit data management + // + UINT32 MaxTxBuf; ///< Maximum size of the transmit buffer + DT_PACKET * pTxOobPacketListHead; ///< Urgent data list head + DT_PACKET * pTxOobPacketListTail; ///< Urgent data list tail + DT_PACKET * pTxPacketListHead; ///< Normal data list head + DT_PACKET * pTxPacketListTail; ///< Normal data list tail +}GCC_DT_SOCKET; + +#define SOCKET_FROM_PROTOCOL(a) CR(a, DT_SOCKET, SocketProtocol, SOCKET_SIGNATURE) ///< Locate DT_SOCKET from protocol + +/** + Socket layer control structure + + The driver uses this structure to manage the driver. +**/ +typedef struct { + UINTN Signature; ///< Structure identification + + // + // Service binding interface + // + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;///< Driver's binding + + // + // Image data + // + EFI_HANDLE ImageHandle; ///< Image handle + + // + // Network services + // + DT_SERVICE * pTcp4List; ///< List of Tcp4 services + DT_SERVICE * pUdp4List; ///< List of Udp4 services + + // + // Socket management + // + DT_SOCKET * pSocketList; ///< List of sockets + + // + // TCP4 service + // + UINTN TcpCloseMax4; ///< Number of entries in the ring buffer + UINTN TcpCloseIn4; ///< Offset into TcpClose4 ring buffer - Close request + UINTN TcpCloseOut4; ///< Offset into TcpClose4 ring buffer - Close operation + EFI_TCP4_PROTOCOL ** ppTcpClose4; ///< Ring buffer to close TCP4 ports +} DT_LAYER; + +#define LAYER_FROM_SERVICE(a) CR(a, DT_LAYER, ServiceBinding, LAYER_SIGNATURE) ///< Locate DT_LAYER from service binding + +//------------------------------------------------------------------------------ +// Data +//------------------------------------------------------------------------------ + +extern DT_LAYER mEslLayer; + +//------------------------------------------------------------------------------ +// Socket Support Routines +//------------------------------------------------------------------------------ + +/** + Allocate and initialize a DT_SOCKET structure. + + The ::SocketAllocate() function allocates a DT_SOCKET structure + and installs a protocol on ChildHandle. If pChildHandle is a + pointer to NULL, then a new handle is created and returned in + pChildHandle. If pChildHandle is not a pointer to NULL, then + the protocol installs on the existing pChildHandle. + + @param [in, out] pChildHandle Pointer to the handle of the child to create. + If it is NULL, then a new handle is created. + If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI + handle. + @param [in] DebugFlags Flags for debug messages + @param [in, out] ppSocket The buffer to receive the DT_SOCKET structure address. + + @retval EFI_SUCCESS The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child + @retval other The child handle was not created + +**/ +EFI_STATUS +EFIAPI +EslSocketAllocate ( + IN OUT EFI_HANDLE * pChildHandle, + IN UINTN DebugFlags, + IN OUT DT_SOCKET ** ppSocket + ); + +/** + Allocate a packet for a receive or transmit operation + + @param [in] ppPacket Address to receive the DT_PACKET structure + @param [in] LengthInBytes Length of the packet structure + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - The packet was allocated successfully + + **/ +EFI_STATUS +EslSocketPacketAllocate ( + IN DT_PACKET ** ppPacket, + IN size_t LengthInBytes, + IN UINTN DebugFlags + ); + +/** + Free a packet used for receive or transmit operation + + @param [in] pPacket Address of the DT_PACKET structure + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - The packet was allocated successfully + + **/ +EFI_STATUS +EslSocketPacketFree ( + IN DT_PACKET * pPacket, + IN UINTN DebugFlags + ); + +//------------------------------------------------------------------------------ +// Tcp4 Routines +//------------------------------------------------------------------------------ + +/** + Accept a network connection. + + The SocketAccept routine waits for a network connection to the socket. + It is able to return the remote network address to the caller if + requested. + + @param [in] pSocket Address of the socket structure. + + @param [in] pSockAddr Address of a buffer to receive the remote + network address. + + @param [in, out] pSockAddrLength Length in bytes of the address buffer. + On output specifies the length of the + remote network address. + + @retval EFI_SUCCESS Remote address is available + @retval Others Remote address not available + + **/ +EFI_STATUS +EslTcpAccept4 ( + IN DT_SOCKET * pSocket, + IN struct sockaddr * pSockAddr, + IN OUT socklen_t * pSockAddrLength + ); + +/** + Bind a name to a socket. + + The ::TcpBind4 routine connects a name to A TCP4 stack on the local machine. + + @param [in] pSocket Address of the socket structure. + + @param [in] pSockAddr Address of a sockaddr structure that contains the + connection point on the local machine. An IPv4 address + of INADDR_ANY specifies that the connection is made to + all of the network stacks on the platform. Specifying a + specific IPv4 address restricts the connection to the + network stack supporting that address. Specifying zero + for the port causes the network layer to assign a port + number from the dynamic range. Specifying a specific + port number causes the network layer to use that port. + + @param [in] SockAddrLen Specifies the length in bytes of the sockaddr structure. + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslTcpBind4 ( + IN DT_SOCKET * pSocket, + IN const struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ); + +/** + Poll for completion of the connection attempt. + + The ::TcpConnectPoll4 routine determines when the connection + attempt transitions from being in process to being complete. + + @param [in] pSocket Address of the socket structure. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslTcpConnectPoll4 ( + IN DT_SOCKET * pSocket + ); + +/** + Connect to a remote system via the network. + + The ::TcpConnectStart4= routine starts the connection processing + for a TCP4 port. + + @param [in] pSocket Address of the socket structure. + + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslTcpConnectStart4 ( + IN DT_SOCKET * pSocket, + IN const struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ); + +/** + Initialize the TCP4 service. + + This routine initializes the TCP4 service after its service binding + protocol was located on a controller. + + @param [in] pService DT_SERVICE structure address + + @retval EFI_SUCCESS The service was properly initialized + @retval other A failure occurred during the service initialization + +**/ +EFI_STATUS +EFIAPI +EslTcpInitialize4 ( + IN DT_SERVICE * pService + ); + +/** + Get the local socket address + + @param [in] pSocket Address of the socket structure. + + @param [out] pAddress Network address to receive the local system address + + @param [in,out] pAddressLength Length of the local network address structure + + @retval EFI_SUCCESS - Address available + @retval Other - Failed to get the address + +**/ +EFI_STATUS +EslTcpGetLocalAddress4 ( + IN DT_SOCKET * pSocket, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ); + +/** + Get the remote socket address + + @param [in] pSocket Address of the socket structure. + + @param [out] pAddress Network address to receive the remote system address + + @param [in,out] pAddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Address available + @retval Other - Failed to get the address + +**/ +EFI_STATUS +EslTcpGetRemoteAddress4 ( + IN DT_SOCKET * pSocket, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ); + +/** + Establish the known port to listen for network connections. + + The ::Tcp4Listen routine places the port into a state that enables connection + attempts. Connections are placed into FIFO order in a queue to be serviced + by the application. The application calls the ::Tcp4Accept routine to remove + the next connection from the queue and get the associated socket. The + POSIX + documentation for the listen routine is available online for reference. + + @param [in] pSocket Address of the socket structure. + + @retval EFI_SUCCESS - Socket successfully created + @retval Other - Failed to enable the socket for listen + +**/ +EFI_STATUS +EslTcpListen4 ( + IN DT_SOCKET * pSocket + ); + +/** + Process the connection attempt + + A system has initiated a connection attempt with a socket in the + listen state. Attempt to complete the connection. + + @param Event The listeen completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslTcpListenComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ); + +/** + Allocate and initialize a DT_PORT structure. + + @param [in] pSocket Address of the socket structure. + @param [in] pService Address of the DT_SERVICE structure. + @param [in] ChildHandle TCP4 child handle + @param [in] pIpAddress Buffer containing IP4 network address of the local host + @param [in] PortNumber Tcp4 port number + @param [in] DebugFlags Flags for debug messages + @param [out] ppPort Buffer to receive new DT_PORT structure address + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslTcpPortAllocate4 ( + IN DT_SOCKET * pSocket, + IN DT_SERVICE * pService, + IN EFI_HANDLE ChildHandle, + IN CONST UINT8 *pIpAddress, + IN UINT16 PortNumber, + IN UINTN DebugFlags, + OUT DT_PORT ** ppPort + ); + +/** + Close a TCP4 port. + + This routine releases the resources allocated by + ::TcpPortAllocate4(). + + @param [in] pPort Address of the port structure. + + @retval EFI_SUCCESS The port is closed + @retval other Port close error + +**/ +EFI_STATUS +EslTcpPortClose4 ( + IN DT_PORT * pPort + ); + +/** + Process the port close completion + + @param Event The close completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslTcpPortCloseComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ); + +/** + Port close state 3 + + Continue the close operation after the receive is complete. + + @param [in] pPort Address of the port structure. + + @retval EFI_SUCCESS The port is closed + @retval EFI_NOT_READY The port is still closing + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslTcpPortCloseRxDone4 ( + IN DT_PORT * pPort + ); + +/** + Start the close operation on a TCP4 port. + + @param [in] pPort Address of the port structure. + @param [in] bAbort Set TRUE to abort active transfers + @param [in] DebugFlags Flags for debug messages + +**/ +EFI_STATUS +EslTcpPortCloseStart4 ( + IN DT_PORT * pPort, + IN BOOLEAN bAbort, + IN UINTN DebugFlags + ); + +/** + Port close state 2 + + Continue the close operation after the transmission is complete. + + @param [in] pPort Address of the port structure. + + @retval EFI_NOT_READY The port is still closing + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslTcpPortCloseTxDone4 ( + IN DT_PORT * pPort + ); + +/** + Receive data from a network connection. + + + @param [in] pSocket Address of a DT_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [out] pAddress Network address to receive the remote system address + + @param [in,out] pAddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Socket data successfully received + + **/ +EFI_STATUS +EslTcpReceive4 ( + IN DT_SOCKET * pSocket, + IN INT32 Flags, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ); + +/** + Cancel the receive operations + + @param [in] pSocket Address of a DT_SOCKET structure + + @retval EFI_SUCCESS - The cancel was successful + + **/ +EFI_STATUS +EslTcpRxCancel4 ( + IN DT_SOCKET * pSocket + ); + +/** + Process the receive completion + + Buffer the data that was just received. + + @param Event The receive completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslTcpRxComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ); + +/** + Start a receive operation + + @param [in] pPort Address of the DT_PORT structure. + + **/ +VOID +EslTcpRxStart4 ( + IN DT_PORT * pPort + ); + +/** + Shutdown the TCP4 service. + + This routine undoes the work performed by ::TcpInitialize4. + + @param [in] pService DT_SERVICE structure address + +**/ +VOID +EFIAPI +EslTcpShutdown4 ( + IN DT_SERVICE * pService + ); + +/** + Determine if the socket is configured. + + + @param [in] pSocket Address of a DT_SOCKET structure + + @retval EFI_SUCCESS - The port is connected + @retval EFI_NOT_STARTED - The port is not connected + + **/ + EFI_STATUS + EslTcpSocketIsConfigured4 ( + IN DT_SOCKET * pSocket + ); + +/** + Buffer data for transmission over a network connection. + + This routine is called by the socket layer API to buffer + data for transmission. When necessary, this routine will + start the transmit engine that performs the data transmission + on the network connection. + + @param [in] pSocket Address of a DT_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @retval EFI_SUCCESS - Socket data successfully buffered + + **/ +EFI_STATUS +EslTcpTxBuffer4 ( + IN DT_SOCKET * pSocket, + IN int Flags, + IN size_t BufferLength, + IN CONST UINT8 * pBuffer, + OUT size_t * pDataLength + ); + +/** + Process the normal data transmit completion + + @param Event The normal transmit completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslTcpTxComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ); + +/** + Process the urgent data transmit completion + + @param Event The urgent transmit completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslTcpTxOobComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ); + +/** + Transmit data using a network connection. + + + @param [in] pPort Address of a DT_PORT structure + @param [in] pToken Address of either the OOB or normal transmit token + @param [in] ppQueueHead Transmit queue head address + @param [in] ppQueueTail Transmit queue tail address + @param [in] ppPacket Active transmit packet address + + **/ +VOID +EslTcpTxStart4 ( + IN DT_PORT * pPort, + IN EFI_TCP4_IO_TOKEN * pToken, + IN DT_PACKET ** ppQueueHead, + IN DT_PACKET ** ppQueueTail, + IN DT_PACKET ** ppPacket + ); + +//------------------------------------------------------------------------------ +// Udp4 Routines +//------------------------------------------------------------------------------ + +/** + Bind a name to a socket. + + The ::UdpBind4 routine connects a name to a UDP4 stack on the local machine. + + @param [in] pSocket Address of the socket structure. + + @param [in] pSockAddr Address of a sockaddr structure that contains the + connection point on the local machine. An IPv4 address + of INADDR_ANY specifies that the connection is made to + all of the network stacks on the platform. Specifying a + specific IPv4 address restricts the connection to the + network stack supporting that address. Specifying zero + for the port causes the network layer to assign a port + number from the dynamic range. Specifying a specific + port number causes the network layer to use that port. + + @param [in] SockAddrLen Specifies the length in bytes of the sockaddr structure. + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslUdpBind4 ( + IN DT_SOCKET * pSocket, + IN const struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ); + +/** + Initialize the UDP4 service. + + This routine initializes the UDP4 service after its service binding + protocol was located on a controller. + + @param [in] pService DT_SERVICE structure address + + @retval EFI_SUCCESS The service was properly initialized + @retval other A failure occurred during the service initialization + +**/ +EFI_STATUS +EFIAPI +EslUdpInitialize4 ( + IN DT_SERVICE * pService + ); + +/** + Allocate and initialize a DT_PORT structure. + + @param [in] pSocket Address of the socket structure. + @param [in] pService Address of the DT_SERVICE structure. + @param [in] ChildHandle Udp4 child handle + @param [in] pIpAddress Buffer containing IP4 network address of the local host + @param [in] PortNumber Udp4 port number + @param [in] DebugFlags Flags for debug messages + @param [out] ppPort Buffer to receive new DT_PORT structure address + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslUdpPortAllocate4 ( + IN DT_SOCKET * pSocket, + IN DT_SERVICE * pService, + IN EFI_HANDLE ChildHandle, + IN CONST UINT8 * pIpAddress, + IN UINT16 PortNumber, + IN UINTN DebugFlags, + OUT DT_PORT ** ppPort + ); + +/** + Close a UDP4 port. + + This routine releases the resources allocated by + ::UdpPortAllocate4(). + + @param [in] pPort Address of the port structure. + + @retval EFI_SUCCESS The port is closed + @retval other Port close error + +**/ +EFI_STATUS +EslUdpPortClose4 ( + IN DT_PORT * pPort + ); + +/** + Start the close operation on a UDP4 port, state 1. + + Closing a port goes through the following states: + 1. Port close starting - Mark the port as closing and wait for transmission to complete + 2. Port TX close done - Transmissions complete, close the port and abort the receives + 3. Port RX close done - Receive operations complete, close the port + 4. Port closed - Release the port resources + + @param [in] pPort Address of the port structure. + @param [in] bCloseNow Set TRUE to abort active transfers + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS The port is closed, not normally returned + @retval EFI_NOT_READY The port has started the closing process + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslUdpPortCloseStart4 ( + IN DT_PORT * pPort, + IN BOOLEAN bCloseNow, + IN UINTN DebugFlags + ); + +/** + Port close state 2 + + Continue the close operation after the transmission is complete. + + @param [in] pPort Address of the port structure. + + @retval EFI_SUCCESS The port is closed, not normally returned + @retval EFI_NOT_READY The port is still closing + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslUdpPortCloseTxDone4 ( + IN DT_PORT * pPort + ); + +/** + Connect to a remote system via the network. + + The ::UdpConnectStart4= routine sets the remote address for the connection. + + @param [in] pSocket Address of the socket structure. + + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslUdpConnect4 ( + IN DT_SOCKET * pSocket, + IN const struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ); + +/** + Get the local socket address + + @param [in] pSocket Address of the socket structure. + + @param [out] pAddress Network address to receive the local system address + + @param [in,out] pAddressLength Length of the local network address structure + + @retval EFI_SUCCESS - Address available + @retval Other - Failed to get the address + +**/ +EFI_STATUS +EslUdpGetLocalAddress4 ( + IN DT_SOCKET * pSocket, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ); + +/** + Get the remote socket address + + @param [in] pSocket Address of the socket structure. + + @param [out] pAddress Network address to receive the remote system address + + @param [in,out] pAddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Address available + @retval Other - Failed to get the address + +**/ +EFI_STATUS +EslUdpGetRemoteAddress4 ( + IN DT_SOCKET * pSocket, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ); + +/** + Receive data from a network connection. + + To minimize the number of buffer copies, the ::UdpRxComplete4 + routine queues the UDP4 driver's buffer to a list of datagrams + waiting to be received. The socket driver holds on to the + buffers from the UDP4 driver until the application layer requests + the data or the socket is closed. + + The application calls this routine in the socket layer to + receive datagrams from one or more remote systems. This routine + removes the next available datagram from the list of datagrams + and copies the data from the UDP4 driver's buffer into the + application's buffer. The UDP4 driver's buffer is then returned. + + @param [in] pSocket Address of a DT_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [out] pAddress Network address to receive the remote system address + + @param [in,out] pAddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Socket data successfully received + +**/ +EFI_STATUS +EslUdpReceive4 ( + IN DT_SOCKET * pSocket, + IN INT32 Flags, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ); + +/** + Cancel the receive operations + + @param [in] pSocket Address of a DT_SOCKET structure + + @retval EFI_SUCCESS - The cancel was successful + + **/ +EFI_STATUS +EslUdpRxCancel4 ( + IN DT_SOCKET * pSocket + ); + +/** + Process the receive completion + + Keep the UDP4 driver's buffer and append it to the list of + datagrams for the application to receive. The UDP4 driver's + buffer will be returned by either ::UdpReceive4 or + ::UdpPortCloseTxDone4. + + @param Event The receive completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslUdpRxComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ); + +/** + Start a receive operation + + @param [in] pPort Address of the DT_PORT structure. + + **/ +VOID +EslUdpRxStart4 ( + IN DT_PORT * pPort + ); + +/** + Determine if the socket is configured. + + + @param [in] pSocket Address of a DT_SOCKET structure + + @retval EFI_SUCCESS - The port is connected + @retval EFI_NOT_STARTED - The port is not connected + + **/ + EFI_STATUS + EslUdpSocketIsConfigured4 ( + IN DT_SOCKET * pSocket + ); + +/** + Process the transmit completion + + @param Event The normal transmit completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslUdpTxComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ); + +/** + Shutdown the UDP4 service. + + This routine undoes the work performed by ::UdpInitialize4. + + @param [in] pService DT_SERVICE structure address + +**/ +VOID +EFIAPI +EslUdpShutdown4 ( + IN DT_SERVICE * pService + ); + +/** + Buffer data for transmission over a network connection. + + This routine is called by the socket layer API to buffer + data for transmission. The data is copied into a local buffer + freeing the application buffer for reuse upon return. When + necessary, this routine will start the transmit engine that + performs the data transmission on the network connection. The + transmit engine transmits the data a packet at a time over the + network connection. + + Transmission errors are returned during the next transmission or + during the close operation. Only buffering errors are returned + during the current transmission attempt. + + @param [in] pSocket Address of a DT_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [in] pAddress Network address of the remote system address + + @param [in] AddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Socket data successfully buffered + +**/ +EFI_STATUS +EslUdpTxBuffer4 ( + IN DT_SOCKET * pSocket, + IN int Flags, + IN size_t BufferLength, + IN CONST UINT8 * pBuffer, + OUT size_t * pDataLength, + IN const struct sockaddr * pAddress, + IN socklen_t AddressLength + ); + +/** + Transmit data using a network connection. + + @param [in] pPort Address of a DT_PORT structure + + **/ +VOID +EslUdpTxStart4 ( + IN DT_PORT * pPort + ); + +//------------------------------------------------------------------------------ + +#endif // _SOCKET_H_ diff --git a/StdLib/EfiSocketLib/Tcp4.c b/StdLib/EfiSocketLib/Tcp4.c new file mode 100644 index 0000000000..2840dd7e0e --- /dev/null +++ b/StdLib/EfiSocketLib/Tcp4.c @@ -0,0 +1,3415 @@ +/** @file + Implement the TCP4 driver support for the socket layer. + + Copyright (c) 2011, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Socket.h" + + +/** + Accept a network connection. + + The SocketAccept routine waits for a network connection to the socket. + It is able to return the remote network address to the caller if + requested. + + @param [in] pSocket Address of the socket structure. + + @param [in] pSockAddr Address of a buffer to receive the remote + network address. + + @param [in, out] pSockAddrLength Length in bytes of the address buffer. + On output specifies the length of the + remote network address. + + @retval EFI_SUCCESS Remote address is available + @retval Others Remote address not available + + **/ +EFI_STATUS +EslTcpAccept4 ( + IN DT_SOCKET * pSocket, + IN struct sockaddr * pSockAddr, + IN OUT socklen_t * pSockAddrLength + ) +{ + DT_PORT * pPort; + struct sockaddr_in * pRemoteAddress; + DT_TCP4_CONTEXT * pTcp4; + UINT32 RemoteAddress; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Validate the socket length + // + pRemoteAddress = (struct sockaddr_in *) pSockAddr; + if (( NULL == pSockAddrLength ) + || ( sizeof ( *pRemoteAddress ) > *pSockAddrLength )) { + // + // Invalid socket address + // + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + DEBUG (( DEBUG_ACCEPT, + "ERROR - Invalid address length\r\n" )); + } + else { + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Locate the address context + // + pPort = pSocket->pPortList; + pTcp4 = &pPort->Context.Tcp4; + + // + // Fill-in the remote address structure + // + ZeroMem ( pRemoteAddress, sizeof ( *pRemoteAddress )); + pRemoteAddress->sin_len = sizeof ( *pRemoteAddress ); + pRemoteAddress->sin_family = AF_INET; + pRemoteAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.RemotePort ); + RemoteAddress = pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3]; + RemoteAddress <<= 8; + RemoteAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2]; + RemoteAddress <<= 8; + RemoteAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1]; + RemoteAddress <<= 8; + RemoteAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0]; + pRemoteAddress->sin_addr.s_addr = RemoteAddress; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Bind a name to a socket. + + The ::TcpBind4 routine connects a name to a TCP4 stack on the local machine. + + @param [in] pSocket Address of the socket structure. + + @param [in] pSockAddr Address of a sockaddr structure that contains the + connection point on the local machine. An IPv4 address + of INADDR_ANY specifies that the connection is made to + all of the network stacks on the platform. Specifying a + specific IPv4 address restricts the connection to the + network stack supporting that address. Specifying zero + for the port causes the network layer to assign a port + number from the dynamic range. Specifying a specific + port number causes the network layer to use that port. + + @param [in] SockAddrLen Specifies the length in bytes of the sockaddr structure. + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslTcpBind4 ( + IN DT_SOCKET * pSocket, + IN const struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ) +{ + EFI_HANDLE ChildHandle; + DT_LAYER * pLayer; + DT_PORT * pPort; + DT_SERVICE * pService; + CONST struct sockaddr_in * pIp4Address; + EFI_SERVICE_BINDING_PROTOCOL * pTcp4Service; + EFI_STATUS Status; + EFI_STATUS TempStatus; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Assume success + // + pSocket->errno = 0; + Status = EFI_SUCCESS; + + // + // Validate the address length + // + pIp4Address = (CONST struct sockaddr_in *) pSockAddr; + if ( SockAddrLength >= ( sizeof ( *pIp4Address ) + - sizeof ( pIp4Address->sin_zero ))) { + + // + // Walk the list of services + // + pLayer = &mEslLayer; + pService = pLayer->pTcp4List; + while ( NULL != pService ) { + // + // Create the TCP port + // + pTcp4Service = pService->pInterface; + ChildHandle = NULL; + Status = pTcp4Service->CreateChild ( pTcp4Service, + &ChildHandle ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_BIND | DEBUG_POOL, + "0x%08x: Tcp4 port handle created\r\n", + ChildHandle )); + + // + // Open the port + // + Status = EslTcpPortAllocate4 ( pSocket, + pService, + ChildHandle, + (UINT8 *) &pIp4Address->sin_addr.s_addr, + SwapBytes16 ( pIp4Address->sin_port ), + DEBUG_BIND, + &pPort ); + } + else { + DEBUG (( DEBUG_BIND | DEBUG_POOL, + "ERROR - Failed to open Tcp4 port handle, Status: %r\r\n", + Status )); + ChildHandle = NULL; + } + + // + // Close the port if necessary + // + if (( EFI_ERROR ( Status )) && ( NULL != ChildHandle )) { + TempStatus = pTcp4Service->DestroyChild ( pTcp4Service, + ChildHandle ); + if ( !EFI_ERROR ( TempStatus )) { + DEBUG (( DEBUG_BIND | DEBUG_POOL, + "0x%08x: Tcp4 port handle destroyed\r\n", + ChildHandle )); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_BIND | DEBUG_POOL, + "ERROR - Failed to destroy the Tcp4 port handle 0x%08x, Status: %r\r\n", + ChildHandle, + TempStatus )); + ASSERT ( EFI_SUCCESS == TempStatus ); + } + } + + // + // Set the next service + // + pService = pService->pNext; + } + + // + // Verify that at least one network connection was found + // + if ( NULL == pSocket->pPortList ) { + DEBUG (( DEBUG_BIND | DEBUG_POOL | DEBUG_INIT, + "Socket address %d.%d.%d.%d (0x%08x) is not available!\r\n", + ( pIp4Address->sin_addr.s_addr >> 24 ) & 0xff, + ( pIp4Address->sin_addr.s_addr >> 16 ) & 0xff, + ( pIp4Address->sin_addr.s_addr >> 8 ) & 0xff, + pIp4Address->sin_addr.s_addr & 0xff, + pIp4Address->sin_addr.s_addr )); + pSocket->errno = EADDRNOTAVAIL; + Status = EFI_INVALID_PARAMETER; + } + } + else { + DEBUG (( DEBUG_BIND, + "ERROR - Invalid TCP4 address length: %d\r\n", + SockAddrLength )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Attempt to connect to a remote TCP port + + @param [in] pSocket Address of the socket structure. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslTcpConnectAttempt4 ( + IN DT_SOCKET * pSocket + ) +{ + DT_PORT * pPort; + DT_TCP4_CONTEXT * pTcp4; + EFI_TCP4_PROTOCOL * pTcp4Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if any more local adapters are available + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Configure the port + // + pTcp4 = &pPort->Context.Tcp4; + pTcp4->ConfigData.AccessPoint.ActiveFlag = TRUE; + pTcp4->ConfigData.TimeToLive = 255; + pTcp4Protocol = pTcp4->pProtocol; + Status = pTcp4Protocol->Configure ( pTcp4Protocol, + &pTcp4->ConfigData ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to configure the Tcp4 port, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NO_MAPPING: + pSocket->errno = EAFNOSUPPORT; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = EOPNOTSUPP; + break; + } + } + else { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port configured\r\n", + pPort )); + pTcp4->bConfigured = TRUE; + + // + // Attempt the connection to the remote system + // + Status = pTcp4Protocol->Connect ( pTcp4Protocol, + &pTcp4->ConnectToken ); + if ( !EFI_ERROR ( Status )) { + // + // Connection in progress + // + pSocket->errno = EINPROGRESS; + Status = EFI_NOT_READY; + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port attempting connection to %d.%d.%d.%d:%d\r\n", + pPort, + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp4->ConfigData.AccessPoint.RemotePort )); + } + else { + // + // Connection error + // + pSocket->errno = EINVAL; + DEBUG (( DEBUG_CONNECT, + "ERROR - Port 0x%08x not connected, Status: %r\r\n", + pPort, + Status )); + } + } + } + else { + // + // No more local adapters available + // + pSocket->errno = ENETUNREACH; + Status = EFI_NO_RESPONSE; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the remote connection attempt + + A connection attempt to a remote system has just completed when + this routine is invoked. Release the port in the case of an + error and start a connection attempt on the next port. If the + connection attempt was successful, then release all of the other + ports. + + @param Event The connect completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslTcpConnectComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ) +{ + BOOLEAN bRemoveFirstPort; + BOOLEAN bRemovePorts; + DT_PORT * pNextPort; + DT_SOCKET * pSocket; + DT_TCP4_CONTEXT * pTcp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the TCP context + // + pSocket = pPort->pSocket; + pTcp4 = &pPort->Context.Tcp4; + + // + // Get the connection status + // + bRemoveFirstPort = FALSE; + bRemovePorts = FALSE; + Status = pTcp4->ConnectToken.CompletionToken.Status; + pSocket->ConnectStatus = Status; + if ( !EFI_ERROR ( Status )) { + // + // The connection was successful + // + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port connected to %d.%d.%d.%d:%d\r\n", + pPort, + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [0], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [1], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [2], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [3], + pTcp4->ConfigData.AccessPoint.RemotePort )); + + // + // Remove the rest of the ports + // + bRemovePorts = TRUE; + } + else { + // + // The connection failed + // + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port connection to %d.%d.%d.%d:%d failed, Status: %r\r\n", + pPort, + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [0], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [1], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [2], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [3], + pTcp4->ConfigData.AccessPoint.RemotePort, + Status )); + + // + // Close the current port + // + Status = EslTcpPortClose4 ( pPort ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port closed\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to close port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } + + // + // Try to connect using the next port + // + Status = EslTcpConnectAttempt4 ( pSocket ); + if ( EFI_NOT_READY != Status ) { + pSocket->ConnectStatus = Status; + bRemoveFirstPort = TRUE; + } + } + + // + // Remove the ports if necessary + // + if ( bRemoveFirstPort || bRemovePorts ) { + // + // Remove the first port if necessary + // + pPort = pSocket->pPortList; + if (( !bRemoveFirstPort ) && ( NULL != pPort )) { + pPort = pPort->pLinkSocket; + } + + // + // Remove the rest of the list + // + while ( NULL != pPort ) { + pNextPort = pPort->pLinkSocket; + EslTcpPortClose4 ( pPort ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port closed\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to close port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } + pPort = pNextPort; + } + + // + // Notify the poll routine + // + pSocket->bConnected = TRUE; + } + + DBG_EXIT ( ); +} + + +/** + Poll for completion of the connection attempt. + + The ::TcpConnectPoll4 routine determines when the connection + attempt transitions from being in process to being complete. + + @param [in] pSocket Address of the socket structure. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslTcpConnectPoll4 ( + IN DT_SOCKET * pSocket + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if the connection is complete + // + if ( !pSocket->bConnected ) { + // + // Not connected + // + pSocket->errno = EAGAIN; + Status = EFI_NOT_READY; + } + else { + // + // The connection processing is complete + // + pSocket->bConnected = FALSE; + + // + // Translate the connection status + // + Status = pSocket->ConnectStatus; + switch ( Status ) { + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_ABORTED: + pSocket->errno = ECONNREFUSED; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EINVAL; + break; + + case EFI_NO_MAPPING: + case EFI_NO_RESPONSE: + pSocket->errno = EHOSTUNREACH; + break; + + case EFI_NO_MEDIA: + pSocket->errno = ENETDOWN; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOMEM; + break; + + case EFI_SUCCESS: + pSocket->errno = 0; + pSocket->bConfigured = TRUE; + break; + + case EFI_TIMEOUT: + pSocket->errno = ETIMEDOUT; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = ENOTSUP; + break; + + case 0x80000069: + pSocket->errno = ECONNRESET; + break; + } + } + + // + // Return the initialization status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Connect to a remote system via the network. + + The ::TcpConnectStart4= routine starts the connection processing + for a TCP4 port. + + @param [in] pSocket Address of the socket structure. + + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslTcpConnectStart4 ( + IN DT_SOCKET * pSocket, + IN const struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ) +{ + struct sockaddr_in LocalAddress; + DT_PORT * pPort; + DT_TCP4_CONTEXT * pTcp4; + CONST struct sockaddr_in * pIp4Address; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Validate the address length + // + Status = EFI_SUCCESS; + pIp4Address = (CONST struct sockaddr_in *) pSockAddr; + if ( SockAddrLength >= ( sizeof ( *pIp4Address ) + - sizeof ( pIp4Address->sin_zero ))) { + // + // Determine if BIND was already called + // + if ( NULL == pSocket->pPortList ) { + // + // Allow any local port + // + ZeroMem ( &LocalAddress, sizeof ( LocalAddress )); + LocalAddress.sin_len = sizeof ( LocalAddress ); + LocalAddress.sin_family = AF_INET; + Status = EslSocketBind ( &pSocket->SocketProtocol, + (struct sockaddr *)&LocalAddress, + LocalAddress.sin_len, + &pSocket->errno ); + } + if ( NULL != pSocket->pPortList ) { + // + // Walk the list of ports + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Set the remote address + // + pTcp4 = &pPort->Context.Tcp4; + *(UINT32 *)&pTcp4->ConfigData.AccessPoint.RemoteAddress = pIp4Address->sin_addr.s_addr; + pTcp4->ConfigData.AccessPoint.RemotePort = SwapBytes16 ( pIp4Address->sin_port ); + + // + // Set the next port + // + pPort = pPort->pLinkSocket; + } + + // + // Attempt a connection using the first adapter + // + Status = EslTcpConnectAttempt4 ( pSocket ); + } + } + else { + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid TCP4 address length: %d\r\n", + SockAddrLength )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + } + + // + // Return the initialization status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Initialize the TCP4 service. + + This routine initializes the TCP4 service after its service binding + protocol was located on a controller. + + @param [in] pService DT_SERVICE structure address + + @retval EFI_SUCCESS The service was properly initialized + @retval other A failure occurred during the service initialization + +**/ +EFI_STATUS +EFIAPI +EslTcpInitialize4 ( + IN DT_SERVICE * pService + ) +{ + DT_LAYER * pLayer; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Identify the service + // + pService->NetworkType = NETWORK_TYPE_TCP4; + + // + // Connect this service to the service list + // + pLayer = &mEslLayer; + pService->pNext = pLayer->pTcp4List; + pLayer->pTcp4List = pService; + + // + // Assume the list is empty + // + Status = EFI_SUCCESS; + + // + // Return the initialization status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Get the local socket address + + @param [in] pSocket Address of the socket structure. + + @param [out] pAddress Network address to receive the local system address + + @param [in,out] pAddressLength Length of the local network address structure + + @retval EFI_SUCCESS - Address available + @retval Other - Failed to get the address + +**/ +EFI_STATUS +EslTcpGetLocalAddress4 ( + IN DT_SOCKET * pSocket, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ) +{ + socklen_t LengthInBytes; + DT_PORT * pPort; + struct sockaddr_in * pLocalAddress; + DT_TCP4_CONTEXT * pTcp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Verify that there is just a single connection + // + pPort = pSocket->pPortList; + if (( NULL != pPort ) && ( NULL == pPort->pLinkSocket )) { + // + // Verify the address length + // + LengthInBytes = sizeof ( struct sockaddr_in ); + if ( LengthInBytes <= * pAddressLength ) { + // + // Return the local address + // + pTcp4 = &pPort->Context.Tcp4; + pLocalAddress = (struct sockaddr_in *)pAddress; + ZeroMem ( pLocalAddress, LengthInBytes ); + pLocalAddress->sin_family = AF_INET; + pLocalAddress->sin_len = (uint8_t)LengthInBytes; + pLocalAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.StationPort ); + CopyMem ( &pLocalAddress->sin_addr, + &pTcp4->ConfigData.AccessPoint.StationAddress.Addr[0], + sizeof ( pLocalAddress->sin_addr )); + pSocket->errno = 0; + Status = EFI_SUCCESS; + } + else { + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + } + } + else { + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Get the remote socket address + + @param [in] pSocket Address of the socket structure. + + @param [out] pAddress Network address to receive the remote system address + + @param [in,out] pAddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Address available + @retval Other - Failed to get the address + +**/ +EFI_STATUS +EslTcpGetRemoteAddress4 ( + IN DT_SOCKET * pSocket, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ) +{ + socklen_t LengthInBytes; + DT_PORT * pPort; + struct sockaddr_in * pRemoteAddress; + DT_TCP4_CONTEXT * pTcp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Verify that there is just a single connection + // + pPort = pSocket->pPortList; + if (( NULL != pPort ) && ( NULL == pPort->pLinkSocket )) { + // + // Verify the address length + // + LengthInBytes = sizeof ( struct sockaddr_in ); + if ( LengthInBytes <= * pAddressLength ) { + // + // Return the local address + // + pTcp4 = &pPort->Context.Tcp4; + pRemoteAddress = (struct sockaddr_in *)pAddress; + ZeroMem ( pRemoteAddress, LengthInBytes ); + pRemoteAddress->sin_family = AF_INET; + pRemoteAddress->sin_len = (uint8_t)LengthInBytes; + pRemoteAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.RemotePort ); + CopyMem ( &pRemoteAddress->sin_addr, + &pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin_addr )); + pSocket->errno = 0; + Status = EFI_SUCCESS; + } + else { + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + } + } + else { + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Establish the known port to listen for network connections. + + The ::Tcp4Listen routine places the port into a state that enables connection + attempts. Connections are placed into FIFO order in a queue to be serviced + by the application. The application calls the ::Tcp4Accept routine to remove + the next connection from the queue and get the associated socket. The + POSIX + documentation for the listen routine is available online for reference. + + @param [in] pSocket Address of the socket structure. + + @retval EFI_SUCCESS - Socket successfully created + @retval Other - Failed to enable the socket for listen + +**/ +EFI_STATUS +EslTcpListen4 ( + IN DT_SOCKET * pSocket + ) +{ + DT_PORT * pNextPort; + DT_PORT * pPort; + DT_TCP4_CONTEXT * pTcp4; + EFI_TCP4_PROTOCOL * pTcp4Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Use for/break instead of goto + // + for ( ; ; ) { + // + // Assume no ports are available + // + pSocket->errno = EOPNOTSUPP; + Status = EFI_NOT_READY; + + // + // Walk the list of ports + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Assume success + // + pSocket->errno = 0; + + // + // Use for/break insteak of goto + // + for ( ; ; ) { + // + // Create the listen completion event + // + pTcp4 = &pPort->Context.Tcp4; + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcpListenComplete4, + pPort, + &pTcp4->ListenToken.CompletionToken.Event ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DEBUG_LISTEN, + "ERROR - Failed to create the listen completion event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_POOL, + "0x%08x: Created listen completion event\r\n", + pTcp4->ListenToken.CompletionToken.Event )); + + // + // Configure the port + // + pTcp4Protocol = pTcp4->pProtocol; + Status = pTcp4Protocol->Configure ( pTcp4Protocol, + &pTcp4->ConfigData ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed to configure the Tcp4 port, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NO_MAPPING: + pSocket->errno = EAFNOSUPPORT; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = EOPNOTSUPP; + break; + } + break; + } + DEBUG (( DEBUG_LISTEN, + "0x%08x: Port configured\r\n", + pPort )); + pTcp4->bConfigured = TRUE; + + // + // Start the listen operation on the port + // + Status = pTcp4Protocol->Accept ( pTcp4Protocol, + &pTcp4->ListenToken ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed Tcp4 accept, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NOT_STARTED: + pSocket->errno = ENETDOWN; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + } + break; + } + DEBUG (( DEBUG_LISTEN, + "0x%08x: Listen pending on Port\r\n", + pPort )); + + // + // Listen is pending on this port + // + break; + } + + // + // Get the next port + // + pNextPort = pPort->pLinkSocket; + + // + // Close the port upon error + // + if ( EFI_ERROR ( Status )) + { + EslTcpPortCloseStart4 ( pPort, TRUE, DEBUG_LISTEN ); + } + + // + // Set the next port + // + pPort = pNextPort; + } + + // + // Determine if any ports are in the listen state + // + if ( NULL == pSocket->pPortList ) { + // + // No ports in the listen state + // + pSocket->MaxFifoDepth = 0; + + // + // Return the last error detected + // + break; + } + + // + // Mark the socket as configured + // + pSocket->bConfigured = TRUE; + + // + // All done + // + DEBUG (( DEBUG_LISTEN, + "0x%08x: pSocket - Listen pending on socket\r\n", + pSocket )); + break; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the connection attempt + + A system has initiated a connection attempt with a socket in the + listen state. Attempt to complete the connection. + + @param Event The listen completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslTcpListenComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ) +{ + EFI_HANDLE ChildHandle; + EFI_TCP4_CONFIG_DATA * pConfigData; + DT_LAYER * pLayer; + DT_PORT * pNewPort; + DT_SOCKET * pNewSocket; + DT_SOCKET * pSocket; + DT_TCP4_CONTEXT * pTcp4; + EFI_TCP4_PROTOCOL * pTcp4Protocol; + EFI_STATUS Status; + EFI_HANDLE TcpPortHandle; + EFI_STATUS TempStatus; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Determine if this connection fits into the connection FIFO + // + pSocket = pPort->pSocket; + TcpPortHandle = pPort->Context.Tcp4.ListenToken.NewChildHandle; + if (( SOCKET_STATE_LISTENING == pSocket->State ) + && ( pSocket->MaxFifoDepth > pSocket->FifoDepth )) { + // + // Allocate a socket for this connection + // + ChildHandle = NULL; + pLayer = &mEslLayer; + Status = EslSocketAllocate ( &ChildHandle, + DEBUG_CONNECTION, + &pNewSocket ); + if ( !EFI_ERROR ( Status )) { + // + // Clone the socket parameters + // + pNewSocket->Domain = pSocket->Domain; + pNewSocket->Protocol = pSocket->Protocol; + pNewSocket->Type = pSocket->Type; + + // + // Allocate a port for this connection + // + pTcp4 = &pPort->Context.Tcp4; + Status = EslTcpPortAllocate4 ( pNewSocket, + pPort->pService, + TcpPortHandle, + &pTcp4->ConfigData.AccessPoint.StationAddress.Addr[0], + 0, + DEBUG_CONNECTION, + &pNewPort ); + if ( !EFI_ERROR ( Status )) { + // + // Restart the listen operation on the port + // + pTcp4Protocol = pTcp4->pProtocol; + Status = pTcp4Protocol->Accept ( pTcp4Protocol, + &pTcp4->ListenToken ); + + // + // Close the TCP port using SocketClose + // + TcpPortHandle = NULL; + pTcp4 = &pNewPort->Context.Tcp4; + pTcp4->bConfigured = TRUE; + + // + // Check for an accept call error + // + if ( !EFI_ERROR ( Status )) { + // + // Get the port configuration + // + pConfigData = &pTcp4->ConfigData; + pConfigData->ControlOption = &pTcp4->Option; + pTcp4Protocol = pTcp4->pProtocol; + Status = pTcp4Protocol->GetModeData ( pTcp4Protocol, + NULL, + pConfigData, + NULL, + NULL, + NULL ); + if ( !EFI_ERROR ( Status )) { + // + // Add the new socket to the connection FIFO + // + if ( NULL == pSocket->pFifoTail ) { + // + // First connection + // + pSocket->pFifoHead = pNewSocket; + } + else { + // + // Add to end of list. + // + pSocket->pFifoTail->pNextConnection = pNewSocket; + } + pSocket->pFifoTail = pNewSocket; + pSocket->FifoDepth += 1; + + // + // Update the socket state + // + pNewSocket->State = SOCKET_STATE_IN_FIFO; + + // + // Log the connection + // + DEBUG (( DEBUG_CONNECTION | DEBUG_INFO, + "0x%08x: Socket on port %d.%d.%d.%d:%d connected to %d.%d.%d.%d:%d\r\n", + pNewSocket, + pConfigData->AccessPoint.StationAddress.Addr[0], + pConfigData->AccessPoint.StationAddress.Addr[1], + pConfigData->AccessPoint.StationAddress.Addr[2], + pConfigData->AccessPoint.StationAddress.Addr[3], + pConfigData->AccessPoint.StationPort, + pConfigData->AccessPoint.RemoteAddress.Addr[0], + pConfigData->AccessPoint.RemoteAddress.Addr[1], + pConfigData->AccessPoint.RemoteAddress.Addr[2], + pConfigData->AccessPoint.RemoteAddress.Addr[3], + pConfigData->AccessPoint.RemotePort )); + DEBUG (( DEBUG_CONNECTION | DEBUG_INFO, + "0x%08x: Listen socket adding socket 0x%08x to FIFO, depth: %d\r\n", + pSocket, + pNewSocket, + pSocket->FifoDepth )); + + // + // Start the receive operation + // + EslTcpRxStart4 ( pNewPort ); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_CONNECTION | DEBUG_INFO, + "ERROR - GetModeData failed on port 0x%08x, Status: %r\r\n", + pNewPort, + Status )); + } + } + else { + // + // The listen failed on this port + // + DEBUG (( DEBUG_LISTEN | DEBUG_INFO, + "ERROR - Listen failed on port 0x%08x, Status: %r\r\n", + pPort, + Status )); + + // + // Close the listening port + // + EslTcpPortCloseStart4 ( pPort, TRUE, DEBUG_LISTEN ); + } + } + + // + // Done with the socket if necessary + // + if ( EFI_ERROR ( Status )) { + TempStatus = EslSocketCloseStart ( &pNewSocket->SocketProtocol, + TRUE, + &pSocket->errno ); + ASSERT ( EFI_SUCCESS == TempStatus ); + } + } + } + else { + DEBUG (( DEBUG_CONNECTION, + "0x%08x: Socket FIFO full, connection refused\r\n", + pSocket )); + + // + // The FIFO is full or the socket is in the wrong state + // + Status = EFI_BUFFER_TOO_SMALL; + } + + // + // Close the connection if necessary + // + if (( EFI_ERROR ( Status )) + && ( NULL == TcpPortHandle )) { + // + // TODO: Finish this code path + // The new connection does not fit into the connection FIFO + // + // Process: + // Call close + // Release the resources + + } + + DBG_EXIT ( ); +} + + +/** + Allocate and initialize a DT_PORT structure. + + @param [in] pSocket Address of the socket structure. + @param [in] pService Address of the DT_SERVICE structure. + @param [in] ChildHandle TCP4 child handle + @param [in] pIpAddress Buffer containing IP4 network address of the local host + @param [in] PortNumber Tcp4 port number + @param [in] DebugFlags Flags for debug messages + @param [out] ppPort Buffer to receive new DT_PORT structure address + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslTcpPortAllocate4 ( + IN DT_SOCKET * pSocket, + IN DT_SERVICE * pService, + IN EFI_HANDLE ChildHandle, + IN CONST UINT8 * pIpAddress, + IN UINT16 PortNumber, + IN UINTN DebugFlags, + OUT DT_PORT ** ppPort + ) +{ + UINTN LengthInBytes; + EFI_TCP4_ACCESS_POINT * pAccessPoint; + DT_LAYER * pLayer; + DT_PORT * pPort; + DT_TCP4_CONTEXT * pTcp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Use for/break instead of goto + for ( ; ; ) { + // + // Allocate a port structure + // + pLayer = &mEslLayer; + LengthInBytes = sizeof ( *pPort ); + Status = gBS->AllocatePool ( EfiRuntimeServicesData, + LengthInBytes, + (VOID **)&pPort ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL | DEBUG_INIT, + "ERROR - Failed to allocate the port structure, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + pPort = NULL; + break; + } + DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, + "0x%08x: Allocate pPort, %d bytes\r\n", + pPort, + LengthInBytes )); + + // + // Initialize the port + // + ZeroMem ( pPort, LengthInBytes ); + pPort->Signature = PORT_SIGNATURE; + pPort->pService = pService; + pPort->pSocket = pSocket; + pPort->pfnCloseStart = EslTcpPortCloseStart4; + pPort->DebugFlags = DebugFlags; + + // + // Allocate the receive event + // + pTcp4 = &pPort->Context.Tcp4; + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcpRxComplete4, + pPort, + &pTcp4->RxToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the receive event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_RX | DEBUG_POOL, + "0x%08x: Created receive event\r\n", + pTcp4->RxToken.CompletionToken.Event )); + + // + // Allocate the urgent transmit event + // + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcpTxOobComplete4, + pPort, + &pTcp4->TxOobToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the urgent transmit event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Created urgent transmit event\r\n", + pTcp4->TxOobToken.CompletionToken.Event )); + + // + // Allocate the normal transmit event + // + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcpTxComplete4, + pPort, + &pTcp4->TxToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the normal transmit event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Created normal transmit event\r\n", + pTcp4->TxToken.CompletionToken.Event )); + + // + // Allocate the close event + // + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcpPortCloseComplete4, + pPort, + &pTcp4->CloseToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the close event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Created close event\r\n", + pTcp4->CloseToken.CompletionToken.Event )); + + // + // Allocate the connection event + // + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcpConnectComplete4, + pPort, + &pTcp4->ConnectToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the connect event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Created connect event\r\n", + pTcp4->ConnectToken.CompletionToken.Event )); + + // + // Open the port protocol + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &pTcp4->pProtocol, + pLayer->ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to open gEfiTcp4ProtocolGuid on controller 0x%08x\r\n", + pTcp4->Handle )); + pSocket->errno = EEXIST; + break; + } + DEBUG (( DebugFlags, + "0x%08x: gEfiTcp4ProtocolGuid opened on controller 0x%08x\r\n", + pTcp4->pProtocol, + ChildHandle )); + + // + // Set the port address + // + pTcp4->Handle = ChildHandle; + pAccessPoint = &pPort->Context.Tcp4.ConfigData.AccessPoint; + pAccessPoint->StationPort = PortNumber; + if (( 0 == pIpAddress[0]) + && ( 0 == pIpAddress[1]) + && ( 0 == pIpAddress[2]) + && ( 0 == pIpAddress[3])) { + pAccessPoint->UseDefaultAddress = TRUE; + } + else { + pAccessPoint->StationAddress.Addr[0] = pIpAddress[0]; + pAccessPoint->StationAddress.Addr[1] = pIpAddress[1]; + pAccessPoint->StationAddress.Addr[2] = pIpAddress[2]; + pAccessPoint->StationAddress.Addr[3] = pIpAddress[3]; + pAccessPoint->SubnetMask.Addr[0] = 0xff; + pAccessPoint->SubnetMask.Addr[1] = 0xff; + pAccessPoint->SubnetMask.Addr[2] = 0xff; + pAccessPoint->SubnetMask.Addr[3] = 0xff; + } + pAccessPoint->ActiveFlag = FALSE; + pTcp4->ConfigData.TimeToLive = 255; + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Add this port to the socket + // + pPort->pLinkSocket = pSocket->pPortList; + pSocket->pPortList = pPort; + DEBUG (( DebugFlags, + "0x%08x: Socket adding port: 0x%08x\r\n", + pSocket, + pPort )); + + // + // Add this port to the service + // + pPort->pLinkService = pService->pPortList; + pService->pPortList = pPort; + + // + // Return the port + // + *ppPort = pPort; + break; + } + + // + // Clean up after the error if necessary + // + if (( EFI_ERROR ( Status )) && ( NULL != pPort )) { + // + // Close the port + // + EslTcpPortClose4 ( pPort ); + } + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Close a TCP4 port. + + This routine releases the resources allocated by + ::TcpPortAllocate4(). + + @param [in] pPort Address of the port structure. + + @retval EFI_SUCCESS The port is closed + @retval other Port close error + +**/ +EFI_STATUS +EslTcpPortClose4 ( + IN DT_PORT * pPort + ) +{ + UINTN DebugFlags; + DT_LAYER * pLayer; + DT_PACKET * pPacket; + DT_PORT * pPreviousPort; + DT_SERVICE * pService; + DT_SOCKET * pSocket; + EFI_SERVICE_BINDING_PROTOCOL * pTcp4Service; + DT_TCP4_CONTEXT * pTcp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Locate the port in the socket list + // + Status = EFI_SUCCESS; + pLayer = &mEslLayer; + DebugFlags = pPort->DebugFlags; + pSocket = pPort->pSocket; + pPreviousPort = pSocket->pPortList; + if ( pPreviousPort == pPort ) { + // + // Remove this port from the head of the socket list + // + pSocket->pPortList = pPort->pLinkSocket; + } + else { + // + // Locate the port in the middle of the socket list + // + while (( NULL != pPreviousPort ) + && ( pPreviousPort->pLinkSocket != pPort )) { + pPreviousPort = pPreviousPort->pLinkSocket; + } + if ( NULL != pPreviousPort ) { + // + // Remove the port from the middle of the socket list + // + pPreviousPort->pLinkSocket = pPort->pLinkSocket; + } + } + + // + // Locate the port in the service list + // + pService = pPort->pService; + pPreviousPort = pService->pPortList; + if ( pPreviousPort == pPort ) { + // + // Remove this port from the head of the service list + // + pService->pPortList = pPort->pLinkService; + } + else { + // + // Locate the port in the middle of the service list + // + while (( NULL != pPreviousPort ) + && ( pPreviousPort->pLinkService != pPort )) { + pPreviousPort = pPreviousPort->pLinkService; + } + if ( NULL != pPreviousPort ) { + // + // Remove the port from the middle of the service list + // + pPreviousPort->pLinkService = pPort->pLinkService; + } + } + + // + // Empty the urgent receive queue + // + pTcp4 = &pPort->Context.Tcp4; + while ( NULL != pSocket->pRxOobPacketListHead ) { + pPacket = pSocket->pRxOobPacketListHead; + pSocket->pRxOobPacketListHead = pPacket->pNext; + pSocket->RxOobBytes -= pPacket->Op.Tcp4Rx.ValidBytes; + EslSocketPacketFree ( pPacket, DEBUG_RX ); + } + pSocket->pRxOobPacketListTail = NULL; + ASSERT ( 0 == pSocket->RxOobBytes ); + + // + // Empty the receive queue + // + while ( NULL != pSocket->pRxPacketListHead ) { + pPacket = pSocket->pRxPacketListHead; + pSocket->pRxPacketListHead = pPacket->pNext; + pSocket->RxBytes -= pPacket->Op.Tcp4Rx.ValidBytes; + EslSocketPacketFree ( pPacket, DEBUG_RX ); + } + pSocket->pRxPacketListTail = NULL; + ASSERT ( 0 == pSocket->RxBytes ); + + // + // Empty the receive free queue + // + while ( NULL != pSocket->pRxFree ) { + pPacket = pSocket->pRxFree; + pSocket->pRxFree = pPacket->pNext; + EslSocketPacketFree ( pPacket, DEBUG_RX ); + } + + // + // Done with the connect event + // + if ( NULL != pTcp4->ConnectToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp4->ConnectToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed connect event\r\n", + pTcp4->ConnectToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the connect event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the close event + // + if ( NULL != pTcp4->CloseToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp4->CloseToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed close event\r\n", + pTcp4->CloseToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the close event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the listen completion event + // + if ( NULL != pTcp4->ListenToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp4->ListenToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed listen completion event\r\n", + pTcp4->ListenToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the listen completion event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the receive event + // + if ( NULL != pTcp4->RxToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp4->RxToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed receive event\r\n", + pTcp4->RxToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the receive event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the normal transmit event + // + if ( NULL != pTcp4->TxToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp4->TxToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed normal transmit event\r\n", + pTcp4->TxToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the normal transmit event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the urgent transmit event + // + if ( NULL != pTcp4->TxOobToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp4->TxOobToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed urgent transmit event\r\n", + pTcp4->TxOobToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the urgent transmit event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the TCP protocol + // + pTcp4Service = pService->pInterface; + if ( NULL != pTcp4->pProtocol ) { + Status = gBS->CloseProtocol ( pTcp4->Handle, + &gEfiTcp4ProtocolGuid, + pLayer->ImageHandle, + NULL ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags, + "0x%08x: gEfiTcp4ProtocolGuid closed on controller 0x%08x\r\n", + pTcp4->pProtocol, + pTcp4->Handle )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close gEfiTcp4ProtocolGuid opened on controller 0x%08x, Status: %r\r\n", + pTcp4->Handle, + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the TCP port + // + if ( NULL != pTcp4->Handle ) { + Status = pTcp4Service->DestroyChild ( pTcp4Service, + pTcp4->Handle ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Tcp4 port handle destroyed\r\n", + pTcp4->Handle )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL, + "ERROR - Failed to destroy the Tcp4 port handle, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Release the port structure + // + Status = gBS->FreePool ( pPort ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Free pPort, %d bytes\r\n", + pPort, + sizeof ( *pPort ))); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL, + "ERROR - Failed to free pPort: 0x%08x, Status: %r\r\n", + pPort, + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the port close completion + + @param Event The close completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslTcpPortCloseComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Update the port state + // + pPort->State = PORT_STATE_CLOSE_DONE; + + // + // Release the resources once the receive operation completes + // + Status = EslTcpPortCloseRxDone4 ( pPort ); + DBG_EXIT_STATUS ( Status ); +} + + +/** + Start the close operation on a TCP4 port, state 1. + + Closing a port goes through the following states: + 1. Port close starting - Mark the port as closing and wait for transmission to complete + 2. Port TX close done - Transmissions complete, close the port and abort the receives + 3. Port RX close done - Receive operations complete, close the port + 4. Port closed - Release the port resources + + @param [in] pPort Address of the port structure. + @param [in] bCloseNow Set TRUE to abort active transfers + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS The port is closed, not normally returned + @retval EFI_NOT_READY The port has started the closing process + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslTcpPortCloseStart4 ( + IN DT_PORT * pPort, + IN BOOLEAN bCloseNow, + IN UINTN DebugFlags + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Mark the port as closing + // + Status = EFI_ALREADY_STARTED; + pSocket = pPort->pSocket; + pSocket->errno = EALREADY; + if ( PORT_STATE_CLOSE_STARTED > pPort->State ) { + + // + // Update the port state + // + pPort->State = PORT_STATE_CLOSE_STARTED; + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close State: PORT_STATE_CLOSE_STARTED\r\n", + pPort )); + pPort->bCloseNow = bCloseNow; + pPort->DebugFlags = DebugFlags; + + // + // Determine if transmits are complete + // + Status = EslTcpPortCloseTxDone4 ( pPort ); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Port close state 3 + + Continue the close operation after the receive is complete. + + @param [in] pPort Address of the port structure. + + @retval EFI_SUCCESS The port is closed + @retval EFI_NOT_READY The port is still closing + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslTcpPortCloseRxDone4 ( + IN DT_PORT * pPort + ) +{ + PORT_STATE PortState; + DT_TCP4_CONTEXT * pTcp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Verify that the port is closing + // + Status = EFI_ALREADY_STARTED; + PortState = pPort->State; + if (( PORT_STATE_CLOSE_TX_DONE == PortState ) + || ( PORT_STATE_CLOSE_DONE == PortState )) { + // + // Determine if the receive operation is pending + // + Status = EFI_NOT_READY; + pTcp4 = &pPort->Context.Tcp4; + if ( NULL == pTcp4->pReceivePending ) { + // + // The receive operation is complete + // Update the port state + // + pPort->State = PORT_STATE_CLOSE_RX_DONE; + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close State: PORT_STATE_CLOSE_RX_DONE\r\n", + pPort )); + + // + // Determine if the close operation has completed + // + if ( PORT_STATE_CLOSE_DONE == PortState ) { + // + // The close operation has completed + // Release the port resources + // + Status = EslTcpPortClose4 ( pPort ); + } + else + { + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close: Close operation still pending!\r\n", + pPort )); + } + } + else { + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close: Receive still pending!\r\n", + pPort )); + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Port close state 2 + + Continue the close operation after the transmission is complete. + + @param [in] pPort Address of the port structure. + + @retval EFI_SUCCESS The port is closed, not normally returned + @retval EFI_NOT_READY The port is still closing + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslTcpPortCloseTxDone4 ( + IN DT_PORT * pPort + ) +{ + DT_SOCKET * pSocket; + DT_TCP4_CONTEXT * pTcp4; + EFI_TCP4_PROTOCOL * pTcp4Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // All transmissions are complete or must be stopped + // Mark the port as TX complete + // + Status = EFI_ALREADY_STARTED; + if ( PORT_STATE_CLOSE_STARTED == pPort->State ) { + // + // Verify that the transmissions are complete + // + pSocket = pPort->pSocket; + if ( pPort->bCloseNow + || ( EFI_SUCCESS != pSocket->TxError ) + || (( 0 == pSocket->TxOobBytes ) + && ( 0 == pSocket->TxBytes ))) { + // + // Start the close operation on the port + // + pTcp4 = &pPort->Context.Tcp4; + pTcp4->CloseToken.AbortOnClose = FALSE; + pTcp4Protocol = pTcp4->pProtocol; + if ( !pTcp4->bConfigured ) { + // + // Skip the close operation since the port is not + // configured + // + // Update the port state + // + pPort->State = PORT_STATE_CLOSE_DONE; + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close State: PORT_STATE_CLOSE_DONE\r\n", + pPort )); + Status = EFI_SUCCESS; + } + else { + // + // Close the configured port + // + Status = pTcp4Protocol->Close ( pTcp4Protocol, + &pTcp4->CloseToken ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port close started\r\n", + pPort )); + + // + // Update the port state + // + pPort->State = PORT_STATE_CLOSE_TX_DONE; + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close State: PORT_STATE_CLOSE_TX_DONE\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_ERROR | pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "ERROR - Close failed on port 0x%08x, Status: %r\r\n", + pPort, + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Determine if the receive operation is pending + // + if ( !EFI_ERROR ( Status )) { + Status = EslTcpPortCloseRxDone4 ( pPort ); + } + } + else { + // + // Transmissions are still active, exit + // + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close: Transmits are still pending!\r\n", + pPort )); + Status = EFI_NOT_READY; + pSocket->errno = EAGAIN; + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Receive data from a network connection. + + + @param [in] pSocket Address of a DT_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [out] pAddress Network address to receive the remote system address + + @param [in,out] pAddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Socket data successfully received + + **/ +EFI_STATUS +EslTcpReceive4 ( + IN DT_SOCKET * pSocket, + IN INT32 Flags, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ) +{ + socklen_t AddressLength; + size_t BytesToCopy; + in_addr_t IpAddress; + size_t LengthInBytes; + DT_PACKET * pPacket; + DT_PORT * pPort; + DT_PACKET ** ppQueueHead; + DT_PACKET ** ppQueueTail; + struct sockaddr_in * pRemoteAddress; + size_t * pRxDataBytes; + DT_TCP4_CONTEXT * pTcp4; + struct sockaddr_in RemoteAddress; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume failure + // + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTCONN; + + // + // Verify that the socket is connected + // + if (( SOCKET_STATE_CONNECTED == pSocket->State ) + || ( PORT_STATE_RX_ERROR == pSocket->State )) { + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine the queue head + // + pTcp4 = &pPort->Context.Tcp4; + if ( 0 != ( Flags & MSG_OOB )) { + ppQueueHead = &pSocket->pRxOobPacketListHead; + ppQueueTail = &pSocket->pRxOobPacketListTail; + pRxDataBytes = &pSocket->RxOobBytes; + } + else { + ppQueueHead = &pSocket->pRxPacketListHead; + ppQueueTail = &pSocket->pRxPacketListTail; + pRxDataBytes = &pSocket->RxBytes; + } + + // + // Determine if there is any data on the queue + // + pPacket = *ppQueueHead; + if ( NULL != pPacket ) { + // + // Validate the return address parameters + // + if (( NULL == pAddress ) || ( NULL != pAddressLength )) { + // + // Return the remote system address if requested + // + if ( NULL != pAddress ) { + // + // Build the remote address + // + ZeroMem ( &RemoteAddress, sizeof ( RemoteAddress )); + RemoteAddress.sin_len = sizeof ( RemoteAddress ); + RemoteAddress.sin_family = AF_INET; + IpAddress = pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3]; + IpAddress <<= 8; + IpAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2]; + IpAddress <<= 8; + IpAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1]; + IpAddress <<= 8; + IpAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0]; + RemoteAddress.sin_addr.s_addr = IpAddress; + RemoteAddress.sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.RemotePort ); + + // + // Copy the address + // + pRemoteAddress = (struct sockaddr_in *)pAddress; + AddressLength = sizeof ( *pRemoteAddress ); + if ( AddressLength > *pAddressLength ) { + AddressLength = *pAddressLength; + } + CopyMem ( pRemoteAddress, + &RemoteAddress, + AddressLength ); + + // + // Update the address length + // + *pAddressLength = AddressLength; + } + + // + // Copy the received data + // + LengthInBytes = 0; + do { + // + // Determine the amount of received data + // + BytesToCopy = pPacket->Op.Tcp4Rx.ValidBytes; + if (( BufferLength - LengthInBytes ) < BytesToCopy ) { + BytesToCopy = BufferLength - LengthInBytes; + } + LengthInBytes += BytesToCopy; + + // + // Move the data into the buffer + // + DEBUG (( DEBUG_RX, + "0x%08x: Port copy packet 0x%08x data into 0x%08x, 0x%08x bytes\r\n", + pPort, + pPacket, + pBuffer, + BytesToCopy )); + CopyMem ( pBuffer, pPacket->Op.Tcp4Rx.pBuffer, BytesToCopy ); + + // + // Determine if the data is being read + // + if ( 0 == ( Flags & MSG_PEEK )) { + // + // Account for the bytes consumed + // + pPacket->Op.Tcp4Rx.pBuffer += BytesToCopy; + pPacket->Op.Tcp4Rx.ValidBytes -= BytesToCopy; + *pRxDataBytes -= BytesToCopy; + DEBUG (( DEBUG_RX, + "0x%08x: Port account for 0x%08x bytes\r\n", + pPort, + BytesToCopy )); + + // + // Determine if the entire packet was consumed + // + if (( 0 == pPacket->Op.Tcp4Rx.ValidBytes ) + || ( SOCK_STREAM != pSocket->Type )) { + // + // All done with this packet + // Account for any discarded data + // + *pRxDataBytes -= pPacket->Op.Tcp4Rx.ValidBytes; + if ( 0 != pPacket->Op.Tcp4Rx.ValidBytes ) { + DEBUG (( DEBUG_RX, + "0x%08x: Port, packet read, skipping over 0x%08x bytes\r\n", + pPort, + pPacket->Op.Tcp4Rx.ValidBytes )); + } + + // + // Remove this packet from the queue + // + *ppQueueHead = pPacket->pNext; + if ( NULL == *ppQueueHead ) { + *ppQueueTail = NULL; + } + + // + // Move the packet to the free queue + // + pPacket->pNext = pSocket->pRxFree; + pSocket->pRxFree = pPacket; + DEBUG (( DEBUG_RX, + "0x%08x: Port freeing packet 0x%08x\r\n", + pPort, + pPacket )); + + // + // Restart this receive operation if necessary + // + if (( NULL == pTcp4->pReceivePending ) + && ( MAX_RX_DATA > pSocket->RxBytes )) { + EslTcpRxStart4 ( pPort ); + } + } + } + + // + // Get the next packet + // + pPacket = *ppQueueHead; + } while (( SOCK_STREAM == pSocket->Type ) + && ( NULL != pPacket ) + && ( 0 == ( Flags & MSG_PEEK )) + && ( BufferLength > LengthInBytes )); + + // + // Return the data length + // + *pDataLength = LengthInBytes; + + // + // Successful operation + // + Status = EFI_SUCCESS; + pSocket->errno = 0; + } + else { + // + // Bad return address pointer and length + // + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + } + } + else { + // + // The queue is empty + // Determine if it is time to return the receive error + // + if ( EFI_ERROR ( pSocket->RxError ) + && ( NULL == pSocket->pRxPacketListHead ) + && ( NULL == pSocket->pRxOobPacketListHead )) { + Status = pSocket->RxError; + switch ( Status ) { + default: + pSocket->errno = EIO; + break; + + case EFI_CONNECTION_FIN: + pSocket->errno = ESHUTDOWN; + break; + + case EFI_CONNECTION_REFUSED: + pSocket->errno = ECONNREFUSED; + break; + + case EFI_CONNECTION_RESET: + pSocket->errno = ECONNRESET; + break; + + case EFI_HOST_UNREACHABLE: + pSocket->errno = EHOSTUNREACH; + break; + + case EFI_NETWORK_UNREACHABLE: + pSocket->errno = ENETUNREACH; + break; + + case EFI_PORT_UNREACHABLE: + pSocket->errno = EPROTONOSUPPORT; + break; + + case EFI_PROTOCOL_UNREACHABLE: + pSocket->errno = ENOPROTOOPT; + break; + } + pSocket->RxError = EFI_SUCCESS; + } + else { + Status = EFI_NOT_READY; + pSocket->errno = EAGAIN; + } + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Cancel the receive operations + + @param [in] pSocket Address of a DT_SOCKET structure + + @retval EFI_SUCCESS - The cancel was successful + + **/ +EFI_STATUS +EslTcpRxCancel4 ( + IN DT_SOCKET * pSocket + ) +{ + DT_PACKET * pPacket; + DT_PORT * pPort; + DT_TCP4_CONTEXT * pTcp4; + EFI_TCP4_PROTOCOL * pTcp4Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume failure + // + Status = EFI_NOT_FOUND; + + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine if a receive is pending + // + pTcp4 = &pPort->Context.Tcp4; + pPacket = pTcp4->pReceivePending; + if ( NULL != pPacket ) { + // + // Attempt to cancel the receive operation + // + pTcp4Protocol = pTcp4->pProtocol; + Status = pTcp4Protocol->Cancel ( pTcp4Protocol, + &pTcp4->RxToken.CompletionToken ); + if ( EFI_NOT_FOUND == Status ) { + // + // The receive is complete + // + Status = EFI_SUCCESS; + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the receive completion + + Buffer the data that was just received. + + @param Event The receive completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslTcpRxComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ) +{ + BOOLEAN bUrgent; + size_t LengthInBytes; + DT_PACKET * pPacket; + DT_PACKET * pPrevious; + DT_SOCKET * pSocket; + DT_TCP4_CONTEXT * pTcp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Mark this receive complete + // + pTcp4 = &pPort->Context.Tcp4; + pPacket = pTcp4->pReceivePending; + pTcp4->pReceivePending = NULL; + + // + // Determine if this receive was successful + // + pSocket = pPort->pSocket; + Status = pTcp4->RxToken.CompletionToken.Status; + if (( !EFI_ERROR ( Status )) && ( !pSocket->bRxDisable )) { + // + // Set the buffer size and address + // + pPacket->Op.Tcp4Rx.pBuffer = pPacket->Op.Tcp4Rx.RxData.FragmentTable[0].FragmentBuffer; + LengthInBytes = pPacket->Op.Tcp4Rx.RxData.DataLength; + pPacket->Op.Tcp4Rx.ValidBytes = LengthInBytes; + pPacket->pNext = NULL; + + // + // Queue this packet + // + bUrgent = pPacket->Op.Tcp4Rx.RxData.UrgentFlag; + if ( bUrgent ) { + // + // Add packet to the urgent list + // + pPrevious = pSocket->pRxOobPacketListTail; + if ( NULL == pPrevious ) { + pSocket->pRxOobPacketListHead = pPacket; + } + else { + pPrevious->pNext = pPacket; + } + pSocket->pRxOobPacketListTail = pPacket; + + // + // Account for the urgent data + // + pSocket->RxOobBytes += LengthInBytes; + } + else { + // + // Add packet to the normal list + // + pPrevious = pSocket->pRxPacketListTail; + if ( NULL == pPrevious ) { + pSocket->pRxPacketListHead = pPacket; + } + else { + pPrevious->pNext = pPacket; + } + pSocket->pRxPacketListTail = pPacket; + + // + // Account for the normal data + // + pSocket->RxBytes += LengthInBytes; + } + + // + // Log the received data + // + DEBUG (( DEBUG_RX | DEBUG_INFO, + "0x%08x: Packet queued on port 0x%08x with 0x%08x bytes of %s data\r\n", + pPacket, + pPort, + LengthInBytes, + bUrgent ? L"urgent" : L"normal" )); + + // + // Attempt to restart this receive operation + // + if ( pSocket->MaxRxBuf > pSocket->RxBytes ) { + EslTcpRxStart4 ( pPort ); + } + else { + DEBUG (( DEBUG_RX, + "0x%08x: Port RX suspended, 0x%08x bytes queued\r\n", + pPort, + pSocket->RxBytes )); + } + } + else + { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "ERROR - Receiving packet 0x%08x, on port 0x%08x, Status:%r\r\n", + pPacket, + pPort, + Status )); + + // + // Receive error, free the packet save the error + // + EslSocketPacketFree ( pPacket, DEBUG_RX ); + if ( !EFI_ERROR ( pSocket->RxError )) { + pSocket->RxError = Status; + } + + // + // Update the port state + // + if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { + EslTcpPortCloseRxDone4 ( pPort ); + } + else { + if ( EFI_ERROR ( Status )) { + pPort->State = PORT_STATE_RX_ERROR; + } + } + } + + DBG_EXIT ( ); +} + + +/** + Start a receive operation + + @param [in] pPort Address of the DT_PORT structure. + + **/ +VOID +EslTcpRxStart4 ( + IN DT_PORT * pPort + ) +{ + size_t LengthInBytes; + DT_PACKET * pPacket; + DT_SOCKET * pSocket; + DT_TCP4_CONTEXT * pTcp4; + EFI_TCP4_PROTOCOL * pTcp4Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if a receive is already pending + // + Status = EFI_SUCCESS; + pPacket = NULL; + pSocket = pPort->pSocket; + pTcp4 = &pPort->Context.Tcp4; + if ( !EFI_ERROR ( pPort->pSocket->RxError )) { + if (( NULL == pTcp4->pReceivePending ) + && ( PORT_STATE_CLOSE_STARTED > pPort->State )) { + // + // Determine if there are any free packets + // + pPacket = pSocket->pRxFree; + LengthInBytes = sizeof ( pPacket->Op.Tcp4Rx.Buffer ); + if ( NULL != pPacket ) { + // + // Remove this packet from the free list + // + pSocket->pRxFree = pPacket->pNext; + DEBUG (( DEBUG_RX, + "0x%08x: Port removed packet 0x%08x from free list\r\n", + pPort, + pPacket )); + } + else { + // + // Allocate a packet structure + // + Status = EslSocketPacketAllocate ( &pPacket, + sizeof ( pPacket->Op.Tcp4Rx ), + DEBUG_RX ); + if ( EFI_ERROR ( Status )) { + pPacket = NULL; + DEBUG (( DEBUG_ERROR | DEBUG_RX, + "0x%08x: Port failed to allocate RX packet, Status: %r\r\n", + pPort, + Status )); + } + } + + // + // Determine if a packet is available + // + if ( NULL != pPacket ) { + // + // Initialize the buffer for receive + // + pTcp4->RxToken.Packet.RxData = &pPacket->Op.Tcp4Rx.RxData; + pPacket->Op.Tcp4Rx.RxData.DataLength = (UINT32) LengthInBytes; + pPacket->Op.Tcp4Rx.RxData.FragmentCount = 1; + pPacket->Op.Tcp4Rx.RxData.FragmentTable [0].FragmentLength = (UINT32) LengthInBytes; + pPacket->Op.Tcp4Rx.RxData.FragmentTable [0].FragmentBuffer = &pPacket->Op.Tcp4Rx.Buffer [0]; + pTcp4->pReceivePending = pPacket; + + // + // Start the receive on the packet + // + pTcp4Protocol = pTcp4->pProtocol; + Status = pTcp4Protocol->Receive ( pTcp4Protocol, + &pTcp4->RxToken ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "0x%08x: Packet receive pending on port 0x%08x\r\n", + pPacket, + pPort )); + } + else { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "ERROR - Failed to post a receive on port 0x%08x, Status: %r\r\n", + pPort, + Status )); + pTcp4->pReceivePending = NULL; + if ( !EFI_ERROR ( pSocket->RxError )) { + // + // Save the error status + // + pSocket->RxError = Status; + } + } + } + } + } + + DBG_EXIT ( ); +} + + +/** + Shutdown the TCP4 service. + + This routine undoes the work performed by ::TcpInitialize4. + + @param [in] pService DT_SERVICE structure address + +**/ +VOID +EFIAPI +EslTcpShutdown4 ( + IN DT_SERVICE * pService + ) +{ + DT_LAYER * pLayer; + DT_PORT * pPort; + DT_SERVICE * pPreviousService; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Walk the list of ports + // + do { + pPort = pService->pPortList; + if ( NULL != pPort ) { + // + // Remove the port from the port list + // + pService->pPortList = pPort->pLinkService; + + // + // Close the port + // TODO: Fix this + // +// pPort->pfnClosePort ( pPort, DEBUG_LISTEN | DEBUG_CONNECTION ); + } + } while ( NULL != pPort ); + + // + // Remove the service from the service list + // + pLayer = &mEslLayer; + pPreviousService = pLayer->pTcp4List; + if ( pService == pPreviousService ) { + // + // Remove the service from the beginning of the list + // + pLayer->pTcp4List = pService->pNext; + } + else { + // + // Remove the service from the middle of the list + // + while ( NULL != pPreviousService ) { + if ( pService == pPreviousService->pNext ) { + pPreviousService->pNext = pService->pNext; + break; + } + } + } + + DBG_EXIT ( ); +} + + +/** + Determine if the socket is configured. + + + @param [in] pSocket Address of a DT_SOCKET structure + + @retval EFI_SUCCESS - The port is connected + @retval EFI_NOT_STARTED - The port is not connected + + **/ + EFI_STATUS + EslTcpSocketIsConfigured4 ( + IN DT_SOCKET * pSocket + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine the socket configuration status + // + Status = pSocket->bConfigured ? EFI_SUCCESS : EFI_NOT_STARTED; + + // + // Return the port connected state. + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Buffer data for transmission over a network connection. + + This routine is called by the socket layer API to buffer + data for transmission. When necessary, this routine will + start the transmit engine that performs the data transmission + on the network connection. + + The transmit engine uses two queues, one for urgent (out-of-band) + data and the other for normal data. The urgent data is provided + to TCP as soon as it is available, allowing the TCP layer to + schedule transmission of the urgent data between packets of normal + data. + + Transmission errors are returned during the next transmission or + during the close operation. Only buffering errors are returned + during the current transmission attempt. + + @param [in] pSocket Address of a DT_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @retval EFI_SUCCESS - Socket data successfully buffered + + **/ +EFI_STATUS +EslTcpTxBuffer4 ( + IN DT_SOCKET * pSocket, + IN int Flags, + IN size_t BufferLength, + IN CONST UINT8 * pBuffer, + OUT size_t * pDataLength + ) +{ + BOOLEAN bUrgent; + DT_PACKET * pPacket; + DT_PACKET * pPreviousPacket; + DT_PACKET ** ppPacket; + DT_PACKET ** ppQueueHead; + DT_PACKET ** ppQueueTail; + DT_PORT * pPort; + DT_TCP4_CONTEXT * pTcp4; + EFI_TCP4_IO_TOKEN * pToken; + size_t * pTxBytes; + EFI_TCP4_TRANSMIT_DATA * pTxData; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume failure + // + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTCONN; + * pDataLength = 0; + + // + // Verify that the socket is connected + // + if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine the queue head + // + pTcp4 = &pPort->Context.Tcp4; + bUrgent = (BOOLEAN)( 0 != ( Flags & MSG_OOB )); + if ( bUrgent ) { + ppQueueHead = &pSocket->pTxOobPacketListHead; + ppQueueTail = &pSocket->pTxOobPacketListTail; + ppPacket = &pTcp4->pTxOobPacket; + pToken = &pTcp4->TxOobToken; + pTxBytes = &pSocket->TxOobBytes; + } + else { + ppQueueHead = &pSocket->pTxPacketListHead; + ppQueueTail = &pSocket->pTxPacketListTail; + ppPacket = &pTcp4->pTxPacket; + pToken = &pTcp4->TxToken; + pTxBytes = &pSocket->TxBytes; + } + + // + // Verify that there is enough room to buffer another + // transmit operation + // + if ( pSocket->MaxTxBuf > *pTxBytes ) { + // + // Attempt to allocate the packet + // + Status = EslSocketPacketAllocate ( &pPacket, + sizeof ( pPacket->Op.Tcp4Tx ) + - sizeof ( pPacket->Op.Tcp4Tx.Buffer ) + + BufferLength, + DEBUG_TX ); + if ( !EFI_ERROR ( Status )) { + // + // Initialize the transmit operation + // + pTxData = &pPacket->Op.Tcp4Tx.TxData; + pTxData->Push = TRUE; + pTxData->Urgent = bUrgent; + pTxData->DataLength = (UINT32) BufferLength; + pTxData->FragmentCount = 1; + pTxData->FragmentTable[0].FragmentLength = (UINT32) BufferLength; + pTxData->FragmentTable[0].FragmentBuffer = &pPacket->Op.Tcp4Tx.Buffer[0]; + + // + // Copy the data into the buffer + // + CopyMem ( &pPacket->Op.Tcp4Tx.Buffer[0], + pBuffer, + BufferLength ); + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Stop transmission after an error + // + if ( !EFI_ERROR ( pSocket->TxError )) { + // + // Display the request + // + DEBUG (( DEBUG_TX, + "Send %d %s bytes from 0x%08x\r\n", + BufferLength, + bUrgent ? L"urgent" : L"normal", + pBuffer )); + + // + // Queue the data for transmission + // + pPacket->pNext = NULL; + pPreviousPacket = *ppQueueTail; + if ( NULL == pPreviousPacket ) { + *ppQueueHead = pPacket; + } + else { + pPreviousPacket->pNext = pPacket; + } + *ppQueueTail = pPacket; + DEBUG (( DEBUG_TX, + "0x%08x: Packet on %s transmit list\r\n", + pPacket, + bUrgent ? L"urgent" : L"normal" )); + + // + // Account for the buffered data + // + *pTxBytes += BufferLength; + *pDataLength = BufferLength; + + // + // Start the transmit engine if it is idle + // + if ( NULL == *ppPacket ) { + EslTcpTxStart4 ( pSocket->pPortList, + pToken, + ppQueueHead, + ppQueueTail, + ppPacket ); + } + } + else { + // + // Previous transmit error + // Stop transmission + // + Status = pSocket->TxError; + pSocket->errno = EIO; + + // + // Free the packet + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + // + // Packet allocation failed + // + pSocket->errno = ENOMEM; + } + } + else { + // + // Not enough buffer space available + // + pSocket->errno = EAGAIN; + Status = EFI_NOT_READY; + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the normal data transmit completion + + @param Event The normal transmit completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslTcpTxComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ) +{ + UINT32 LengthInBytes; + DT_PACKET * pCurrentPacket; + DT_PACKET * pNextPacket; + DT_PACKET * pPacket; + DT_SOCKET * pSocket; + DT_TCP4_CONTEXT * pTcp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the active transmit packet + // + pSocket = pPort->pSocket; + pTcp4 = &pPort->Context.Tcp4; + pPacket = pTcp4->pTxPacket; + + // + // Mark this packet as complete + // + pTcp4->pTxPacket = NULL; + LengthInBytes = pPacket->Op.Tcp4Tx.TxData.DataLength; + pSocket->TxBytes -= LengthInBytes; + + // + // Save any transmit error + // + Status = pTcp4->TxToken.CompletionToken.Status; + if ( EFI_ERROR ( Status )) { + if ( !EFI_ERROR ( pSocket->TxError )) { + pSocket->TxError = Status; + } + DEBUG (( DEBUG_TX | DEBUG_INFO, + "ERROR - Transmit failure for packet 0x%08x, Status: %r\r\n", + pPacket, + Status )); + + // + // Empty the normal transmit list + // + pCurrentPacket = pPacket; + pNextPacket = pSocket->pTxPacketListHead; + while ( NULL != pNextPacket ) { + pPacket = pNextPacket; + pNextPacket = pPacket->pNext; + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + pSocket->pTxPacketListHead = NULL; + pSocket->pTxPacketListTail = NULL; + pPacket = pCurrentPacket; + } + else + { + DEBUG (( DEBUG_TX | DEBUG_INFO, + "0x%08x: Packet transmitted %d bytes successfully\r\n", + pPacket, + LengthInBytes )); + + // + // Verify the transmit engine is still running + // + if ( !pPort->bCloseNow ) { + // + // Start the next packet transmission + // + EslTcpTxStart4 ( pPort, + &pTcp4->TxToken, + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pTcp4->pTxPacket ); + } + } + + // + // Release this packet + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + + // + // Finish the close operation if necessary + // + if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { + // + // Indicate that the transmit is complete + // + EslTcpPortCloseTxDone4 ( pPort ); + } + DBG_EXIT ( ); +} + + +/** + Process the urgent data transmit completion + + @param Event The urgent transmit completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslTcpTxOobComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ) +{ + UINT32 LengthInBytes; + DT_PACKET * pCurrentPacket; + DT_PACKET * pNextPacket; + DT_PACKET * pPacket; + DT_SOCKET * pSocket; + DT_TCP4_CONTEXT * pTcp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the active transmit packet + // + pSocket = pPort->pSocket; + pTcp4 = &pPort->Context.Tcp4; + pPacket = pTcp4->pTxOobPacket; + + // + // Mark this packet as complete + // + pTcp4->pTxOobPacket = NULL; + LengthInBytes = pPacket->Op.Tcp4Tx.TxData.DataLength; + pSocket->TxOobBytes -= LengthInBytes; + + // + // Save any transmit error + // + Status = pTcp4->TxOobToken.CompletionToken.Status; + if ( EFI_ERROR ( Status )) { + if ( !EFI_ERROR ( Status )) { + pSocket->TxError = Status; + } + DEBUG (( DEBUG_TX | DEBUG_INFO, + "ERROR - Transmit failure for urgent packet 0x%08x, Status: %r\r\n", + pPacket, + Status )); + + + // + // Empty the OOB transmit list + // + pCurrentPacket = pPacket; + pNextPacket = pSocket->pTxOobPacketListHead; + while ( NULL != pNextPacket ) { + pPacket = pNextPacket; + pNextPacket = pPacket->pNext; + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + pSocket->pTxOobPacketListHead = NULL; + pSocket->pTxOobPacketListTail = NULL; + pPacket = pCurrentPacket; + } + else + { + DEBUG (( DEBUG_TX | DEBUG_INFO, + "0x%08x: Urgent packet transmitted %d bytes successfully\r\n", + pPacket, + LengthInBytes )); + + // + // Verify the transmit engine is still running + // + if ( !pPort->bCloseNow ) { + // + // Start the next packet transmission + // + EslTcpTxStart4 ( pPort, + &pTcp4->TxOobToken, + &pSocket->pTxOobPacketListHead, + &pSocket->pTxOobPacketListTail, + &pTcp4->pTxOobPacket ); + } + } + + // + // Release this packet + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + + // + // Finish the close operation if necessary + // + if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { + // + // Indicate that the transmit is complete + // + EslTcpPortCloseTxDone4 ( pPort ); + } + DBG_EXIT ( ); +} + + +/** + Transmit data using a network connection. + + + @param [in] pPort Address of a DT_PORT structure + @param [in] pToken Address of either the OOB or normal transmit token + @param [in] ppQueueHead Transmit queue head address + @param [in] ppQueueTail Transmit queue tail address + @param [in] ppPacket Active transmit packet address + + **/ +VOID +EslTcpTxStart4 ( + IN DT_PORT * pPort, + IN EFI_TCP4_IO_TOKEN * pToken, + IN DT_PACKET ** ppQueueHead, + IN DT_PACKET ** ppQueueTail, + IN DT_PACKET ** ppPacket + ) +{ + DT_PACKET * pNextPacket; + DT_PACKET * pPacket; + DT_SOCKET * pSocket; + EFI_TCP4_PROTOCOL * pTcp4Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Get the packet from the queue head + // + pPacket = *ppQueueHead; + if ( NULL != pPacket ) { + // + // Remove the packet from the queue + // + pNextPacket = pPacket->pNext; + *ppQueueHead = pNextPacket; + if ( NULL == pNextPacket ) { + *ppQueueTail = NULL; + } + + // + // Set the packet as active + // + *ppPacket = pPacket; + + // + // Start the transmit operation + // + pTcp4Protocol = pPort->Context.Tcp4.pProtocol; + pToken->Packet.TxData = &pPacket->Op.Tcp4Tx.TxData; + Status = pTcp4Protocol->Transmit ( pTcp4Protocol, pToken ); + if ( EFI_ERROR ( Status )) { + pSocket = pPort->pSocket; + if ( EFI_SUCCESS == pSocket->TxError ) { + pSocket->TxError = Status; + } + } + } + + DBG_EXIT ( ); +} diff --git a/StdLib/EfiSocketLib/Udp4.c b/StdLib/EfiSocketLib/Udp4.c new file mode 100644 index 0000000000..95fc66517a --- /dev/null +++ b/StdLib/EfiSocketLib/Udp4.c @@ -0,0 +1,2408 @@ +/** @file + Implement the UDP4 driver support for the socket layer. + + Copyright (c) 2011, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Socket.h" + + +/** + Bind a name to a socket. + + The ::UdpBind4 routine connects a name to a UDP4 stack on the local machine. + + The configure call to the UDP4 driver occurs on the first poll, recv, recvfrom, + send or sentto call. Until then, all changes are made in the local UDP context + structure. + + @param [in] pSocket Address of the socket structure. + + @param [in] pSockAddr Address of a sockaddr structure that contains the + connection point on the local machine. An IPv4 address + of INADDR_ANY specifies that the connection is made to + all of the network stacks on the platform. Specifying a + specific IPv4 address restricts the connection to the + network stack supporting that address. Specifying zero + for the port causes the network layer to assign a port + number from the dynamic range. Specifying a specific + port number causes the network layer to use that port. + + @param [in] SockAddrLen Specifies the length in bytes of the sockaddr structure. + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslUdpBind4 ( + IN DT_SOCKET * pSocket, + IN const struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ) +{ + EFI_HANDLE ChildHandle; + DT_LAYER * pLayer; + DT_PORT * pPort; + DT_SERVICE * pService; + CONST struct sockaddr_in * pIp4Address; + EFI_SERVICE_BINDING_PROTOCOL * pUdp4Service; + EFI_STATUS Status; + EFI_STATUS TempStatus; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Assume success + // + pSocket->errno = 0; + Status = EFI_SUCCESS; + + // + // Validate the address length + // + pIp4Address = (CONST struct sockaddr_in *) pSockAddr; + if ( SockAddrLength >= ( sizeof ( *pIp4Address ) + - sizeof ( pIp4Address->sin_zero ))) { + + // + // Walk the list of services + // + pLayer = &mEslLayer; + pService = pLayer->pUdp4List; + while ( NULL != pService ) { + + // + // Create the UDP port + // + pUdp4Service = pService->pInterface; + ChildHandle = NULL; + Status = pUdp4Service->CreateChild ( pUdp4Service, + &ChildHandle ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_BIND | DEBUG_POOL, + "0x%08x: Udp4 port handle created\r\n", + ChildHandle )); + + // + // Open the port + // + Status = EslUdpPortAllocate4 ( pSocket, + pService, + ChildHandle, + (UINT8 *) &pIp4Address->sin_addr.s_addr, + SwapBytes16 ( pIp4Address->sin_port ), + DEBUG_BIND, + &pPort ); + } + else { + DEBUG (( DEBUG_BIND | DEBUG_POOL, + "ERROR - Failed to open Udp4 port handle, Status: %r\r\n", + Status )); + ChildHandle = NULL; + } + + // + // Close the port if necessary + // + if (( EFI_ERROR ( Status )) && ( NULL != ChildHandle )) { + TempStatus = pUdp4Service->DestroyChild ( pUdp4Service, + ChildHandle ); + if ( !EFI_ERROR ( TempStatus )) { + DEBUG (( DEBUG_BIND | DEBUG_POOL, + "0x%08x: Udp4 port handle destroyed\r\n", + ChildHandle )); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_BIND | DEBUG_POOL, + "ERROR - Failed to destroy the Udp4 port handle 0x%08x, Status: %r\r\n", + ChildHandle, + TempStatus )); + ASSERT ( EFI_SUCCESS == TempStatus ); + } + } + + // + // Set the next service + // + pService = pService->pNext; + } + + // + // Verify that at least one network connection was found + // + if ( NULL == pSocket->pPortList ) { + DEBUG (( DEBUG_BIND | DEBUG_POOL | DEBUG_INIT, + "Socket address %d.%d.%d.%d (0x%08x) is not available!\r\n", + ( pIp4Address->sin_addr.s_addr >> 24 ) & 0xff, + ( pIp4Address->sin_addr.s_addr >> 16 ) & 0xff, + ( pIp4Address->sin_addr.s_addr >> 8 ) & 0xff, + pIp4Address->sin_addr.s_addr & 0xff, + pIp4Address->sin_addr.s_addr )); + pSocket->errno = EADDRNOTAVAIL; + Status = EFI_INVALID_PARAMETER; + } + } + else { + DEBUG (( DEBUG_BIND, + "ERROR - Invalid Udp4 address length: %d\r\n", + SockAddrLength )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Initialize the UDP4 service. + + This routine initializes the UDP4 service after its service binding + protocol was located on a controller. + + @param [in] pService DT_SERVICE structure address + + @retval EFI_SUCCESS The service was properly initialized + @retval other A failure occurred during the service initialization + +**/ +EFI_STATUS +EFIAPI +EslUdpInitialize4 ( + IN DT_SERVICE * pService + ) +{ + DT_LAYER * pLayer; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Identify the service + // + pService->NetworkType = NETWORK_TYPE_UDP4; + + // + // Connect this service to the service list + // + pLayer = &mEslLayer; + pService->pNext = pLayer->pUdp4List; + pLayer->pUdp4List = pService; + + // + // Assume the list is empty + // + Status = EFI_SUCCESS; + + // + // Return the initialization status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Allocate and initialize a DT_PORT structure. + + @param [in] pSocket Address of the socket structure. + @param [in] pService Address of the DT_SERVICE structure. + @param [in] ChildHandle Udp4 child handle + @param [in] pIpAddress Buffer containing IP4 network address of the local host + @param [in] PortNumber Udp4 port number + @param [in] DebugFlags Flags for debug messages + @param [out] ppPort Buffer to receive new DT_PORT structure address + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslUdpPortAllocate4 ( + IN DT_SOCKET * pSocket, + IN DT_SERVICE * pService, + IN EFI_HANDLE ChildHandle, + IN CONST UINT8 * pIpAddress, + IN UINT16 PortNumber, + IN UINTN DebugFlags, + OUT DT_PORT ** ppPort + ) +{ + UINTN LengthInBytes; + EFI_UDP4_CONFIG_DATA * pConfig; + DT_LAYER * pLayer; + DT_PORT * pPort; + DT_UDP4_CONTEXT * pUdp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Use for/break instead of goto + for ( ; ; ) { + // + // Allocate a port structure + // + pLayer = &mEslLayer; + LengthInBytes = sizeof ( *pPort ); + Status = gBS->AllocatePool ( EfiRuntimeServicesData, + LengthInBytes, + (VOID **)&pPort ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL | DEBUG_INIT, + "ERROR - Failed to allocate the port structure, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + pPort = NULL; + break; + } + DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, + "0x%08x: Allocate pPort, %d bytes\r\n", + pPort, + LengthInBytes )); + + // + // Initialize the port + // + ZeroMem ( pPort, LengthInBytes ); + pPort->Signature = PORT_SIGNATURE; + pPort->pService = pService; + pPort->pSocket = pSocket; + pPort->pfnCloseStart = EslUdpPortCloseStart4; + pPort->DebugFlags = DebugFlags; + + // + // Allocate the receive event + // + pUdp4 = &pPort->Context.Udp4; + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslUdpRxComplete4, + pPort, + &pUdp4->RxToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the receive event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_RX | DEBUG_POOL, + "0x%08x: Created receive event\r\n", + pUdp4->RxToken.Event )); + + // + // Allocate the transmit event + // + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslUdpTxComplete4, + pPort, + &pUdp4->TxToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the transmit event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Created transmit event\r\n", + pUdp4->TxToken.Event )); + + // + // Open the port protocol + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiUdp4ProtocolGuid, + (VOID **) &pUdp4->pProtocol, + pLayer->ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to open gEfiUdp4ProtocolGuid on controller 0x%08x\r\n", + pUdp4->Handle )); + pSocket->errno = EEXIST; + break; + } + DEBUG (( DebugFlags, + "0x%08x: gEfiUdp4ProtocolGuid opened on controller 0x%08x\r\n", + pUdp4->pProtocol, + ChildHandle )); + + // + // Set the port address + // + pUdp4->Handle = ChildHandle; + pConfig = &pPort->Context.Udp4.ConfigData; + pConfig->StationPort = PortNumber; + if (( 0 == pIpAddress[0]) + && ( 0 == pIpAddress[1]) + && ( 0 == pIpAddress[2]) + && ( 0 == pIpAddress[3])) { + pConfig->UseDefaultAddress = TRUE; + } + else { + pConfig->StationAddress.Addr[0] = pIpAddress[0]; + pConfig->StationAddress.Addr[1] = pIpAddress[1]; + pConfig->StationAddress.Addr[2] = pIpAddress[2]; + pConfig->StationAddress.Addr[3] = pIpAddress[3]; + pConfig->SubnetMask.Addr[0] = 0xff; + pConfig->SubnetMask.Addr[1] = 0xff; + pConfig->SubnetMask.Addr[2] = 0xff; + pConfig->SubnetMask.Addr[3] = 0xff; + } + pConfig->TimeToLive = 255; + pConfig->AcceptAnyPort = FALSE; + pConfig->AcceptBroadcast = FALSE; + pConfig->AcceptPromiscuous = FALSE; + pConfig->AllowDuplicatePort = TRUE; + pConfig->DoNotFragment = TRUE; + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Add this port to the socket + // + pPort->pLinkSocket = pSocket->pPortList; + pSocket->pPortList = pPort; + DEBUG (( DebugFlags, + "0x%08x: Socket adding port: 0x%08x\r\n", + pSocket, + pPort )); + + // + // Add this port to the service + // + pPort->pLinkService = pService->pPortList; + pService->pPortList = pPort; + + // + // Return the port + // + *ppPort = pPort; + break; + } + + // + // Clean up after the error if necessary + // + if (( EFI_ERROR ( Status )) && ( NULL != pPort )) { + // + // Close the port + // + EslUdpPortClose4 ( pPort ); + } + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Close a UDP4 port. + + This routine releases the resources allocated by + ::UdpPortAllocate4(). + + @param [in] pPort Address of the port structure. + + @retval EFI_SUCCESS The port is closed + @retval other Port close error + +**/ +EFI_STATUS +EslUdpPortClose4 ( + IN DT_PORT * pPort + ) +{ + UINTN DebugFlags; + DT_LAYER * pLayer; + DT_PACKET * pPacket; + DT_PORT * pPreviousPort; + DT_SERVICE * pService; + DT_SOCKET * pSocket; + EFI_SERVICE_BINDING_PROTOCOL * pUdp4Service; + DT_UDP4_CONTEXT * pUdp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Assume success + // + Status = EFI_SUCCESS; + pSocket = pPort->pSocket; + pSocket->errno = 0; + + // + // Locate the port in the socket list + // + pLayer = &mEslLayer; + DebugFlags = pPort->DebugFlags; + pPreviousPort = pSocket->pPortList; + if ( pPreviousPort == pPort ) { + // + // Remove this port from the head of the socket list + // + pSocket->pPortList = pPort->pLinkSocket; + } + else { + // + // Locate the port in the middle of the socket list + // + while (( NULL != pPreviousPort ) + && ( pPreviousPort->pLinkSocket != pPort )) { + pPreviousPort = pPreviousPort->pLinkSocket; + } + if ( NULL != pPreviousPort ) { + // + // Remove the port from the middle of the socket list + // + pPreviousPort->pLinkSocket = pPort->pLinkSocket; + } + } + + // + // Locate the port in the service list + // + pService = pPort->pService; + pPreviousPort = pService->pPortList; + if ( pPreviousPort == pPort ) { + // + // Remove this port from the head of the service list + // + pService->pPortList = pPort->pLinkService; + } + else { + // + // Locate the port in the middle of the service list + // + while (( NULL != pPreviousPort ) + && ( pPreviousPort->pLinkService != pPort )) { + pPreviousPort = pPreviousPort->pLinkService; + } + if ( NULL != pPreviousPort ) { + // + // Remove the port from the middle of the service list + // + pPreviousPort->pLinkService = pPort->pLinkService; + } + } + + // + // Empty the receive queue + // + ASSERT ( NULL == pSocket->pRxPacketListHead ); + ASSERT ( NULL == pSocket->pRxPacketListTail ); + ASSERT ( 0 == pSocket->RxBytes ); + + // + // Empty the receive free queue + // + while ( NULL != pSocket->pRxFree ) { + pPacket = pSocket->pRxFree; + pSocket->pRxFree = pPacket->pNext; + EslSocketPacketFree ( pPacket, DEBUG_RX ); + } + + // + // Done with the receive event + // + pUdp4 = &pPort->Context.Udp4; + if ( NULL != pUdp4->RxToken.Event ) { + Status = gBS->CloseEvent ( pUdp4->RxToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed receive event\r\n", + pUdp4->RxToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the receive event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the transmit event + // + if ( NULL != pUdp4->TxToken.Event ) { + Status = gBS->CloseEvent ( pUdp4->TxToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed normal transmit event\r\n", + pUdp4->TxToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the normal transmit event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the UDP protocol + // + pUdp4Service = pService->pInterface; + if ( NULL != pUdp4->pProtocol ) { + Status = gBS->CloseProtocol ( pUdp4->Handle, + &gEfiUdp4ProtocolGuid, + pLayer->ImageHandle, + NULL ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags, + "0x%08x: gEfiUdp4ProtocolGuid closed on controller 0x%08x\r\n", + pUdp4->pProtocol, + pUdp4->Handle )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close gEfiUdp4ProtocolGuid opened on controller 0x%08x, Status: %r\r\n", + pUdp4->Handle, + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the UDP port + // + if ( NULL != pUdp4->Handle ) { + Status = pUdp4Service->DestroyChild ( pUdp4Service, + pUdp4->Handle ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Udp4 port handle destroyed\r\n", + pUdp4->Handle )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL, + "ERROR - Failed to destroy the Udp4 port handle, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Release the port structure + // + Status = gBS->FreePool ( pPort ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Free pPort, %d bytes\r\n", + pPort, + sizeof ( *pPort ))); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL, + "ERROR - Failed to free pPort: 0x%08x, Status: %r\r\n", + pPort, + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + + // + // Mark the socket as closed if necessary + // + if ( NULL == pSocket->pPortList ) { + pSocket->State = SOCKET_STATE_CLOSED; + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Socket State: SOCKET_STATE_CLOSED\r\n", + pSocket )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Start the close operation on a UDP4 port, state 1. + + Closing a port goes through the following states: + 1. Port close starting - Mark the port as closing and wait for transmission to complete + 2. Port TX close done - Transmissions complete, close the port and abort the receives + 3. Port RX close done - Receive operations complete, close the port + 4. Port closed - Release the port resources + + @param [in] pPort Address of the port structure. + @param [in] bCloseNow Set TRUE to abort active transfers + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS The port is closed, not normally returned + @retval EFI_NOT_READY The port has started the closing process + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslUdpPortCloseStart4 ( + IN DT_PORT * pPort, + IN BOOLEAN bCloseNow, + IN UINTN DebugFlags + ) +{ + DT_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Mark the port as closing + // + Status = EFI_ALREADY_STARTED; + pSocket = pPort->pSocket; + pSocket->errno = EALREADY; + if ( PORT_STATE_CLOSE_STARTED > pPort->State ) { + + // + // Update the port state + // + pPort->State = PORT_STATE_CLOSE_STARTED; + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close State: PORT_STATE_CLOSE_STARTED\r\n", + pPort )); + pPort->bCloseNow = bCloseNow; + pPort->DebugFlags = DebugFlags; + + // + // Determine if transmits are complete + // + Status = EslUdpPortCloseTxDone4 ( pPort ); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Port close state 3 + + Continue the close operation after the receive is complete. + + @param [in] pPort Address of the port structure. + + @retval EFI_SUCCESS The port is closed + @retval EFI_NOT_READY The port is still closing + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslUdpPortCloseRxDone4 ( + IN DT_PORT * pPort + ) +{ + PORT_STATE PortState; + DT_SOCKET * pSocket; + DT_UDP4_CONTEXT * pUdp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Verify that the port is closing + // + Status = EFI_ALREADY_STARTED; + pSocket = pPort->pSocket; + pSocket->errno = EALREADY; + PortState = pPort->State; + if (( PORT_STATE_CLOSE_TX_DONE == PortState ) + || ( PORT_STATE_CLOSE_DONE == PortState )) { + // + // Determine if the receive operation is pending + // + Status = EFI_NOT_READY; + pSocket->errno = EAGAIN; + pUdp4 = &pPort->Context.Udp4; + if ( NULL == pUdp4->pReceivePending ) { + // + // The receive operation is complete + // Update the port state + // + pPort->State = PORT_STATE_CLOSE_RX_DONE; + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close State: PORT_STATE_CLOSE_RX_DONE\r\n", + pPort )); + + // + // The close operation has completed + // Release the port resources + // + Status = EslUdpPortClose4 ( pPort ); + } + else { + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close: Receive still pending!\r\n", + pPort )); + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Port close state 2 + + Continue the close operation after the transmission is complete. + + @param [in] pPort Address of the port structure. + + @retval EFI_SUCCESS The port is closed, not normally returned + @retval EFI_NOT_READY The port is still closing + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslUdpPortCloseTxDone4 ( + IN DT_PORT * pPort + ) +{ + DT_PACKET * pPacket; + DT_SOCKET * pSocket; + DT_UDP4_CONTEXT * pUdp4; + EFI_UDP4_PROTOCOL * pUdp4Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // All transmissions are complete or must be stopped + // Mark the port as TX complete + // + Status = EFI_ALREADY_STARTED; + if ( PORT_STATE_CLOSE_STARTED == pPort->State ) { + // + // Verify that the transmissions are complete + // + pSocket = pPort->pSocket; + if ( pPort->bCloseNow + || ( EFI_SUCCESS != pSocket->TxError ) + || ( 0 == pSocket->TxBytes )) { + // + // Start the close operation on the port + // + pUdp4 = &pPort->Context.Udp4; + pUdp4Protocol = pUdp4->pProtocol; + if ( !pUdp4->bConfigured ) { + // + // Skip the close operation since the port is not + // configured + // + // Update the port state + // + pPort->State = PORT_STATE_CLOSE_DONE; + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close State: PORT_STATE_CLOSE_DONE\r\n", + pPort )); + Status = EFI_SUCCESS; + } + else { + // + // Update the port state + // + pPort->State = PORT_STATE_CLOSE_TX_DONE; + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close State: PORT_STATE_CLOSE_TX_DONE\r\n", + pPort )); + + // + // Empty the receive queue + // + while ( NULL != pSocket->pRxPacketListHead ) { + pPacket = pSocket->pRxPacketListHead; + pSocket->pRxPacketListHead = pPacket->pNext; + pSocket->RxBytes -= pPacket->Op.Udp4Rx.pRxData->DataLength; + + // + // Return the buffer to the UDP4 driver + // + gBS->SignalEvent ( pPacket->Op.Udp4Rx.pRxData->RecycleSignal ); + + // + // Done with this packet + // + EslSocketPacketFree ( pPacket, DEBUG_RX ); + } + pSocket->pRxPacketListTail = NULL; + ASSERT ( 0 == pSocket->RxBytes ); + + // + // Reset the port, cancel the outstanding receive + // + Status = pUdp4Protocol->Configure ( pUdp4Protocol, + NULL ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port reset\r\n", + pPort )); + + // + // Free the receive packet + // + Status = gBS->CheckEvent ( pUdp4->RxToken.Event ); + if ( EFI_SUCCESS != Status ) { + EslSocketPacketFree ( pUdp4->pReceivePending, DEBUG_CLOSE ); + pUdp4->pReceivePending = NULL; + Status = EFI_SUCCESS; + } + } + else { + DEBUG (( DEBUG_ERROR | pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "ERROR - Port 0x%08x reset failed, Status: %r\r\n", + pPort, + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Determine if the receive operation is pending + // + if ( !EFI_ERROR ( Status )) { + Status = EslUdpPortCloseRxDone4 ( pPort ); + } + } + else { + // + // Transmissions are still active, exit + // + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close: Transmits are still pending!\r\n", + pPort )); + Status = EFI_NOT_READY; + pSocket->errno = EAGAIN; + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Connect to a remote system via the network. + + The ::UdpConnectStart4= routine sets the remote address for the connection. + + @param [in] pSocket Address of the socket structure. + + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslUdpConnect4 ( + IN DT_SOCKET * pSocket, + IN const struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ) +{ + struct sockaddr_in LocalAddress; + DT_PORT * pPort; + struct sockaddr_in * pRemoteAddress; + DT_UDP4_CONTEXT * pUdp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume failure + // + Status = EFI_NETWORK_UNREACHABLE; + pSocket->errno = ENETUNREACH; + + // + // Get the address + // + pRemoteAddress = (struct sockaddr_in *)pSockAddr; + + // + // Validate the address length + // + if ( SockAddrLength >= ( sizeof ( *pRemoteAddress ) + - sizeof ( pRemoteAddress->sin_zero ))) { + // + // Determine if BIND was already called + // + if ( NULL == pSocket->pPortList ) { + // + // Allow any local port + // + ZeroMem ( &LocalAddress, sizeof ( LocalAddress )); + LocalAddress.sin_len = sizeof ( LocalAddress ); + LocalAddress.sin_family = AF_INET; + Status = EslSocketBind ( &pSocket->SocketProtocol, + (struct sockaddr *)&LocalAddress, + LocalAddress.sin_len, + &pSocket->errno ); + } + + // + // Walk the list of ports + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Set the remote address + // + pUdp4 = &pPort->Context.Udp4; + pUdp4->ConfigData.RemoteAddress.Addr[0] = (UINT8)( pRemoteAddress->sin_addr.s_addr ); + pUdp4->ConfigData.RemoteAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 ); + pUdp4->ConfigData.RemoteAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 ); + pUdp4->ConfigData.RemoteAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 ); + pUdp4->ConfigData.RemotePort = SwapBytes16 ( pRemoteAddress->sin_port ); + + // + // At least one path exists + // + Status = EFI_SUCCESS; + pSocket->errno = 0; + + // + // Set the next port + // + pPort = pPort->pLinkSocket; + } + } + else { + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid UDP4 address length: %d\r\n", + SockAddrLength )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + } + + // + // Return the connect status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Get the local socket address + + @param [in] pSocket Address of the socket structure. + + @param [out] pAddress Network address to receive the local system address + + @param [in,out] pAddressLength Length of the local network address structure + + @retval EFI_SUCCESS - Address available + @retval Other - Failed to get the address + +**/ +EFI_STATUS +EslUdpGetLocalAddress4 ( + IN DT_SOCKET * pSocket, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ) +{ + socklen_t LengthInBytes; + DT_PORT * pPort; + struct sockaddr_in * pLocalAddress; + DT_UDP4_CONTEXT * pUdp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Verify that there is just a single connection + // + pPort = pSocket->pPortList; + if (( NULL != pPort ) && ( NULL == pPort->pLinkSocket )) { + // + // Verify the address length + // + LengthInBytes = sizeof ( struct sockaddr_in ); + if ( LengthInBytes <= * pAddressLength ) { + // + // Return the local address + // + pUdp4 = &pPort->Context.Udp4; + pLocalAddress = (struct sockaddr_in *)pAddress; + ZeroMem ( pLocalAddress, LengthInBytes ); + pLocalAddress->sin_family = AF_INET; + pLocalAddress->sin_len = (uint8_t)LengthInBytes; + pLocalAddress->sin_port = SwapBytes16 ( pUdp4->ConfigData.StationPort ); + CopyMem ( &pLocalAddress->sin_addr, + &pUdp4->ConfigData.StationAddress.Addr[0], + sizeof ( pLocalAddress->sin_addr )); + pSocket->errno = 0; + Status = EFI_SUCCESS; + } + else { + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + } + } + else { + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Get the remote socket address + + @param [in] pSocket Address of the socket structure. + + @param [out] pAddress Network address to receive the remote system address + + @param [in,out] pAddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Address available + @retval Other - Failed to get the address + +**/ +EFI_STATUS +EslUdpGetRemoteAddress4 ( + IN DT_SOCKET * pSocket, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ) +{ + socklen_t LengthInBytes; + DT_PORT * pPort; + struct sockaddr_in * pRemoteAddress; + DT_UDP4_CONTEXT * pUdp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Verify that there is just a single connection + // + pPort = pSocket->pPortList; + if (( NULL != pPort ) && ( NULL == pPort->pLinkSocket )) { + // + // Verify the address length + // + LengthInBytes = sizeof ( struct sockaddr_in ); + if ( LengthInBytes <= * pAddressLength ) { + // + // Return the local address + // + pUdp4 = &pPort->Context.Udp4; + pRemoteAddress = (struct sockaddr_in *)pAddress; + ZeroMem ( pRemoteAddress, LengthInBytes ); + pRemoteAddress->sin_family = AF_INET; + pRemoteAddress->sin_len = (uint8_t)LengthInBytes; + pRemoteAddress->sin_port = SwapBytes16 ( pUdp4->ConfigData.RemotePort ); + CopyMem ( &pRemoteAddress->sin_addr, + &pUdp4->ConfigData.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin_addr )); + pSocket->errno = 0; + Status = EFI_SUCCESS; + } + else { + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + } + } + else { + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Receive data from a network connection. + + To minimize the number of buffer copies, the ::UdpRxComplete4 + routine queues the UDP4 driver's buffer to a list of datagrams + waiting to be received. The socket driver holds on to the + buffers from the UDP4 driver until the application layer requests + the data or the socket is closed. + + The application calls this routine in the socket layer to + receive datagrams from one or more remote systems. This routine + removes the next available datagram from the list of datagrams + and copies the data from the UDP4 driver's buffer into the + application's buffer. The UDP4 driver's buffer is then returned. + + @param [in] pSocket Address of a DT_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [out] pAddress Network address to receive the remote system address + + @param [in,out] pAddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Socket data successfully received + +**/ +EFI_STATUS +EslUdpReceive4 ( + IN DT_SOCKET * pSocket, + IN INT32 Flags, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength, + OUT struct sockaddr * pAddress, + IN OUT socklen_t * pAddressLength + ) +{ + socklen_t AddressLength; + size_t BytesToCopy; + size_t DataBytes; + UINT32 Fragment; + in_addr_t IpAddress; + size_t LengthInBytes; + UINT8 * pData; + DT_PACKET * pPacket; + DT_PORT * pPort; + struct sockaddr_in * pRemoteAddress; + EFI_UDP4_RECEIVE_DATA * pRxData; + DT_UDP4_CONTEXT * pUdp4; + struct sockaddr_in RemoteAddress; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume failure + // + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTCONN; + + // + // Verify that the socket is connected + // + if (( SOCKET_STATE_CONNECTED == pSocket->State ) + || ( PORT_STATE_RX_ERROR == pSocket->State )) { + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine if there is any data on the queue + // + pUdp4 = &pPort->Context.Udp4; + pPacket = pSocket->pRxPacketListHead; + if ( NULL != pPacket ) { + // + // Validate the return address parameters + // + pRxData = pPacket->Op.Udp4Rx.pRxData; + if (( NULL == pAddress ) || ( NULL != pAddressLength )) { + // + // Return the remote system address if requested + // + if ( NULL != pAddress ) { + // + // Build the remote address + // + DEBUG (( DEBUG_RX, + "Getting packet source address: %d.%d.%d.%d:%d\r\n", + pRxData->UdpSession.SourceAddress.Addr[0], + pRxData->UdpSession.SourceAddress.Addr[1], + pRxData->UdpSession.SourceAddress.Addr[2], + pRxData->UdpSession.SourceAddress.Addr[3], + pRxData->UdpSession.SourcePort )); + ZeroMem ( &RemoteAddress, sizeof ( RemoteAddress )); + RemoteAddress.sin_len = sizeof ( RemoteAddress ); + RemoteAddress.sin_family = AF_INET; + IpAddress = pRxData->UdpSession.SourceAddress.Addr[3]; + IpAddress <<= 8; + IpAddress |= pRxData->UdpSession.SourceAddress.Addr[2]; + IpAddress <<= 8; + IpAddress |= pRxData->UdpSession.SourceAddress.Addr[1]; + IpAddress <<= 8; + IpAddress |= pRxData->UdpSession.SourceAddress.Addr[0]; + RemoteAddress.sin_addr.s_addr = IpAddress; + RemoteAddress.sin_port = SwapBytes16 ( pRxData->UdpSession.SourcePort ); + + // + // Copy the address + // + pRemoteAddress = (struct sockaddr_in *)pAddress; + AddressLength = sizeof ( *pRemoteAddress ); + if ( AddressLength > *pAddressLength ) { + AddressLength = *pAddressLength; + } + CopyMem ( pRemoteAddress, + &RemoteAddress, + AddressLength ); + + // + // Update the address length + // + *pAddressLength = AddressLength; + } + + // + // Reduce the buffer length if necessary + // + DataBytes = pRxData->DataLength; + if ( DataBytes < BufferLength ) { + BufferLength = DataBytes; + } + + // + // Copy the received data + // + LengthInBytes = 0; + Fragment = 0; + do { + // + // Determine the amount of received data + // + pData = pRxData->FragmentTable[Fragment].FragmentBuffer; + BytesToCopy = pRxData->FragmentTable[Fragment].FragmentLength; + if (( BufferLength - LengthInBytes ) < BytesToCopy ) { + BytesToCopy = BufferLength - LengthInBytes; + } + LengthInBytes += BytesToCopy; + + // + // Move the data into the buffer + // + DEBUG (( DEBUG_RX, + "0x%08x: Port copy packet 0x%08x data into 0x%08x, 0x%08x bytes\r\n", + pPort, + pPacket, + pBuffer, + BytesToCopy )); + CopyMem ( pBuffer, pData, BytesToCopy ); + } while ( BufferLength > LengthInBytes ); + + // + // Determine if the data is being read + // + if ( 0 == ( Flags & MSG_PEEK )) { + // + // Display for the bytes consumed + // + DEBUG (( DEBUG_RX, + "0x%08x: Port account for 0x%08x bytes\r\n", + pPort, + BufferLength )); + + // + // All done with this packet + // Account for any discarded data + // + pSocket->RxBytes -= DataBytes; + if ( 0 != ( DataBytes - BufferLength )) { + DEBUG (( DEBUG_RX, + "0x%08x: Port, packet read, skipping over 0x%08x bytes\r\n", + pPort, + DataBytes - BufferLength )); + } + + // + // Remove this packet from the queue + // + pSocket->pRxPacketListHead = pPacket->pNext; + if ( NULL == pSocket->pRxPacketListHead ) { + pSocket->pRxPacketListTail = NULL; + } + + // + // Return this packet to the UDP4 driver + // + gBS->SignalEvent ( pRxData->RecycleSignal ); + + // + // Move the packet to the free queue + // + pPacket->pNext = pSocket->pRxFree; + pSocket->pRxFree = pPacket; + DEBUG (( DEBUG_RX, + "0x%08x: Port freeing packet 0x%08x\r\n", + pPort, + pPacket )); + + // + // Restart this receive operation if necessary + // + if (( NULL == pUdp4->pReceivePending ) + && ( MAX_RX_DATA > pSocket->RxBytes )) { + EslUdpRxStart4 ( pPort ); + } + } + + // + // Return the data length + // + *pDataLength = LengthInBytes; + + // + // Successful operation + // + Status = EFI_SUCCESS; + pSocket->errno = 0; + } + else { + // + // Bad return address pointer and length + // + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + } + } + else { + // + // The queue is empty + // Determine if it is time to return the receive error + // + if ( EFI_ERROR ( pSocket->RxError )) { + Status = pSocket->RxError; + switch ( Status ) { + default: + pSocket->errno = EIO; + break; + + case EFI_HOST_UNREACHABLE: + pSocket->errno = EHOSTUNREACH; + break; + + case EFI_NETWORK_UNREACHABLE: + pSocket->errno = ENETUNREACH; + break; + + case EFI_PORT_UNREACHABLE: + pSocket->errno = EPROTONOSUPPORT; + break; + + case EFI_PROTOCOL_UNREACHABLE: + pSocket->errno = ENOPROTOOPT; + break; + } + pSocket->RxError = EFI_SUCCESS; + } + else { + Status = EFI_NOT_READY; + pSocket->errno = EAGAIN; + } + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Cancel the receive operations + + @param [in] pSocket Address of a DT_SOCKET structure + + @retval EFI_SUCCESS - The cancel was successful + + **/ +EFI_STATUS +EslUdpRxCancel4 ( + IN DT_SOCKET * pSocket + ) +{ + DT_PACKET * pPacket; + DT_PORT * pPort; + DT_UDP4_CONTEXT * pUdp4; + EFI_UDP4_PROTOCOL * pUdp4Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume failure + // + Status = EFI_NOT_FOUND; + + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine if a receive is pending + // + pUdp4 = &pPort->Context.Udp4; + pPacket = pUdp4->pReceivePending; + if ( NULL != pPacket ) { + // + // Attempt to cancel the receive operation + // + pUdp4Protocol = pUdp4->pProtocol; + Status = pUdp4Protocol->Cancel ( pUdp4Protocol, + &pUdp4->RxToken ); + if ( EFI_NOT_FOUND == Status ) { + // + // The receive is complete + // + Status = EFI_SUCCESS; + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the receive completion + + Keep the UDP4 driver's buffer and append it to the list of + datagrams for the application to receive. The UDP4 driver's + buffer will be returned by either ::UdpReceive4 or + ::UdpPortCloseTxDone4. + + @param Event The receive completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslUdpRxComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ) +{ + size_t LengthInBytes; + DT_PACKET * pPacket; + DT_PACKET * pPrevious; + EFI_UDP4_RECEIVE_DATA * pRxData; + DT_SOCKET * pSocket; + DT_UDP4_CONTEXT * pUdp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Mark this receive complete + // + pUdp4 = &pPort->Context.Udp4; + pPacket = pUdp4->pReceivePending; + pUdp4->pReceivePending = NULL; + + // + // Determine if this receive was successful + // + pSocket = pPort->pSocket; + Status = pUdp4->RxToken.Status; + if (( !EFI_ERROR ( Status )) && ( !pSocket->bRxDisable )) { + pRxData = pUdp4->RxToken.Packet.RxData; + if ( PORT_STATE_CLOSE_STARTED >= pPort->State ) { + // + // Save the data in the packet + // + pPacket->Op.Udp4Rx.pRxData = pRxData; + + // + // Queue this packet + // + pPrevious = pSocket->pRxPacketListTail; + if ( NULL == pPrevious ) { + pSocket->pRxPacketListHead = pPacket; + } + else { + pPrevious->pNext = pPacket; + } + pSocket->pRxPacketListTail = pPacket; + + // + // Account for the normal data + // + LengthInBytes = pRxData->DataLength; + pSocket->RxBytes += LengthInBytes; + + // + // Log the received data + // + DEBUG (( DEBUG_RX | DEBUG_INFO, + "Received packet from: %d.%d.%d.%d:%d\r\n", + pRxData->UdpSession.SourceAddress.Addr[0], + pRxData->UdpSession.SourceAddress.Addr[1], + pRxData->UdpSession.SourceAddress.Addr[2], + pRxData->UdpSession.SourceAddress.Addr[3], + pRxData->UdpSession.SourcePort )); + DEBUG (( DEBUG_RX | DEBUG_INFO, + "Received packet sent to: %d.%d.%d.%d:%d\r\n", + pRxData->UdpSession.DestinationAddress.Addr[0], + pRxData->UdpSession.DestinationAddress.Addr[1], + pRxData->UdpSession.DestinationAddress.Addr[2], + pRxData->UdpSession.DestinationAddress.Addr[3], + pRxData->UdpSession.DestinationPort )); + DEBUG (( DEBUG_RX | DEBUG_INFO, + "0x%08x: Packet queued on port 0x%08x with 0x%08x bytes of data\r\n", + pPacket, + pPort, + LengthInBytes )); + + // + // Attempt to restart this receive operation + // + if ( pSocket->MaxRxBuf > pSocket->RxBytes ) { + EslUdpRxStart4 ( pPort ); + } + else { + DEBUG (( DEBUG_RX, + "0x%08x: Port RX suspended, 0x%08x bytes queued\r\n", + pPort, + pSocket->RxBytes )); + } + } + else { + // + // The port is being closed + // Return the buffer to the UDP4 driver + // + gBS->SignalEvent ( pRxData->RecycleSignal ); + + // + // Free the packet + // + EslSocketPacketFree ( pPacket, DEBUG_RX ); + } + } + else + { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "ERROR - Receiving packet 0x%08x, on port 0x%08x, Status:%r\r\n", + pPacket, + pPort, + Status )); + + // + // Receive error, free the packet save the error + // + EslSocketPacketFree ( pPacket, DEBUG_RX ); + if ( !EFI_ERROR ( pSocket->RxError )) { + pSocket->RxError = Status; + } + + // + // Update the port state + // + if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { + EslUdpPortCloseRxDone4 ( pPort ); + } + else { + if ( EFI_ERROR ( Status )) { + pPort->State = PORT_STATE_RX_ERROR; + } + } + } + + DBG_EXIT ( ); +} + + +/** + Start a receive operation + + @param [in] pPort Address of the DT_PORT structure. + + **/ +VOID +EslUdpRxStart4 ( + IN DT_PORT * pPort + ) +{ + DT_PACKET * pPacket; + DT_SOCKET * pSocket; + DT_UDP4_CONTEXT * pUdp4; + EFI_UDP4_PROTOCOL * pUdp4Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if a receive is already pending + // + Status = EFI_SUCCESS; + pPacket = NULL; + pSocket = pPort->pSocket; + pUdp4 = &pPort->Context.Udp4; + if ( !EFI_ERROR ( pPort->pSocket->RxError )) { + if (( NULL == pUdp4->pReceivePending ) + && ( PORT_STATE_CLOSE_STARTED > pPort->State )) { + // + // Determine if there are any free packets + // + pPacket = pSocket->pRxFree; + if ( NULL != pPacket ) { + // + // Remove this packet from the free list + // + pSocket->pRxFree = pPacket->pNext; + DEBUG (( DEBUG_RX, + "0x%08x: Port removed packet 0x%08x from free list\r\n", + pPort, + pPacket )); + } + else { + // + // Allocate a packet structure + // + Status = EslSocketPacketAllocate ( &pPacket, + sizeof ( pPacket->Op.Udp4Rx ), + DEBUG_RX ); + if ( EFI_ERROR ( Status )) { + pPacket = NULL; + DEBUG (( DEBUG_ERROR | DEBUG_RX, + "0x%08x: Port failed to allocate RX packet, Status: %r\r\n", + pPort, + Status )); + } + } + + // + // Determine if a packet is available + // + if ( NULL != pPacket ) { + // + // Initialize the buffer for receive + // + pPacket->pNext = NULL; + pPacket->Op.Udp4Rx.pRxData = NULL; + pUdp4->RxToken.Packet.RxData = NULL; + pUdp4->pReceivePending = pPacket; + + // + // Start the receive on the packet + // + pUdp4Protocol = pUdp4->pProtocol; + Status = pUdp4Protocol->Receive ( pUdp4Protocol, + &pUdp4->RxToken ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "0x%08x: Packet receive pending on port 0x%08x\r\n", + pPacket, + pPort )); + } + else { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "ERROR - Failed to post a receive on port 0x%08x, Status: %r\r\n", + pPort, + Status )); + if ( !EFI_ERROR ( pSocket->RxError )) { + // + // Save the error status + // + pSocket->RxError = Status; + } + + // + // Free the packet + // + pUdp4->pReceivePending = NULL; + pPacket->pNext = pSocket->pRxFree; + pSocket->pRxFree = pPacket; + } + } + } + } + + DBG_EXIT ( ); +} + + +/** + Shutdown the UDP4 service. + + This routine undoes the work performed by ::UdpInitialize4. + + @param [in] pService DT_SERVICE structure address + +**/ +VOID +EFIAPI +EslUdpShutdown4 ( + IN DT_SERVICE * pService + ) +{ + DT_LAYER * pLayer; + DT_PORT * pPort; + DT_SERVICE * pPreviousService; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Walk the list of ports + // + do { + pPort = pService->pPortList; + if ( NULL != pPort ) { + // + // Remove the port from the port list + // + pService->pPortList = pPort->pLinkService; + + // + // Close the port + // TODO: Fix this + // +// pPort->pfnClosePort ( pPort, 0 ); + } + } while ( NULL != pPort ); + + // + // Remove the service from the service list + // + pLayer = &mEslLayer; + pPreviousService = pLayer->pUdp4List; + if ( pService == pPreviousService ) { + // + // Remove the service from the beginning of the list + // + pLayer->pUdp4List = pService->pNext; + } + else { + // + // Remove the service from the middle of the list + // + while ( NULL != pPreviousService ) { + if ( pService == pPreviousService->pNext ) { + pPreviousService->pNext = pService->pNext; + break; + } + } + } + + DBG_EXIT ( ); +} + + +/** + Determine if the sockedt is configured. + + + @param [in] pSocket Address of a DT_SOCKET structure + + @retval EFI_SUCCESS - The port is connected + @retval EFI_NOT_STARTED - The port is not connected + + **/ + EFI_STATUS + EslUdpSocketIsConfigured4 ( + IN DT_SOCKET * pSocket + ) +{ + DT_PORT * pPort; + DT_PORT * pNextPort; + DT_UDP4_CONTEXT * pUdp4; + EFI_UDP4_PROTOCOL * pUdp4Protocol; + EFI_STATUS Status; + struct sockaddr_in LocalAddress; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Configure the port if necessary + // + if ( !pSocket->bConfigured ) { + // + // Fill in the port list if necessary + // + if ( NULL == pSocket->pPortList ) { + LocalAddress.sin_len = sizeof ( LocalAddress ); + LocalAddress.sin_family = AF_INET; + LocalAddress.sin_addr.s_addr = 0; + LocalAddress.sin_port = 0; + Status = EslUdpBind4 ( pSocket, + (struct sockaddr *)&LocalAddress, + LocalAddress.sin_len ); + } + + // + // Walk the port list + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Attempt to configure the port + // + pNextPort = pPort->pLinkSocket; + pUdp4 = &pPort->Context.Udp4; + pUdp4Protocol = pUdp4->pProtocol; + Status = pUdp4Protocol->Configure ( pUdp4Protocol, + &pUdp4->ConfigData ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed to configure the Udp4 port, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NO_MAPPING: + pSocket->errno = EAFNOSUPPORT; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = EOPNOTSUPP; + break; + } + } + else { + DEBUG (( DEBUG_LISTEN, + "0x%08x: Port configured\r\n", + pPort )); + pUdp4->bConfigured = TRUE; + + // + // Start the first read on the port + // + EslUdpRxStart4 ( pPort ); + + // + // The socket is connected + // + pSocket->State = SOCKET_STATE_CONNECTED; + } + + // + // Set the next port + // + pPort = pNextPort; + } + + // + // Determine the configuration status + // + if ( NULL != pSocket->pPortList ) { + pSocket->bConfigured = TRUE; + } + } + + // + // Determine the socket configuration status + // + if ( !EFI_ERROR ( Status )) { + Status = pSocket->bConfigured ? EFI_SUCCESS : EFI_NOT_STARTED; + } + + // + // Return the port connected state. + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Buffer data for transmission over a network connection. + + This routine is called by the socket layer API to buffer + data for transmission. The data is copied into a local buffer + freeing the application buffer for reuse upon return. When + necessary, this routine will start the transmit engine that + performs the data transmission on the network connection. The + transmit engine transmits the data a packet at a time over the + network connection. + + Transmission errors are returned during the next transmission or + during the close operation. Only buffering errors are returned + during the current transmission attempt. + + @param [in] pSocket Address of a DT_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [in] pAddress Network address of the remote system address + + @param [in] AddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Socket data successfully buffered + +**/ +EFI_STATUS +EslUdpTxBuffer4 ( + IN DT_SOCKET * pSocket, + IN int Flags, + IN size_t BufferLength, + IN CONST UINT8 * pBuffer, + OUT size_t * pDataLength, + IN const struct sockaddr * pAddress, + IN socklen_t AddressLength + ) +{ + DT_PACKET * pPacket; + DT_PACKET * pPreviousPacket; + DT_PACKET ** ppPacket; + DT_PORT * pPort; + const struct sockaddr_in * pRemoteAddress; + DT_UDP4_CONTEXT * pUdp4; + EFI_UDP4_COMPLETION_TOKEN * pToken; + size_t * pTxBytes; + DT_UDP4_TX_DATA * pTxData; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume failure + // + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTCONN; + * pDataLength = 0; + + // + // Verify that the socket is connected + // + if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine the queue head + // + pUdp4 = &pPort->Context.Udp4; + ppPacket = &pUdp4->pTxPacket; + pToken = &pUdp4->TxToken; + pTxBytes = &pSocket->TxBytes; + + // + // Verify that there is enough room to buffer another + // transmit operation + // + if ( pSocket->MaxTxBuf > *pTxBytes ) { + // + // Attempt to allocate the packet + // + Status = EslSocketPacketAllocate ( &pPacket, + sizeof ( pPacket->Op.Udp4Tx ) + - sizeof ( pPacket->Op.Udp4Tx.Buffer ) + + BufferLength, + DEBUG_TX ); + if ( !EFI_ERROR ( Status )) { + // + // Initialize the transmit operation + // + pTxData = &pPacket->Op.Udp4Tx; + pTxData->TxData.GatewayAddress = NULL; + pTxData->TxData.UdpSessionData = NULL; + pTxData->TxData.DataLength = (UINT32) BufferLength; + pTxData->TxData.FragmentCount = 1; + pTxData->TxData.FragmentTable[0].FragmentLength = (UINT32) BufferLength; + pTxData->TxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Udp4Tx.Buffer[0]; + + // + // Set the remote system address if necessary + // + if ( NULL != pAddress ) { + pRemoteAddress = (const struct sockaddr_in *)pAddress; + pTxData->Session.SourceAddress.Addr[0] = 0; + pTxData->Session.SourceAddress.Addr[1] = 0; + pTxData->Session.SourceAddress.Addr[2] = 0; + pTxData->Session.SourceAddress.Addr[3] = 0; + pTxData->Session.SourcePort = 0; + pTxData->Session.DestinationAddress.Addr[0] = (UINT8)pRemoteAddress->sin_addr.s_addr; + pTxData->Session.DestinationAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 ); + pTxData->Session.DestinationAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 ); + pTxData->Session.DestinationAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 ); + pTxData->Session.DestinationPort = SwapBytes16 ( pRemoteAddress->sin_port ); + + // + // Use the remote system address when sending this packet + // + pTxData->TxData.UdpSessionData = &pTxData->Session; + } + + // + // Copy the data into the buffer + // + CopyMem ( &pPacket->Op.Udp4Tx.Buffer[0], + pBuffer, + BufferLength ); + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Stop transmission after an error + // + if ( !EFI_ERROR ( pSocket->TxError )) { + // + // Display the request + // + DEBUG (( DEBUG_TX, + "Send %d %s bytes from 0x%08x\r\n", + BufferLength, + pBuffer )); + + // + // Queue the data for transmission + // + pPacket->pNext = NULL; + pPreviousPacket = pSocket->pTxPacketListTail; + if ( NULL == pPreviousPacket ) { + pSocket->pTxPacketListHead = pPacket; + } + else { + pPreviousPacket->pNext = pPacket; + } + pSocket->pTxPacketListTail = pPacket; + DEBUG (( DEBUG_TX, + "0x%08x: Packet on transmit list\r\n", + pPacket )); + + // + // Account for the buffered data + // + *pTxBytes += BufferLength; + *pDataLength = BufferLength; + + // + // Start the transmit engine if it is idle + // + if ( NULL == pUdp4->pTxPacket ) { + EslUdpTxStart4 ( pSocket->pPortList ); + } + } + else { + // + // Previous transmit error + // Stop transmission + // + Status = pSocket->TxError; + pSocket->errno = EIO; + + // + // Free the packet + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + // + // Packet allocation failed + // + pSocket->errno = ENOMEM; + } + } + else { + // + // Not enough buffer space available + // + pSocket->errno = EAGAIN; + Status = EFI_NOT_READY; + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the transmit completion + + @param Event The normal transmit completion event + + @param pPort The DT_PORT structure address + +**/ +VOID +EslUdpTxComplete4 ( + IN EFI_EVENT Event, + IN DT_PORT * pPort + ) +{ + UINT32 LengthInBytes; + DT_PACKET * pCurrentPacket; + DT_PACKET * pNextPacket; + DT_PACKET * pPacket; + DT_SOCKET * pSocket; + DT_UDP4_CONTEXT * pUdp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the active transmit packet + // + pSocket = pPort->pSocket; + pUdp4 = &pPort->Context.Udp4; + pPacket = pUdp4->pTxPacket; + + // + // Mark this packet as complete + // + pUdp4->pTxPacket = NULL; + LengthInBytes = pPacket->Op.Udp4Tx.TxData.DataLength; + pSocket->TxBytes -= LengthInBytes; + + // + // Save any transmit error + // + Status = pUdp4->TxToken.Status; + if ( EFI_ERROR ( Status )) { + if ( !EFI_ERROR ( pSocket->TxError )) { + pSocket->TxError = Status; + } + DEBUG (( DEBUG_TX | DEBUG_INFO, + "ERROR - Transmit failure for packet 0x%08x, Status: %r\r\n", + pPacket, + Status )); + + // + // Empty the normal transmit list + // + pCurrentPacket = pPacket; + pNextPacket = pSocket->pTxPacketListHead; + while ( NULL != pNextPacket ) { + pPacket = pNextPacket; + pNextPacket = pPacket->pNext; + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + pSocket->pTxPacketListHead = NULL; + pSocket->pTxPacketListTail = NULL; + pPacket = pCurrentPacket; + } + else + { + DEBUG (( DEBUG_TX | DEBUG_INFO, + "0x%08x: Packet transmitted %d bytes successfully\r\n", + pPacket, + LengthInBytes )); + + // + // Verify the transmit engine is still running + // + if ( !pPort->bCloseNow ) { + // + // Start the next packet transmission + // + EslUdpTxStart4 ( pPort ); + } + } + + // + // Release this packet + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + + // + // Finish the close operation if necessary + // + if (( PORT_STATE_CLOSE_STARTED <= pPort->State ) + && ( NULL == pSocket->pTxPacketListHead ) + && ( NULL == pUdp4->pTxPacket )) { + // + // Indicate that the transmit is complete + // + EslUdpPortCloseTxDone4 ( pPort ); + } + DBG_EXIT ( ); +} + + +/** + Transmit data using a network connection. + + @param [in] pPort Address of a DT_PORT structure + + **/ +VOID +EslUdpTxStart4 ( + IN DT_PORT * pPort + ) +{ + DT_PACKET * pNextPacket; + DT_PACKET * pPacket; + DT_SOCKET * pSocket; + DT_UDP4_CONTEXT * pUdp4; + EFI_UDP4_PROTOCOL * pUdp4Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Get the packet from the queue head + // + pSocket = pPort->pSocket; + pPacket = pSocket->pTxPacketListHead; + if ( NULL != pPacket ) { + // + // Remove the packet from the queue + // + pNextPacket = pPacket->pNext; + pSocket->pTxPacketListHead = pNextPacket; + if ( NULL == pNextPacket ) { + pSocket->pTxPacketListTail = NULL; + } + + // + // Set the packet as active + // + pUdp4 = &pPort->Context.Udp4; + pUdp4->pTxPacket = pPacket; + + // + // Start the transmit operation + // + pUdp4Protocol = pUdp4->pProtocol; + pUdp4->TxToken.Packet.TxData = &pPacket->Op.Udp4Tx.TxData; + Status = pUdp4Protocol->Transmit ( pUdp4Protocol, &pUdp4->TxToken ); + if ( EFI_ERROR ( Status )) { + pSocket = pPort->pSocket; + if ( EFI_SUCCESS == pSocket->TxError ) { + pSocket->TxError = Status; + } + } + } + + DBG_EXIT ( ); +} + diff --git a/StdLib/EfiSocketLib/UseEfiSocketLib.c b/StdLib/EfiSocketLib/UseEfiSocketLib.c new file mode 100644 index 0000000000..b0e8ef671a --- /dev/null +++ b/StdLib/EfiSocketLib/UseEfiSocketLib.c @@ -0,0 +1,242 @@ +/** @file + Implement the connection to the EFI socket library + + Copyright (c) 2011, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + + +CONST EFI_GUID mEslRawServiceGuid = { + 0xc31bf4a5, 0x2c7, 0x49d2, { 0xa5, 0x58, 0xfe, 0x62, 0x6f, 0x7e, 0xd4, 0x77 } +}; + +CONST EFI_GUID mEslTcp4ServiceGuid = { + 0xffc659c2, 0x4ef2, 0x4532, { 0xb8, 0x75, 0xcd, 0x9a, 0xa4, 0x27, 0x4c, 0xde } +}; + +CONST EFI_GUID mEslUdp4ServiceGuid = { + 0x44e03a55, 0x8d97, 0x4511, { 0xbf, 0xef, 0xa, 0x8b, 0xc6, 0x2c, 0x25, 0xae } +}; + + +/** + Connect to the EFI socket library + + @param [in] ppSocketProtocol Address to receive the socket protocol address + + @retval 0 Successfully returned the socket protocol + @retval other Value for errno + **/ +int +EslServiceGetProtocol ( + IN EFI_SOCKET_PROTOCOL ** ppSocketProtocol + ) +{ + EFI_HANDLE ChildHandle; + DT_SOCKET * pSocket; + int RetVal; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume success + // + RetVal = 0; + + // + // Locate the socket protocol + // + ChildHandle = NULL; + Status = EslSocketAllocate ( &ChildHandle, + DEBUG_SOCKET, + &pSocket ); + if ( !EFI_ERROR ( Status )) { + *ppSocketProtocol = &pSocket->SocketProtocol; + } + else { + // + // No resources + // + RetVal = ENOMEM; + } + + // + // Return the operation status + // + DBG_EXIT_DEC ( RetVal ); + return RetVal; +} + + +/** + Connect to the network layer + + @retval EFI_SUCCESS Successfully connected to the network layer + + **/ +EFI_STATUS +EslServiceNetworkConnect ( + VOID + ) +{ + UINTN HandleCount; + EFI_HANDLE * pHandles; + UINTN Index; + CONST DT_SOCKET_BINDING * pSocketBinding; + CONST DT_SOCKET_BINDING * pEnd; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Initialize the socket layer + // + Status = EFI_SUCCESS; + EslServiceLoad ( gImageHandle ); + + // + // Connect the network devices + // + pSocketBinding = &cEslSocketBinding [0]; + pEnd = &pSocketBinding [ cEslSocketBindingEntries ]; + while ( pEnd > pSocketBinding ) { + // + // Attempt to locate the network adapters + // + HandleCount = 0; + pHandles = NULL; + Status = gBS->LocateHandleBuffer ( ByProtocol, + pSocketBinding->pNetworkBinding, + NULL, + &HandleCount, + &pHandles ); + if ( EFI_ERROR ( Status )) { + break; + } + if ( NULL != pHandles ) { + // + // Attempt to connect to this network adapter + // + for ( Index = 0; HandleCount > Index; Index++ ) { + Status = EslServiceConnect ( gImageHandle, + pHandles [ Index ]); + if ( EFI_ERROR ( Status )) { + break; + } + } + + // + // Done with the handles + // + gBS->FreePool ( pHandles ); + } + + // + // Set the next network protocol + // + pSocketBinding += 1; + } + + // + // Return the network connection status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Disconnect from the network layer + + @retval EFI_SUCCESS Successfully disconnected from the network layer + + **/ +EFI_STATUS +EslServiceNetworkDisconnect ( + VOID + ) +{ + UINTN HandleCount; + EFI_HANDLE * pHandles; + UINTN Index; + CONST DT_SOCKET_BINDING * pSocketBinding; + CONST DT_SOCKET_BINDING * pEnd; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Disconnect the network devices + // + pSocketBinding = &cEslSocketBinding [0]; + pEnd = &pSocketBinding [ cEslSocketBindingEntries ]; + while ( pEnd > pSocketBinding ) { + // + // Attempt to locate the network adapters + // + HandleCount = 0; + pHandles = NULL; + Status = gBS->LocateHandleBuffer ( ByProtocol, + pSocketBinding->pNetworkBinding, + NULL, + &HandleCount, + &pHandles ); + if ( EFI_ERROR ( Status )) { + break; + } + if ( NULL != pHandles ) { + // + // Attempt to disconnect from this network adapter + // + for ( Index = 0; HandleCount > Index; Index++ ) { + Status = EslServiceDisconnect ( gImageHandle, + pHandles [ Index ]); + if ( EFI_ERROR ( Status )) { + break; + } + } + + // + // Done with the handles + // + gBS->FreePool ( pHandles ); + } + + // + // Set the next network protocol + // + pSocketBinding += 1; + } + + // + // Finish the disconnect operation + // + if ( !EFI_ERROR ( Status )) { + EslServiceUnload ( ); + } + + // + // Return the network connection status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +PFN_ESL_xSTRUCTOR mpfnEslConstructor = EslServiceNetworkConnect; +PFN_ESL_xSTRUCTOR mpfnEslDestructor = EslServiceNetworkDisconnect; -- cgit v1.2.3