From a88c31639bb24c73383a4528a5b77066e805148b Mon Sep 17 00:00:00 2001 From: lpleahy Date: Fri, 30 Sep 2011 23:02:35 +0000 Subject: Update the sockets library code * Passes conformance and functional tests. * Builds with GCC 4.4 compiler. Signed-off by: lpleahy git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12497 6f19259b-4bc3-4df7-8a09-765794883524 --- StdLib/EfiSocketLib/DxeSupport.c | 322 ++ StdLib/EfiSocketLib/EfiSocketLib.inf | 5 + StdLib/EfiSocketLib/Init.c | 34 +- StdLib/EfiSocketLib/Ip4.c | 1264 ++++++++ StdLib/EfiSocketLib/ReleaseNotes.txt | 31 + StdLib/EfiSocketLib/Service.c | 201 +- StdLib/EfiSocketLib/Socket.c | 5646 ++++++++++++++++++++++++--------- StdLib/EfiSocketLib/Socket.h | 1770 ++++++----- StdLib/EfiSocketLib/Tcp4.c | 3073 ++++++------------ StdLib/EfiSocketLib/Udp4.c | 2320 +++----------- StdLib/EfiSocketLib/UseEfiSocketLib.c | 138 +- 11 files changed, 8355 insertions(+), 6449 deletions(-) create mode 100644 StdLib/EfiSocketLib/DxeSupport.c create mode 100644 StdLib/EfiSocketLib/Ip4.c create mode 100644 StdLib/EfiSocketLib/ReleaseNotes.txt (limited to 'StdLib/EfiSocketLib') diff --git a/StdLib/EfiSocketLib/DxeSupport.c b/StdLib/EfiSocketLib/DxeSupport.c new file mode 100644 index 0000000000..284fa9cdfe --- /dev/null +++ b/StdLib/EfiSocketLib/DxeSupport.c @@ -0,0 +1,322 @@ +/** @file + SocketDxe support routines + + 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" + + +/** + Creates a child handle and installs gEfiSocketProtocolGuid. + + This routine creates a child handle for the socket driver and + installs the ::gEfiSocketProtocolGuid on that handle with a pointer + to the ::EFI_SOCKET_PROTOCOL structure address. + + This routine is called by ::EslServiceGetProtocol in UseSocketDxe + when the socket application is linked with UseSocketDxe. + + @param [in] pThis Address of the EFI_SERVICE_BINDING_PROTOCOL structure. + @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 +EslDxeCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL * pThis, + IN OUT EFI_HANDLE * pChildHandle + ) +{ + ESL_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; +} + + +/** + Removes gEfiSocketProtocolGuid and destroys the child handle. + + This routine uninstalls ::gEfiSocketProtocolGuid from the child handle + and destroys the child handle if necessary. + + This routine is called from ???. + + @param [in] pThis Address of the EFI_SERVICE_BINDING_PROTOCOL structure. + @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 +EslDxeDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL * pThis, + IN EFI_HANDLE ChildHandle + ) +{ + ESL_LAYER * pLayer; + ESL_SOCKET * pSocket; + ESL_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; +} + + +/** +Install the socket service + +This routine installs the ::gEfiSocketServiceBindingProtocolGuid +on the SocketDxe image handle to announce the availability +of the socket layer to the rest of EFI. + +SocketDxe's EntryPoint routine calls this routine to +make the socket layer available. + +@param [in] pImageHandle Address of the image handle + +@retval EFI_SUCCESS Service installed successfully +**/ +EFI_STATUS +EFIAPI +EslDxeInstall ( + IN EFI_HANDLE * pImageHandle + ) +{ + EFI_STATUS Status; + + // + // Install the socket service binding protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + pImageHandle, + &gEfiSocketServiceBindingProtocolGuid, + mEslLayer.pServiceBinding, + 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; +} + + +/** +Uninstall the socket service + +This routine removes the gEfiSocketServiceBindingProtocolGuid from +the SocketDxe image handle to notify EFI that the socket layer +is no longer available. + +SocketDxe's DriverUnload routine calls this routine to remove the +socket layer. + +@param [in] ImageHandle Handle for the image. + +@retval EFI_SUCCESS Service installed successfully +**/ +EFI_STATUS +EFIAPI +EslDxeUninstall ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + + // + // Install the socket service binding protocol + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiSocketServiceBindingProtocolGuid, + mEslLayer.pServiceBinding, + 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; +} diff --git a/StdLib/EfiSocketLib/EfiSocketLib.inf b/StdLib/EfiSocketLib/EfiSocketLib.inf index 6671e840f2..df639527f6 100644 --- a/StdLib/EfiSocketLib/EfiSocketLib.inf +++ b/StdLib/EfiSocketLib/EfiSocketLib.inf @@ -28,7 +28,9 @@ # [Sources.common] + DxeSupport.c Init.c + Ip4.c Service.c Socket.c Tcp4.c @@ -43,10 +45,13 @@ [LibraryClasses] BaseMemoryLib DebugLib + MemoryAllocationLib UefiBootServicesTableLib UefiLib [Protocols] + gEfiIp4ProtocolGuid + gEfiIp4ServiceBindingProtocolGuid gEfiTcp4ProtocolGuid gEfiTcp4ServiceBindingProtocolGuid gEfiUdp4ProtocolGuid diff --git a/StdLib/EfiSocketLib/Init.c b/StdLib/EfiSocketLib/Init.c index f444acc742..88d2065c21 100644 --- a/StdLib/EfiSocketLib/Init.c +++ b/StdLib/EfiSocketLib/Init.c @@ -18,7 +18,22 @@ /** EFI Socket Library Constructor - @retval EFI_SUCCESS The initialization was successful + This routine supports an implementation dependent constructor + depending upon whether the library is linked to a socket + application or the SocketDxe driver. The following modules + declare the redirection for the constructor in ::mpfnEslConstructor: + + + The EfiSocketLib.inf file lists ::EslConstructor as the CONSTRUCTOR + in the [Defines] section. As a result, this routine is called by + the ProcessLibraryConstructorList routine of the AutoGen.c module + in the Build directory associated with the socket application or + the SocketDxe driver. + + @retval EFI_SUCCESS The socket layer initialization was successful **/ EFI_STATUS @@ -54,7 +69,22 @@ EslConstructor ( /** EFI Socket Library Destructor - @retval EFI_SUCCESS The shutdown was successful + This routine supports an implementation dependent destructor + depending upon whether the library is linked to a socket + application or the SocketDxe driver. The following modules + declare the redirection for the destructor in ::mpfnEslDestructor: + + + The EfiSocketLib.inf file lists ::EslDestructor as the DESTRUCTOR + in the [Defines] section. As a result, this routine is called by + the ProcessLibraryDestructorList routine of the AutoGen.c module + in the Build directory associated with the socket application or + the SocketDxe driver. + + @retval EFI_SUCCESS The socket layer shutdown was successful **/ EFI_STATUS diff --git a/StdLib/EfiSocketLib/Ip4.c b/StdLib/EfiSocketLib/Ip4.c new file mode 100644 index 0000000000..2d341269cc --- /dev/null +++ b/StdLib/EfiSocketLib/Ip4.c @@ -0,0 +1,1264 @@ +/** @file + Implement the IP4 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" + + +/** + Get the local socket address + + This routine returns the IPv4 address associated with the local + socket. + + This routine is called by ::EslSocketGetLocalAddress to determine the + network address for the SOCK_RAW socket. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pAddress Network address to receive the local system address + +**/ +VOID +EslIp4LocalAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress + ) +{ + struct sockaddr_in * pLocalAddress; + ESL_IP4_CONTEXT * pIp4; + + DBG_ENTER ( ); + + // + // Return the local address + // + pIp4 = &pPort->Context.Ip4; + pLocalAddress = (struct sockaddr_in *)pAddress; + pLocalAddress->sin_family = AF_INET; + CopyMem ( &pLocalAddress->sin_addr, + &pIp4->ModeData.ConfigData.StationAddress.Addr[0], + sizeof ( pLocalAddress->sin_addr )); + + DBG_EXIT ( ); +} + + +/** + Set the local port address. + + This routine sets the local port address. + + This support routine is called by ::EslSocketPortAllocate. + + @param [in] pPort Address of an ESL_PORT 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] bBindTest TRUE = run bind testing + + @retval EFI_SUCCESS The operation was successful + + **/ +EFI_STATUS +EslIp4LocalAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest + ) +{ + EFI_IP4_CONFIG_DATA * pConfig; + CONST struct sockaddr_in * pIpAddress; + CONST UINT8 * pIpv4Address; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Validate the address + // + pIpAddress = (struct sockaddr_in *)pSockAddr; + if ( INADDR_BROADCAST == pIpAddress->sin_addr.s_addr ) { + // + // The local address must not be the broadcast address + // + Status = EFI_INVALID_PARAMETER; + pPort->pSocket->errno = EADDRNOTAVAIL; + } + else { + Status = EFI_SUCCESS; + + // + // Set the local address + // + pIpAddress = (struct sockaddr_in *)pSockAddr; + pIpv4Address = (UINT8 *)&pIpAddress->sin_addr.s_addr; + pConfig = &pPort->Context.Ip4.ModeData.ConfigData; + pConfig->StationAddress.Addr[0] = pIpv4Address[0]; + pConfig->StationAddress.Addr[1] = pIpv4Address[1]; + pConfig->StationAddress.Addr[2] = pIpv4Address[2]; + pConfig->StationAddress.Addr[3] = pIpv4Address[3]; + + // + // Determine if the default address is used + // + pConfig->UseDefaultAddress = (BOOLEAN)( 0 == pIpAddress->sin_addr.s_addr ); + + // + // Display the local address + // + DEBUG (( DEBUG_BIND, + "0x%08x: Port, Local IP4 Address: %d.%d.%d.%d\r\n", + pPort, + pConfig->StationAddress.Addr[0], + pConfig->StationAddress.Addr[1], + pConfig->StationAddress.Addr[2], + pConfig->StationAddress.Addr[3])); + + // + // Set the subnet mask + // + if ( pConfig->UseDefaultAddress ) { + pConfig->SubnetMask.Addr[0] = 0; + pConfig->SubnetMask.Addr[1] = 0; + pConfig->SubnetMask.Addr[2] = 0; + pConfig->SubnetMask.Addr[3] = 0; + } + else { + pConfig->SubnetMask.Addr[0] = 0xff; + pConfig->SubnetMask.Addr[1] = 0xff; + pConfig->SubnetMask.Addr[2] = 0xff; + pConfig->SubnetMask.Addr[3] = 0xff; + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Get the option value + + This routine handles the IPv4 level options. + + The ::EslSocketOptionGet routine calls this routine to retrieve + the IPv4 options one at a time by name. + + @param [in] pSocket Address of an ::ESL_SOCKET structure + @param [in] OptionName Name of the option + @param [out] ppOptionData Buffer to receive address of option value + @param [out] pOptionLength Buffer to receive the option length + + @retval EFI_SUCCESS - Socket data successfully received + + **/ +EFI_STATUS +EslIp4OptionGet ( + IN ESL_SOCKET * pSocket, + IN int OptionName, + OUT CONST void ** __restrict ppOptionData, + OUT socklen_t * __restrict pOptionLength + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume success + // + pSocket->errno = 0; + Status = EFI_SUCCESS; + + // + // Attempt to get the option + // + switch ( OptionName ) { + default: + // + // Option not supported + // + pSocket->errno = ENOPROTOOPT; + Status = EFI_INVALID_PARAMETER; + break; + + case IP_HDRINCL: + *ppOptionData = (void *)pSocket->bIncludeHeader; + *pOptionLength = sizeof ( pSocket->bIncludeHeader ); + break; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Set the option value + + This routine handles the IPv4 level options. + + The ::EslSocketOptionSet routine calls this routine to adjust + the IPv4 options one at a time by name. + + @param [in] pSocket Address of an ::ESL_SOCKET structure + @param [in] OptionName Name of the option + @param [in] pOptionValue Buffer containing the option value + @param [in] OptionLength Length of the buffer in bytes + + @retval EFI_SUCCESS - Option successfully set + + **/ +EFI_STATUS +EslIp4OptionSet ( + IN ESL_SOCKET * pSocket, + IN int OptionName, + IN CONST void * pOptionValue, + IN socklen_t OptionLength + ) +{ + BOOLEAN bTrueFalse; + socklen_t LengthInBytes; + UINT8 * pOptionData; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume success + // + pSocket->errno = 0; + Status = EFI_SUCCESS; + + // + // Determine if the option protocol matches + // + LengthInBytes = 0; + pOptionData = NULL; + switch ( OptionName ) { + default: + // + // Protocol level not supported + // + DEBUG (( DEBUG_INFO | DEBUG_OPTION, "ERROR - Invalid protocol option\r\n" )); + pSocket->errno = ENOTSUP; + Status = EFI_UNSUPPORTED; + break; + + case IP_HDRINCL: + + // + // Validate the option length + // + if ( sizeof ( UINT32 ) == OptionLength ) { + // + // Restrict the input to TRUE or FALSE + // + bTrueFalse = TRUE; + if ( 0 == *(UINT32 *)pOptionValue ) { + bTrueFalse = FALSE; + } + pOptionValue = &bTrueFalse; + + // + // Set the option value + // + pOptionData = (UINT8 *)&pSocket->bIncludeHeader; + LengthInBytes = sizeof ( pSocket->bIncludeHeader ); + } + break; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Free a receive packet + + This routine performs the network specific operations necessary + to free a receive packet. + + This routine is called by ::EslSocketPortCloseTxDone to free a + receive packet. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + @param [in, out] pRxBytes Address of the count of RX bytes + +**/ +VOID +EslIp4PacketFree ( + IN ESL_PACKET * pPacket, + IN OUT size_t * pRxBytes + ) +{ + EFI_IP4_RECEIVE_DATA * pRxData; + DBG_ENTER ( ); + + // + // Account for the receive bytes + // + pRxData = pPacket->Op.Ip4Rx.pRxData; + *pRxBytes -= pRxData->HeaderLength + pRxData->DataLength; + + // + // Disconnect the buffer from the packet + // + pPacket->Op.Ip4Rx.pRxData = NULL; + + // + // Return the buffer to the IP4 driver + // + gBS->SignalEvent ( pRxData->RecycleSignal ); + DBG_EXIT ( ); +} + + +/** + Initialize the network specific portions of an ::ESL_PORT structure. + + This routine initializes the network specific portions of an + ::ESL_PORT structure for use by the socket. + + This support routine is called by ::EslSocketPortAllocate + to connect the socket with the underlying network adapter + running the IPv4 protocol. + + @param [in] pPort Address of an ESL_PORT structure + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslIp4PortAllocate ( + IN ESL_PORT * pPort, + IN UINTN DebugFlags + ) +{ + EFI_IP4_CONFIG_DATA * pConfig; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Initialize the port + // + pSocket = pPort->pSocket; + pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Ip4Tx.TxData ); + pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Ip4Tx.Event ); + pSocket->TxTokenOffset = OFFSET_OF ( EFI_IP4_COMPLETION_TOKEN, Packet.TxData ); + + // + // Save the cancel, receive and transmit addresses + // + pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.IPv4->Configure; + pPort->pfnRxCancel = (PFN_NET_IO_START)pPort->pProtocol.IPv4->Cancel; + pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.IPv4->Receive; + pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.IPv4->Transmit; + + // + // Set the configuration flags + // + pConfig = &pPort->Context.Ip4.ModeData.ConfigData; + pConfig->AcceptIcmpErrors = FALSE; + pConfig->AcceptBroadcast = FALSE; + pConfig->AcceptPromiscuous = FALSE; + pConfig->TypeOfService = 0; + pConfig->TimeToLive = 255; + pConfig->DoNotFragment = FALSE; + pConfig->RawData = FALSE; + pConfig->ReceiveTimeout = 0; + pConfig->TransmitTimeout = 0; + + // + // Set the default protocol + // + pConfig->DefaultProtocol = (UINT8)pSocket->Protocol; + pConfig->AcceptAnyProtocol = (BOOLEAN)( 0 == pConfig->DefaultProtocol ); + Status = EFI_SUCCESS; + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Receive data from a network connection. + + This routine attempts to return buffered data to the caller. The + data is removed from the urgent queue if the message flag MSG_OOB + is specified, otherwise data is removed from the normal queue. + See the \ref ReceiveEngine section. + + This routine is called by ::EslSocketReceive to handle the network + specific receive operation to support SOCK_RAW sockets. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + + @param [in] pbConsumePacket Address of a BOOLEAN indicating if the packet is to be consumed + + @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 [out] pSkipBytes Address to receive the number of bytes skipped + + @return Returns the address of the next free byte in the buffer. + + **/ +UINT8 * +EslIp4Receive ( + IN ESL_PORT * pPort, + IN ESL_PACKET * pPacket, + IN BOOLEAN * pbConsumePacket, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength, + OUT struct sockaddr * pAddress, + OUT size_t * pSkipBytes + ) +{ + size_t DataBytes; + size_t HeaderBytes; + size_t LengthInBytes; + struct sockaddr_in * pRemoteAddress; + EFI_IP4_RECEIVE_DATA * pRxData; + + DBG_ENTER ( ); + + // + // Return the remote system address if requested + // + pRxData = pPacket->Op.Ip4Rx.pRxData; + if ( NULL != pAddress ) { + // + // Build the remote address + // + DEBUG (( DEBUG_RX, + "Getting packet remote address: %d.%d.%d.%d\r\n", + pRxData->Header->SourceAddress.Addr[0], + pRxData->Header->SourceAddress.Addr[1], + pRxData->Header->SourceAddress.Addr[2], + pRxData->Header->SourceAddress.Addr[3])); + pRemoteAddress = (struct sockaddr_in *)pAddress; + CopyMem ( &pRemoteAddress->sin_addr, + &pRxData->Header->SourceAddress.Addr[0], + sizeof ( pRemoteAddress->sin_addr )); + } + + // + // Copy the IP header + // + HeaderBytes = pRxData->HeaderLength; + if ( HeaderBytes > BufferLength ) { + HeaderBytes = BufferLength; + } + DEBUG (( DEBUG_RX, + "0x%08x --> 0x%08x: Copy header 0x%08x bytes\r\n", + pRxData->Header, + pBuffer, + HeaderBytes )); + CopyMem ( pBuffer, pRxData->Header, HeaderBytes ); + pBuffer += HeaderBytes; + LengthInBytes = HeaderBytes; + + // + // Copy the received data + // + if ( 0 < ( BufferLength - LengthInBytes )) { + pBuffer = EslSocketCopyFragmentedBuffer ( pRxData->FragmentCount, + &pRxData->FragmentTable[0], + BufferLength - LengthInBytes, + pBuffer, + &DataBytes ); + LengthInBytes += DataBytes; + } + + // + // Determine if the data is being read + // + if ( *pbConsumePacket ) { + // + // Display for the bytes consumed + // + DEBUG (( DEBUG_RX, + "0x%08x: Port account for 0x%08x bytes\r\n", + pPort, + LengthInBytes )); + + // + // Account for any discarded data + // + *pSkipBytes = pRxData->HeaderLength + pRxData->DataLength - LengthInBytes; + } + + // + // Return the data length and the buffer address + // + *pDataLength = LengthInBytes; + DBG_EXIT_HEX ( pBuffer ); + return pBuffer; +} + + +/** + Get the remote socket address + + This routine returns the address of the remote connection point + associated with the SOCK_RAW socket. + + This routine is called by ::EslSocketGetPeerAddress to detemine + the IPv4 address associated with the network adapter. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pAddress Network address to receive the remote system address + +**/ +VOID +EslIp4RemoteAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress + ) +{ + struct sockaddr_in * pRemoteAddress; + ESL_IP4_CONTEXT * pIp4; + + DBG_ENTER ( ); + + // + // Return the remote address + // + pIp4 = &pPort->Context.Ip4; + pRemoteAddress = (struct sockaddr_in *)pAddress; + pRemoteAddress->sin_family = AF_INET; + CopyMem ( &pRemoteAddress->sin_addr, + &pIp4->DestinationAddress.Addr[0], + sizeof ( pRemoteAddress->sin_addr )); + + DBG_EXIT ( ); +} + + +/** + Set the remote address + + This routine sets the remote address in the port. + + This routine is called by ::EslSocketConnect to specify the + remote network address. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. + + @retval EFI_SUCCESS The operation was successful + + **/ +EFI_STATUS +EslIp4RemoteAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ) +{ + ESL_IP4_CONTEXT * pIp4; + CONST struct sockaddr_in * pRemoteAddress; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the remote address + // + pIp4 = &pPort->Context.Ip4; + pRemoteAddress = (struct sockaddr_in *)pSockAddr; + pIp4->DestinationAddress.Addr[0] = (UINT8)( pRemoteAddress->sin_addr.s_addr ); + pIp4->DestinationAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 ); + pIp4->DestinationAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 ); + pIp4->DestinationAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 ); + Status = EFI_SUCCESS; + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the receive completion + + This routine keeps the IPv4 driver's buffer and queues it in + in FIFO order to the data queue. The IP4 driver's buffer will + be returned by either ::EslIp4Receive or ::EslSocketPortCloseTxDone. + See the \ref ReceiveEngine section. + + This routine is called by the IPv4 driver when data is + received. + + @param [in] Event The receive completion event + + @param [in] pIo The address of an ::ESL_IO_MGMT structure + +**/ +VOID +EslIp4RxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + size_t LengthInBytes; + ESL_PORT * pPort; + ESL_PACKET * pPacket; + EFI_IP4_RECEIVE_DATA * pRxData; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Get the operation status. + // + pPort = pIo->pPort; + Status = pIo->Token.Ip4Rx.Status; + + // + // Get the packet length + // + pRxData = pIo->Token.Ip4Rx.Packet.RxData; + LengthInBytes = pRxData->HeaderLength + pRxData->DataLength; + + // + // +--------------------+ +----------------------+ + // | ESL_IO_MGMT | | Data Buffer | + // | | | (Driver owned) | + // | +---------------+ +----------------------+ + // | | Token | ^ + // | | Rx Event | | + // | | | +----------------------+ + // | | RxData --> | EFI_IP4_RECEIVE_DATA | + // +----+---------------+ | (Driver owned) | + // +----------------------+ + // +--------------------+ ^ + // | ESL_PACKET | . + // | | . + // | +---------------+ . + // | | pRxData --> NULL ....... + // +----+---------------+ + // + // + // Save the data in the packet + // + pPacket = pIo->pPacket; + pPacket->Op.Ip4Rx.pRxData = pRxData; + + // + // Complete this request + // + EslSocketRxComplete ( pIo, Status, LengthInBytes, FALSE ); + DBG_EXIT ( ); +} + + +/** + Determine if the socket is configured. + + This routine uses the flag ESL_SOCKET::bConfigured to determine + if the network layer's configuration routine has been called. + This routine calls the ::EslSocketBind and configuration routines + if they were not already called. After the port is configured, + the \ref ReceiveEngine is started. + + This routine is called by EslSocketIsConfigured to verify + that the socket is configured. + + @param [in] pSocket Address of an ::ESL_SOCKET structure + + @retval EFI_SUCCESS - The port is connected + @retval EFI_NOT_STARTED - The port is not connected + + **/ + EFI_STATUS + EslIp4SocketIsConfigured ( + IN ESL_SOCKET * pSocket + ) +{ + UINTN Index; + ESL_PORT * pPort; + ESL_PORT * pNextPort; + ESL_IP4_CONTEXT * pIp4; + EFI_IP4_PROTOCOL * pIp4Protocol; + 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 = EslSocketBind ( &pSocket->SocketProtocol, + (struct sockaddr *)&LocalAddress, + LocalAddress.sin_len, + &pSocket->errno ); + } + + // + // Walk the port list + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Update the raw setting + // + pIp4 = &pPort->Context.Ip4; + if ( pSocket->bIncludeHeader ) { + // + // IP header will be included with the data on transmit + // + pIp4->ModeData.ConfigData.RawData = TRUE; + } + + // + // Attempt to configure the port + // + pNextPort = pPort->pLinkSocket; + pIp4Protocol = pPort->pProtocol.IPv4; + DEBUG (( DEBUG_TX, + "0x%08x: pPort Configuring for %d.%d.%d.%d --> %d.%d.%d.%d\r\n", + pPort, + pIp4->ModeData.ConfigData.StationAddress.Addr[0], + pIp4->ModeData.ConfigData.StationAddress.Addr[1], + pIp4->ModeData.ConfigData.StationAddress.Addr[2], + pIp4->ModeData.ConfigData.StationAddress.Addr[3], + pIp4->DestinationAddress.Addr[0], + pIp4->DestinationAddress.Addr[1], + pIp4->DestinationAddress.Addr[2], + pIp4->DestinationAddress.Addr[3])); + Status = pIp4Protocol->Configure ( pIp4Protocol, + &pIp4->ModeData.ConfigData ); + if ( !EFI_ERROR ( Status )) { + // + // Update the configuration data + // + Status = pIp4Protocol->GetModeData ( pIp4Protocol, + &pIp4->ModeData, + NULL, + NULL ); + } + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed to configure the Ip4 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_TX, + "0x%08x: pPort Configured for %d.%d.%d.%d --> %d.%d.%d.%d\r\n", + pPort, + pIp4->ModeData.ConfigData.StationAddress.Addr[0], + pIp4->ModeData.ConfigData.StationAddress.Addr[1], + pIp4->ModeData.ConfigData.StationAddress.Addr[2], + pIp4->ModeData.ConfigData.StationAddress.Addr[3], + pIp4->DestinationAddress.Addr[0], + pIp4->DestinationAddress.Addr[1], + pIp4->DestinationAddress.Addr[2], + pIp4->DestinationAddress.Addr[3])); + DEBUG (( DEBUG_TX, + "Subnet Mask: %d.%d.%d.%d\r\n", + pIp4->ModeData.ConfigData.SubnetMask.Addr[0], + pIp4->ModeData.ConfigData.SubnetMask.Addr[1], + pIp4->ModeData.ConfigData.SubnetMask.Addr[2], + pIp4->ModeData.ConfigData.SubnetMask.Addr[3])); + DEBUG (( DEBUG_TX, + "Route Count: %d\r\n", + pIp4->ModeData.RouteCount )); + for ( Index = 0; pIp4->ModeData.RouteCount > Index; Index++ ) { + if ( 0 == Index ) { + DEBUG (( DEBUG_TX, "Route Table:\r\n" )); + } + DEBUG (( DEBUG_TX, + "%5d: %d.%d.%d.%d, %d.%d.%d.%d ==> %d.%d.%d.%d\r\n", + Index, + pIp4->ModeData.RouteTable[Index].SubnetAddress.Addr[0], + pIp4->ModeData.RouteTable[Index].SubnetAddress.Addr[1], + pIp4->ModeData.RouteTable[Index].SubnetAddress.Addr[2], + pIp4->ModeData.RouteTable[Index].SubnetAddress.Addr[3], + pIp4->ModeData.RouteTable[Index].SubnetMask.Addr[0], + pIp4->ModeData.RouteTable[Index].SubnetMask.Addr[1], + pIp4->ModeData.RouteTable[Index].SubnetMask.Addr[2], + pIp4->ModeData.RouteTable[Index].SubnetMask.Addr[3], + pIp4->ModeData.RouteTable[Index].GatewayAddress.Addr[0], + pIp4->ModeData.RouteTable[Index].GatewayAddress.Addr[1], + pIp4->ModeData.RouteTable[Index].GatewayAddress.Addr[2], + pIp4->ModeData.RouteTable[Index].GatewayAddress.Addr[3])); + } + pPort->bConfigured = TRUE; + + // + // Start the first read on the port + // + EslSocketRxStart ( 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 buffers data for the transmit engine in the normal + data queue. When the \ref TransmitEngine has resources, this + routine will start the transmission of the next buffer on the + network connection. + + This routine is called by ::EslSocketTransmit 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 starts 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 an ::ESL_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 +EslIp4TxBuffer ( + IN ESL_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 + ) +{ + ESL_PACKET * pPacket; + ESL_PACKET * pPreviousPacket; + ESL_PORT * pPort; + const struct sockaddr_in * pRemoteAddress; + ESL_IP4_CONTEXT * pIp4; + size_t * pTxBytes; + ESL_IP4_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 + // + pIp4 = &pPort->Context.Ip4; + 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.Ip4Tx ) + - sizeof ( pPacket->Op.Ip4Tx.Buffer ) + + BufferLength, + 0, + DEBUG_TX ); + if ( !EFI_ERROR ( Status )) { + // + // Initialize the transmit operation + // + pTxData = &pPacket->Op.Ip4Tx; + pTxData->TxData.DestinationAddress.Addr[0] = pIp4->DestinationAddress.Addr[0]; + pTxData->TxData.DestinationAddress.Addr[1] = pIp4->DestinationAddress.Addr[1]; + pTxData->TxData.DestinationAddress.Addr[2] = pIp4->DestinationAddress.Addr[2]; + pTxData->TxData.DestinationAddress.Addr[3] = pIp4->DestinationAddress.Addr[3]; + pTxData->TxData.OverrideData = NULL; + pTxData->TxData.OptionsLength = 0; + pTxData->TxData.OptionsBuffer = NULL; + pTxData->TxData.TotalDataLength = (UINT32) BufferLength; + pTxData->TxData.FragmentCount = 1; + pTxData->TxData.FragmentTable[0].FragmentLength = (UINT32) BufferLength; + pTxData->TxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Ip4Tx.Buffer[0]; + + // + // Set the remote system address if necessary + // + if ( NULL != pAddress ) { + pRemoteAddress = (const struct sockaddr_in *)pAddress; + pTxData->Override.SourceAddress.Addr[0] = pIp4->ModeData.ConfigData.StationAddress.Addr[0]; + pTxData->Override.SourceAddress.Addr[1] = pIp4->ModeData.ConfigData.StationAddress.Addr[1]; + pTxData->Override.SourceAddress.Addr[2] = pIp4->ModeData.ConfigData.StationAddress.Addr[2]; + pTxData->Override.SourceAddress.Addr[3] = pIp4->ModeData.ConfigData.StationAddress.Addr[3]; + pTxData->TxData.DestinationAddress.Addr[0] = (UINT8)pRemoteAddress->sin_addr.s_addr; + pTxData->TxData.DestinationAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 ); + pTxData->TxData.DestinationAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 ); + pTxData->TxData.DestinationAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 ); + pTxData->Override.GatewayAddress.Addr[0] = 0; + pTxData->Override.GatewayAddress.Addr[1] = 0; + pTxData->Override.GatewayAddress.Addr[2] = 0; + pTxData->Override.GatewayAddress.Addr[3] = 0; + pTxData->Override.Protocol = (UINT8)pSocket->Protocol; + pTxData->Override.TypeOfService = 0; + pTxData->Override.TimeToLive = 255; + pTxData->Override.DoNotFragment = FALSE; + + // + // Use the remote system address when sending this packet + // + pTxData->TxData.OverrideData = &pTxData->Override; + } + + // + // Copy the data into the buffer + // + CopyMem ( &pPacket->Op.Ip4Tx.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 bytes from 0x%08x, %d.%d.%d.%d --> %d.%d.%d.%d\r\n", + BufferLength, + pBuffer, + pIp4->ModeData.ConfigData.StationAddress.Addr[0], + pIp4->ModeData.ConfigData.StationAddress.Addr[1], + pIp4->ModeData.ConfigData.StationAddress.Addr[2], + pIp4->ModeData.ConfigData.StationAddress.Addr[3], + pTxData->TxData.DestinationAddress.Addr[0], + pTxData->TxData.DestinationAddress.Addr[1], + pTxData->TxData.DestinationAddress.Addr[2], + pTxData->TxData.DestinationAddress.Addr[3])); + + // + // 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 != pPort->pTxFree ) { + EslSocketTxStart ( pPort, + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); + } + } + 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 + + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for data packets. + + This routine is called by the IPv4 network layer when a data + transmit request completes. + + @param [in] Event The normal transmit completion event + + @param [in] pIo The address of an ::ESL_IO_MGMT structure + +**/ +VOID +EslIp4TxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + UINT32 LengthInBytes; + ESL_PORT * pPort; + ESL_PACKET * pPacket; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the active transmit packet + // + pPacket = pIo->pPacket; + pPort = pIo->pPort; + pSocket = pPort->pSocket; + + // + // Get the transmit length and status + // + LengthInBytes = pPacket->Op.Ip4Tx.TxData.TotalDataLength; + pSocket->TxBytes -= LengthInBytes; + Status = pIo->Token.Ip4Tx.Status; + + // + // Complete the transmit operation + // + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "Raw ", + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); + DBG_EXIT ( ); +} + + +/** + Interface between the socket layer and the network specific + code that supports SOCK_RAW sockets over IPv4. +**/ +CONST ESL_PROTOCOL_API cEslIp4Api = { + "IPv4", + IPPROTO_IP, + OFFSET_OF ( ESL_PORT, Context.Ip4.ModeData.ConfigData ), + OFFSET_OF ( ESL_LAYER, pIp4List ), + OFFSET_OF ( struct sockaddr_in, sin_zero ), + sizeof ( struct sockaddr_in ), + AF_INET, + sizeof (((ESL_PACKET *)0 )->Op.Ip4Rx ), + sizeof (((ESL_PACKET *)0 )->Op.Ip4Rx ), + OFFSET_OF ( ESL_IO_MGMT, Token.Ip4Rx.Packet.RxData ), + FALSE, + EADDRNOTAVAIL, + NULL, // Accept + NULL, // ConnectPoll + NULL, // ConnectStart + EslIp4SocketIsConfigured, + EslIp4LocalAddressGet, + EslIp4LocalAddressSet, + NULL, // Listen + EslIp4OptionGet, + EslIp4OptionSet, + EslIp4PacketFree, + EslIp4PortAllocate, + NULL, // PortClose + NULL, // PortCloseOp + TRUE, + EslIp4Receive, + EslIp4RemoteAddressGet, + EslIp4RemoteAddressSet, + EslIp4RxComplete, + NULL, // RxStart + EslIp4TxBuffer, + EslIp4TxComplete, + NULL // TxOobComplete +}; diff --git a/StdLib/EfiSocketLib/ReleaseNotes.txt b/StdLib/EfiSocketLib/ReleaseNotes.txt new file mode 100644 index 0000000000..fd8ed74022 --- /dev/null +++ b/StdLib/EfiSocketLib/ReleaseNotes.txt @@ -0,0 +1,31 @@ +The following issues exist with the EFI Socket Library: + +* Don't run socket applications or the socket driver for a long time - The IPv4Config + and DHCP protocols are not properly running the renew and lease expiration timers. + When the DHCP lease expires it is possible for a duplicate IP address to exist on + the network. HSD 206136 + +* Network adapters must be initialized prior to running the socket application - Static + and dynamic IP address need to be properly assigned to the network adapters on the + system. Note that sockets does not assign the IP addresses since it does not + understand how the system is connected to the network! + +* The default device must contain the Efi\etc directory populated with files containing + the proper network configuration - A template set of files is in StdLib\Efi\etc. Note + that the resolv.conf file contains the set of DNS servers. + +* Since DHCP is not running when the sockets application is running, the last negotiated + packet is no longer available. As a result, any of the options that DHCP did negotiate + are no longer available for sockets such as the list of DNS servers. + +* DHCP does not request the domain name and domain name server (DNS) addresses. This + requires that sockets use configuration files in Efi\etc! + +* TCPv4 transfer rate is slow (< 10 Mbits/sec) - This is an unidentified issue. + +* Raw socket applications are not able to pass the IP header as part of their + payload by using the IP option IP_HDRINCL. This is because the UEFI IPv4 driver + (Ip4Dxe) does not support RawData. HSD 206136 + +* Only version 4 of the UEFI network stack is supported + diff --git a/StdLib/EfiSocketLib/Service.c b/StdLib/EfiSocketLib/Service.c index 49c8884612..927f0e45b4 100644 --- a/StdLib/EfiSocketLib/Service.c +++ b/StdLib/EfiSocketLib/Service.c @@ -14,15 +14,16 @@ #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. + locate any that are not in use. Create ::ESL_SERVICE structures to + manage the network layer interfaces for the socket driver. Tag + each of the network interfaces that are being used. Finally, this + routine calls ESL_SOCKET_BINDING::pfnInitialize to prepare the network + interface for use by the socket layer. @param [in] BindingHandle Handle for protocol binding. @param [in] Controller Handle of device to work with. @@ -40,11 +41,13 @@ EslServiceConnect ( { BOOLEAN bInUse; UINTN LengthInBytes; - CONST DT_SOCKET_BINDING * pEnd; + UINT8 * pBuffer; + CONST ESL_SOCKET_BINDING * pEnd; VOID * pJunk; - VOID * pInterface; - DT_SERVICE * pService; - CONST DT_SOCKET_BINDING * pSocketBinding; + ESL_SERVICE ** ppServiceListHead; + ESL_SERVICE * pService; + CONST ESL_SOCKET_BINDING * pSocketBinding; + EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -68,7 +71,7 @@ EslServiceConnect ( Status = gBS->OpenProtocol ( Controller, pSocketBinding->pNetworkBinding, - &pInterface, + (VOID**)&pServiceBinding, BindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL @@ -108,7 +111,7 @@ EslServiceConnect ( pService->Signature = SERVICE_SIGNATURE; pService->pSocketBinding = pSocketBinding; pService->Controller = Controller; - pService->pInterface = pInterface; + pService->pServiceBinding = pServiceBinding; // // Mark the controller in use @@ -154,9 +157,13 @@ EslServiceConnect ( RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // - // Initialize the service + // Connect the service to the list // - Status = pSocketBinding->pfnInitialize ( pService ); + pBuffer = (UINT8 *)&mEslLayer; + pBuffer = &pBuffer[ pSocketBinding->ServiceListOffset ]; + ppServiceListHead = (ESL_SERVICE **)pBuffer; + pService->pNext = *ppServiceListHead; + *ppServiceListHead = pService; // // Release the socket layer synchronization @@ -253,12 +260,13 @@ EslServiceConnect ( /** - Shutdown the network connections to this controller by removing - NetworkInterfaceIdentifier protocol and closing the DevicePath - and PciIo protocols on Controller. + Shutdown the connections to the network layer by locating the + tags on the network interfaces established by ::EslServiceConnect. + This routine shutdowns any activity on the network interface and + then frees the ::ESL_SERVICE structures. @param [in] BindingHandle Handle for protocol binding. - @param [in] Controller Handle of device to stop driver on. + @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. @@ -272,9 +280,13 @@ EslServiceDisconnect ( IN EFI_HANDLE Controller ) { - CONST DT_SOCKET_BINDING * pEnd; - DT_SERVICE * pService; - CONST DT_SOCKET_BINDING * pSocketBinding; + UINT8 * pBuffer; + CONST ESL_SOCKET_BINDING * pEnd; + ESL_PORT * pPort; + ESL_SERVICE * pPreviousService; + ESL_SERVICE * pService; + ESL_SERVICE ** ppServiceListHead; + CONST ESL_SOCKET_BINDING * pSocketBinding; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -310,9 +322,54 @@ EslServiceDisconnect ( RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // - // Shutdown the service + // Walk the list of ports // - pSocketBinding->pfnShutdown ( pService ); + pPort = pService->pPortList; + while ( NULL != pPort ) { + // + // Remove the port from the port list + // + pPort->pService = NULL; + pService->pPortList = pPort->pLinkService; + + // + // Close the port + // + EslSocketPortCloseStart ( pPort, + TRUE, + DEBUG_POOL | DEBUG_INIT ); + + // + // Set the next port + // + pPort = pService->pPortList; + } + + // + // Remove the service from the service list + // + pBuffer = (UINT8 *)&mEslLayer; + pBuffer = &pBuffer[ pService->pSocketBinding->ServiceListOffset ]; + ppServiceListHead = (ESL_SERVICE **)pBuffer; + pPreviousService = *ppServiceListHead; + if ( pService == pPreviousService ) { + // + // Remove the service from the beginning of the list + // + *ppServiceListHead = pService->pNext; + } + else { + // + // Remove the service from the middle of the list + // + while ( NULL != pPreviousService ) { + if ( pService == pPreviousService->pNext ) { + pPreviousService->pNext = pService->pNext; + break; + } + pPreviousService = pPreviousService->pNext; + } + } // // Release the socket layer synchronization @@ -387,48 +444,6 @@ EslServiceDisconnect ( -/** -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 @@ -441,69 +456,20 @@ EslServiceLoad ( IN EFI_HANDLE ImageHandle ) { - DT_LAYER * pLayer; + ESL_LAYER * pLayer; // // Save the image handle // pLayer = &mEslLayer; + ZeroMem ( pLayer, sizeof ( *pLayer )); 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; + pLayer->pServiceBinding = &mEfiServiceBinding; } @@ -517,13 +483,12 @@ EslServiceUnload ( VOID ) { - DT_LAYER * pLayer; + ESL_LAYER * pLayer; // // Undo the work by ServiceLoad // pLayer = &mEslLayer; pLayer->ImageHandle = NULL; - pLayer->ServiceBinding.CreateChild = NULL; - pLayer->ServiceBinding.DestroyChild = NULL; + pLayer->pServiceBinding = NULL; } diff --git a/StdLib/EfiSocketLib/Socket.c b/StdLib/EfiSocketLib/Socket.c index ebbc8df4d2..42e4689d0b 100644 --- a/StdLib/EfiSocketLib/Socket.c +++ b/StdLib/EfiSocketLib/Socket.c @@ -14,6 +14,446 @@ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + \section DataStructures Data Structures + +
+
+                +-------------+   +-------------+   +-------------+   
+  Service Lists | ::ESL_SERVICE |-->| ESL_SERVICE |-->| ESL_SERVICE |--> NULL (pNext)
+                +-------------+   +-------------+   +-------------+   
+                  ^                       | (pPortList)    |
+    pUdp4List ^   | pTcp4List             |                |
+              |   |                       |                |
+          ^   |   |                       |                |
+ pIp4List |   |   |                       |                |
+        +---------------+                 |                |
+        |   ::ESL_LAYER   |   ::mEslLayer     |                |
+        +---------------+                 |                |
+                  | (pSocketList)         |                |
+    Socket List   V                       V                V
+                +-------------+   +-------------+   +-------------+   
+                | ::ESL_SOCKET  |-->|   ::ESL_PORT  |-->|   ESL_PORT  |--> NULL (pLinkSocket)
+                +-------------+   +-------------+   +-------------+   
+                  |                       |                |
+                  |                       |                V
+                  V                       V               NULL
+                +-------------+   +-------------+         
+                | ESL_SOCKET  |-->|   ESL_PORT  |--> NULL
+                +-------------+   +-------------+
+                  |    | | | |            |
+                  V    | | | |            V
+                 NULL  | | | |           NULL
+               (pNext) | | | |     (pLinkService)
+                       | | | |                                     pRxPacketListHead
+                       | | | `-----------------------------------------------.
+                       | | |                     pRxOobPacketListHead        |
+                       | | `--------------------------------.                |
+                       | |      pTxPacketListHead           |                |
+                       | `---------------.                  |                |
+  pTxOobPacketListHead |                 |                  |                |
+                       V                 V                  V                V
+                  +------------+    +------------+    +------------+    +------------+
+                  | ::ESL_PACKET |    | ESL_PACKET |    | ESL_PACKET |    | ESL_PACKET |
+                  +------------+    +------------+    +------------+    +------------+
+                         |                 |                |                |
+                         V                 V                V                V
+                  +------------+    +------------+    +------------+    +------------+
+                  | ESL_PACKET |    | ESL_PACKET |    | ESL_PACKET |    | ESL_PACKET |
+                  +------------+    +------------+    +------------+    +------------+
+                         |                 |                |                |
+                         V                 V                V                V
+                        NULL              NULL             NULL             NULL
+                       (pNext)
+
+  
+ + ::mEslLayer is the one and only ::ESL_LAYER structure. It connects directly or + indirectly to the other data structures. The ESL_LAYER structure has a unique + service list for each of the network protocol interfaces. + + ::ESL_SERVICE manages the network interfaces for a given transport type (IP4, TCP4, UDP4, etc.) + + ::ESL_SOCKET manages the activity for a single socket instance. As such, it contains + the ::EFI_SOCKET_PROTOCOL structure which the BSD socket library uses as the object + reference and the API into the EFI socket library. + + ::ESL_PORT manages the connection with a single instance of the lower layer network. + This structure is the socket equivalent of an IP connection or a TCP or UDP port. + + ::ESL_PACKET buffers data for transmit and receive. There are four queues connected + to the ::ESL_SOCKET that manage the data: + + The selection of the transmit queue is controlled by the MSG_OOB flag on the transmit + request as well as the socket option SO_OOBINLINE. The receive queue is selected by + the URGENT data flag for TCP and the setting of the socket option SO_OOBINLINE. + + Data structure synchronization is done by raising TPL to TPL_SOCKET. Modifying + critical elements within the data structures must be done at this TPL. TPL is then + restored to the previous level. Note that the code verifies that all callbacks are + entering at TPL_SOCKETS for proper data structure synchronization. + + \section PortCloseStateMachine Port Close State Machine + + The port close state machine walks the port through the necessary + states to stop activity on the port and get it into a state where + the resources may be released. The state machine consists of the + following arcs and states: + +
+
+      +--------------------------+
+      |          Open            |
+      +--------------------------+
+                   |
+                   |  ::EslSocketPortCloseStart
+                   V
+      +--------------------------+
+      | PORT_STATE_CLOSE_STARTED |
+      +--------------------------+
+                   |
+                   |  ::EslSocketPortCloseTxDone
+                   V
+      +--------------------------+
+      | PORT_STATE_CLOSE_TX_DONE |
+      +--------------------------+
+                   |
+                   |  ::EslSocketPortCloseComplete
+                   V
+      +--------------------------+
+      |  PORT_STATE_CLOSE_DONE   |
+      +--------------------------+
+                   |
+                   |  ::EslSocketPortCloseRxDone
+                   V
+      +--------------------------+
+      | PORT_STATE_CLOSE_RX_DONE |
+      +--------------------------+
+                   |
+                   |  ::EslSocketPortClose
+                   V
+      +--------------------------+
+      |          Closed          |
+      +--------------------------+
+
+  
+ + + + + \section ReceiveEngine Receive Engine + + The receive path accepts data from the network and queues (buffers) it for the + application. Flow control is applied once a maximum amount of buffering is reached + and is released when the buffer usage drops below that limit. Eventually the + application requests data from the socket which removes entries from the queue and + returns the data. + + The receive engine is the state machine which reads data from the network and + fills the queue with received packets. The receive engine uses two data structures + to manage the network receive opeations and the buffers. + + At a high level, the ::ESL_IO_MGMT structures are managing the tokens and + events for the interface to the UEFI network stack. The ::ESL_PACKET + structures are managing the receive data buffers. The receive engine + connects these two structures in the network specific receive completion + routines. + +
+
+      +------------------+
+      |     ::ESL_PORT     |
+      |                  |
+      +------------------+
+      |    ::ESL_IO_MGMT   |
+      +------------------+
+      |    ESL_IO_MGMT   |
+      +------------------+
+      .                  .
+      .    ESL_IO_MGMT   .
+      .                  .
+      +------------------+
+
+
+ + The ::ESL_IO_MGMT structures are allocated as part of the ::ESL_PORT structure in + ::EslSocketPortAllocate. The ESL_IO_MGMT structures are separated and placed on + the free list by calling ::EslSocketIoInit. The ESL_IO_MGMT structure contains + the network layer specific receive completion token and event. The receive engine + is eventually shutdown by ::EslSocketPortCloseTxDone and the resources in these + structures are released in ::EslSocketPortClose by a call to ::EslSocketIoFree. + +
+
+         pPort->pRxActive
+                |
+                V
+          +-------------+   +-------------+   +-------------+   
+  Active  | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL
+          +-------------+   +-------------+   +-------------+   
+
+          +-------------+   +-------------+   +-------------+   
+  Free    | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL
+          +-------------+   +-------------+   +-------------+   
+                ^
+                |
+          pPort->pRxFree
+
+ + The receive engine is started by calling ::EslSocketRxStart. Flow control pauses + the receive engine by stopping the calls to EslSocketRxStart when the amount of + receive data waiting for the application meets or exceeds MAX_RX_DATA. After + the application reads enough data that the amount of buffering drops below this + limit, the calls to EslSockeRxStart continue which releases the flow control. + + Receive flow control is applied when the port is created, since no receive + operation are pending to the low layer network driver. The flow control gets + released when the low layer network port is configured or the first receive + operation is posted. Flow control remains in the released state until the + maximum buffer space is consumed. During this time, ::EslSocketRxComplete + calls ::EslSocketRxStart. Flow control is applied in EslSocketRxComplete + by skipping the call to EslSocketRxStart. Flow control is eventually + released in ::EslSocketReceive when the buffer space drops below the + maximum amount causing EslSocketReceive to call EslSocketRxStart. + +
+
+                    +------------+   +------------+   
+    High     .----->| ESL_PACKET |-->| ESL_PACKET |--> NULL (pNext)
+  Priority   |      +------------+   +------------+
+             |
+             | pRxOobPacketListHead
+       +------------+
+       | ::ESL_SOCKET |
+       +------------+
+             | pRxPacketListHead
+    Low      |
+  Priority   |      +------------+   +------------+   +------------+   
+             `----->| ::ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL
+                    +------------+   +------------+   +------------+   
+
+
+ + ::EslSocketRxStart connects an ::ESL_PACKET structure to the ::ESL_IO_MGMT structure + and then calls the network layer to start the receive operation. Upon + receive completion, ::EslSocketRxComplete breaks the connection between these + structrues and places the ESL_IO_MGMT structure onto the ESL_PORT::pRxFree list to + make token and event available for another receive operation. EslSocketRxComplete + then queues the ESL_PACKET structure (data packet) to either the + ESL_SOCKET::pRxOobPacketListTail or ESL_SOCKET::pRxPacketListTail depending on + whether urgent or normal data was received. Finally ::EslSocketRxComplete attempts + to start another receive operation. + +
+
+  Setup for IP4 and UDP4
+
+      +--------------------+
+      | ESL_IO_MGMT        |
+      |                    |
+      |    +---------------+
+      |    | Token         |
+      |    |        RxData --> NULL
+      +----+---------------+
+         |
+         V
+      +--------------------+
+      | ESL_PACKET         |
+      |                    |
+      |    +---------------+
+      |    |       pRxData --> NULL
+      +----+---------------+
+
+  Completion for IP4 and UDP4
+
+      +--------------------+   +----------------------+
+      | ESL_IO_MGMT        |   |      Data Buffer     |
+      |                    |   |     (Driver owned)   |
+      |    +---------------+   +----------------------+
+      |    | Token         |               ^
+      |    |      Rx Event |               |
+      |    |               |   +----------------------+
+      |    |        RxData --> | EFI_IP4_RECEIVE_DATA |
+      +----+---------------+   |    (Driver owned)    |
+         |                     +----------------------+
+         V                                 ^
+      +--------------------+               .
+      | ESL_PACKET         |               .
+      |                    |               .
+      |    +---------------+               .
+      |    |       pRxData --> NULL  .......
+      +----+---------------+
+
+
+  Setup and completion for TCP4
+
+      +--------------------+   +--------------------------+
+      | ESL_IO_MGMT        |-->| ESL_PACKET               |
+      |                    |   |                          |
+      |    +---------------+   +----------------------+   |
+      |    | Token         |   | EFI_IP4_RECEIVE_DATA |   |
+      |    |        RxData --> |                      |   |
+      |    |               |   +----------------------+---+
+      |    |        Event  |   |       Data Buffer        |
+      +----+---------------+   |                          |
+                               |                          |
+                               +--------------------------+
+
+
+ + To minimize the number of buffer copies, the data is not copied until the + application makes a receive call. At this point socket performs a single copy + in the receive path to move the data from the buffer filled by the network layer + into the application's buffer. + + The IP4 and UDP4 drivers go one step further to reduce buffer copies. They + allow the socket layer to hold on to the actual receive buffer until the + application has performed a receive operation or closes the socket. Both + of theses operations return the buffer to the lower layer network driver + by calling ESL_PROTOCOL_API::pfnPacketFree. + + When a socket application wants to receive data it indirectly calls + ::EslSocketReceive to remove data from one of the receive data queues. This routine + removes the next available packet from ESL_SOCKET::pRxOobPacketListHead or + ESL_SOCKET::pRxPacketListHead and copies the data from the packet + into the application's buffer. For SOCK_STREAM sockets, if the packet + contains more data then the ESL_PACKET structures remains at the head of the + receive queue for the next application receive + operation. For SOCK_DGRAM, SOCK_RAW and SOCK_SEQ_PACKET sockets, the ::ESL_PACKET + structure is removed from the head of the receive queue and any remaining data is + discarded as the packet is placed on the free queue. + + During socket layer shutdown, ::EslSocketShutdown calls ::EslSocketRxCancel to + cancel any pending receive operations. EslSocketRxCancel calls the network specific + cancel routine using ESL_PORT::pfnRxCancel. + + + \section TransmitEngine Transmit Engine + + Application calls to ::EslSocketTransmit cause data to be copied into a buffer. + The buffer exists as an extension to an ESL_PACKET structure and the structure + is placed at the end of the transmit queue. + +
+
+     *ppQueueHead: pSocket->pRxPacketListHead or pSocket->pRxOobPacketListHead
+          |
+          V
+        +------------+   +------------+   +------------+   
+  Data  | ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL
+        +------------+   +------------+   +------------+   
+                                                     ^
+                                                     |
+     *ppQueueTail: pSocket->pRxPacketListTail or pSocket->pRxOobPacketListTail
+
+
+ + There are actually two transmit queues the normal or low priority queue which is + the default and the urgent or high priority queue which is addressed by specifying + the MSG_OOB flag during the transmit request. Associated with each queue is a + transmit engine which is responsible for sending the data in that queue. + + The transmit engine is the state machine which removes entries from the head + of the transmit queue and causes the data to be sent over the network. + +
+
+      +--------------------+   +--------------------+
+      | ESL_IO_MGMT        |   | ESL_PACKET         |
+      |                    |   |                    |
+      |    +---------------+   +----------------+   |
+      |    | Token         |   | Buffer Length  |   |
+      |    |        TxData --> | Buffer Address |   |
+      |    |               |   +----------------+---+
+      |    |        Event  |   | Data Buffer        |
+      +----+---------------+   |                    |
+                               +--------------------+
+
+ + At a high level, the transmit engine uses a couple of data structures + to manage the data flow. The ::ESL_IO_MGMT structures manage the tokens and + events for the interface to the UEFI network stack. The ::ESL_PACKET + structures manage the data buffers that get sent. The transmit + engine connects these two structures prior to transmission and disconnects + them upon completion. + +
+
+         pPort->pTxActive or pTxOobActive
+                |
+                V
+          +-------------+   +-------------+   +-------------+   
+  Active  | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL
+          +-------------+   +-------------+   +-------------+   
+
+          +-------------+   +-------------+   +-------------+   
+  Free    | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL
+          +-------------+   +-------------+   +-------------+   
+                ^
+                |
+          pPort->pTxFree or pTxOobFree
+
+
+ + The transmit engine manages multiple transmit operations using the + active and free lists shown above. ::EslSocketPortAllocate allocates the + ::ESL_IO_MGMT structures as an extension to the ::ESL_PORT structure. + This routine places the ESL_IO_MGMT structures on the free list by calling + ::EslSocketIoInit. During their lifetime, the ESL_IO_MGMT structures + will move from the free list to the active list and back again. The + active list contains the packets that are actively being processed by + the UEFI network stack. Eventually the ESL_IO_MGMT structures will be + removed from the free list and be deallocated by the EslSocketPortClose + routine. + + The network specific code calls the ::EslSocketTxStart routine + to hand a packet to the network stack. EslSocketTxStart connects + the transmit packet (::ESL_PACKET) to an ::ESL_IO_MGMT structure + and then queues the result to one of the active lists: + ESL_PORT::pTxActive or ESL_PORT::pTxOobActive. The routine then + hands the packet to the network stack. + + Upon completion, the network specific TxComplete routine calls + ::EslSocketTxComplete to disconnect the transmit packet from the + ESL_IO_MGMT structure and frees the ::ESL_PACKET structure by calling + ::EslSocketPacketFree. The routine places the ::ESL_IO_MGMT structure + into the free list either ESL_PORT::pTxFree or ESL_PORT::pTxOobFree. + EslSocketTxComplete then starts the next transmit operation while + the socket is active or calls the ::EslSocketPortCloseTxDone routine + when the socket is shutting down. + **/ #include "Socket.h" @@ -24,62 +464,92 @@ List the network stack connection points for the socket driver. **/ -CONST DT_SOCKET_BINDING cEslSocketBinding [] = { +CONST ESL_SOCKET_BINDING cEslSocketBinding[] = { + { L"Ip4", + &gEfiIp4ServiceBindingProtocolGuid, + &gEfiIp4ProtocolGuid, + &mEslIp4ServiceGuid, + OFFSET_OF ( ESL_LAYER, pIp4List ), + 4, // RX buffers + 4, // TX buffers + 0 }, // TX Oob buffers { L"Tcp4", &gEfiTcp4ServiceBindingProtocolGuid, + &gEfiTcp4ProtocolGuid, &mEslTcp4ServiceGuid, - EslTcpInitialize4, - EslTcpShutdown4 }, + OFFSET_OF ( ESL_LAYER, pTcp4List ), + 4, // RX buffers + 4, // TX buffers + 4 }, // TX Oob buffers { L"Udp4", &gEfiUdp4ServiceBindingProtocolGuid, + &gEfiUdp4ProtocolGuid, &mEslUdp4ServiceGuid, - EslUdpInitialize4, - EslUdpShutdown4 } + OFFSET_OF ( ESL_LAYER, pUdp4List ), + 4, // RX buffers + 4, // TX buffers + 0 } // TX Oob buffers }; CONST UINTN cEslSocketBindingEntries = DIM ( cEslSocketBinding ); -DT_LAYER mEslLayer; +/** + APIs to support the various socket types for the v4 network stack. +**/ +CONST ESL_PROTOCOL_API * cEslAfInetApi[] = { + NULL, // 0 + &cEslTcp4Api, // SOCK_STREAM + &cEslUdp4Api, // SOCK_DGRAM + &cEslIp4Api, // SOCK_RAW + NULL, // SOCK_RDM + &cEslTcp4Api // SOCK_SEQPACKET +}; + +/** + Number of entries in the v4 API array ::cEslAfInetApi. +**/ +CONST int cEslAfInetApiSize = DIM ( cEslAfInetApi ); + + +/** + APIs to support the various socket types for the v6 network stack. +**/ +CONST ESL_PROTOCOL_API * cEslAfInet6Api[] = { + NULL, // 0 + NULL, // SOCK_STREAM + NULL, // SOCK_DGRAM + NULL, // SOCK_RAW + NULL, // SOCK_RDM + NULL // SOCK_SEQPACKET +}; + +/** + Number of entries in the v6 API array ::cEslAfInet6Api. +**/ +CONST int cEslAfInet6ApiSize = DIM ( cEslAfInet6Api ); + + +/** + Global management structure for the socket layer. +**/ +ESL_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. + This routine initializes the communication endpoint. + + The ::socket routine calls this routine indirectly to create + the communication endpoint. @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: - - + application. See the ::socket documentation for values. + @param [in] type Specifies how to make the network connection. + See the ::socket documentation for values. + @param [in] protocol Specifies the lower layer protocol to use. + See the ::socket documentation for values. @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Socket successfully created @@ -97,7 +567,11 @@ EslSocket ( IN int * pErrno ) { - DT_SOCKET * pSocket; + CONST ESL_PROTOCOL_API * pApi; + CONST ESL_PROTOCOL_API ** ppApiArray; + CONST ESL_PROTOCOL_API ** ppApiArrayEnd; + int ApiArraySize; + ESL_SOCKET * pSocket; EFI_STATUS Status; int errno; @@ -129,15 +603,29 @@ EslSocket ( // Validate the domain value // if (( AF_INET != domain ) - && ( AF_LOCAL != domain )) - { + && ( AF_LOCAL != domain )) { DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, - "ERROR - Invalid domain value" )); + "ERROR - Invalid domain value\r\n" )); Status = EFI_INVALID_PARAMETER; errno = EAFNOSUPPORT; break; } + // + // Determine the protocol APIs + // + ppApiArray = NULL; + ApiArraySize = 0; + if (( AF_INET == domain ) + || ( AF_LOCAL == domain )) { + ppApiArray = &cEslAfInetApi[0]; + ApiArraySize = cEslAfInetApiSize; + } + else { + ppApiArray = &cEslAfInet6Api[0]; + ApiArraySize = cEslAfInet6ApiSize; + } + // // Set the default type if necessary // @@ -148,46 +636,81 @@ EslSocket ( // // 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 ) { + if (( type >= ApiArraySize ) + || ( NULL == ppApiArray ) + || ( NULL == ppApiArray[ type ])) { + DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, + "ERROR - Invalid type value\r\n" )); // - // Set the default protocol if necessary + // The socket type is not supported // - if ( 0 == protocol ) { - protocol = IPPROTO_UDP; - } - } - else { - DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, - "ERROR - Invalid type value" )); Status = EFI_INVALID_PARAMETER; - errno = EINVAL; + errno = EPROTOTYPE; break; } + // + // Set the default protocol if necessary + // + pApi = ppApiArray[ type ]; + if ( 0 == protocol ) { + protocol = pApi->DefaultProtocol; + } + // // Validate the protocol value // - if (( IPPROTO_TCP != protocol ) - && ( IPPROTO_UDP != protocol )) { - DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, - "ERROR - Invalid protocol value" )); + if (( pApi->DefaultProtocol != protocol ) + && ( SOCK_RAW != type )) { Status = EFI_INVALID_PARAMETER; - errno = EINVAL; + + // + // Assume that the driver supports this protocol + // + ppApiArray = &cEslAfInetApi[0]; + ppApiArrayEnd = &ppApiArray [ cEslAfInetApiSize ]; + while ( ppApiArrayEnd > ppApiArray ) { + pApi = *ppApiArray; + if ( protocol == pApi->DefaultProtocol ) { + break; + } + ppApiArray += 1; + } + if ( ppApiArrayEnd <= ppApiArray ) { + // + // Verify against the IPv6 table + // + ppApiArray = &cEslAfInet6Api[0]; + ppApiArrayEnd = &ppApiArray [ cEslAfInet6ApiSize ]; + while ( ppApiArrayEnd > ppApiArray ) { + pApi = *ppApiArray; + if ( protocol == pApi->DefaultProtocol ) { + break; + } + ppApiArray += 1; + } + } + if ( ppApiArrayEnd <= ppApiArray ) { + DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, + "ERROR - The protocol is not supported!\r\n" )); + errno = EPROTONOSUPPORT; + break; + } + + // + // The driver does not support this protocol + // + DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, + "ERROR - The protocol does not support this socket type!\r\n" )); + errno = EPROTONOSUPPORT; + errno = EPROTOTYPE; break; } // // Save the socket attributes // + pSocket->pApi = pApi; pSocket->Domain = domain; pSocket->Type = type; pSocket->Protocol = protocol; @@ -212,11 +735,16 @@ EslSocket ( /** 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. + This routine calls the network specific layer to remove the next + connection from the FIFO. - @param [in] pSocketProtocol Address of the socket protocol structure. + The ::accept calls this routine to poll for a network + connection to the socket. When a connection is available + this routine returns the ::EFI_SOCKET_PROTOCOL structure address + associated with the new socket and the remote network address + if requested. + + @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] pSockAddr Address of a buffer to receive the remote network address. @@ -225,8 +753,9 @@ EslSocket ( 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] ppSocketProtocol Address of a buffer to receive the + ::EFI_SOCKET_PROTOCOL instance + associated with the new socket. @param [out] pErrno Address to receive the errno value upon completion. @@ -243,8 +772,8 @@ EslSocketAccept ( IN int * pErrno ) { - DT_SOCKET * pNewSocket; - DT_SOCKET * pSocket; + ESL_SOCKET * pNewSocket; + ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -264,134 +793,122 @@ EslSocketAccept ( pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // - // Validate the sockaddr + // Verify the API // - if (( NULL != pSockAddr ) - && ( NULL == pSockAddrLength )) { - DEBUG (( DEBUG_ACCEPT, - "ERROR - pSockAddr is NULL!\r\n" )); - Status = EFI_INVALID_PARAMETER; - pSocket->errno = EFAULT; + if ( NULL == pSocket->pApi->pfnAccept ) { + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTSUP; } else { // - // Synchronize with the socket layer - // - RAISE_TPL ( TplPrevious, TPL_SOCKETS ); - - // - // Verify that the socket is in the listen state + // Validate the sockaddr // - if ( SOCKET_STATE_LISTENING != pSocket->State ) { + if (( NULL != pSockAddr ) + && ( NULL == pSockAddrLength )) { DEBUG (( DEBUG_ACCEPT, - "ERROR - Socket is not listening!\r\n" )); - Status = EFI_NOT_STARTED; - pSocket->errno = EOPNOTSUPP; + "ERROR - pSockAddr is NULL!\r\n" )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EFAULT; } else { // - // Determine if a socket is available + // Synchronize with the socket layer // - 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; + 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" )); + if ( NULL == pSocket->pApi->pfnAccept ) { // - // Update the socket state + // Socket does not support listen // - pSocket->State = SOCKET_STATE_NO_PORTS; + pSocket->errno = EOPNOTSUPP; + Status = EFI_UNSUPPORTED; } else { // - // Ports are available - // No connection requests at this time + // Socket supports listen, but not in listen state // - Status = EFI_NOT_READY; - pSocket->errno = EAGAIN; + pSocket->errno = EINVAL; + Status = EFI_NOT_STARTED; } } else { - // - // Get the remote network address + // Determine if a socket is available // - 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: + if ( 0 == pSocket->FifoDepth ) { // - // Determine the connection point within the network stack + // No connections available + // Determine if any ports are available // - 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; - */ + if ( NULL == pSocket->pPortList ) { + // + // No ports available + // + Status = EFI_DEVICE_ERROR; + pSocket->errno = EINVAL; + + // + // Update the socket state + // + pSocket->State = SOCKET_STATE_NO_PORTS; } - break; - } - if ( !EFI_ERROR ( Status )) { - // - // Remove the new socket from the list - // - pSocket->pFifoHead = pNewSocket->pNextConnection; - if ( NULL == pSocket->pFifoHead ) { - pSocket->pFifoTail = NULL; + else { + // + // Ports are available + // No connection requests at this time + // + Status = EFI_NOT_READY; + pSocket->errno = EAGAIN; } + } + else { // - // Account for this socket + // Attempt to accept the connection and + // get the remote network address // - pSocket->FifoDepth -= 1; + pNewSocket = pSocket->pFifoHead; + ASSERT ( NULL != pNewSocket ); + Status = pSocket->pApi->pfnAccept ( pNewSocket, + pSockAddr, + pSockAddrLength ); + if ( !EFI_ERROR ( Status )) { + // + // Remove the new socket from the list + // + pSocket->pFifoHead = pNewSocket->pNextConnection; + if ( NULL == pSocket->pFifoHead ) { + pSocket->pFifoTail = NULL; + } - // - // Update the new socket's state - // - pNewSocket->State = SOCKET_STATE_CONNECTED; - pNewSocket->bConfigured = TRUE; - DEBUG (( DEBUG_ACCEPT, - "0x%08x: Socket connected\r\n", - pNewSocket )); + // + // 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 ); + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } } } @@ -410,10 +927,9 @@ EslSocketAccept ( if ( NULL != pSocket ) { *pErrno = pSocket->errno; } - else - { + else { Status = EFI_INVALID_PARAMETER; - *pErrno = EBADF; + *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); @@ -422,9 +938,9 @@ EslSocketAccept ( /** - Allocate and initialize a DT_SOCKET structure. + Allocate and initialize a ESL_SOCKET structure. - The ::SocketAllocate() function allocates a DT_SOCKET structure + This support function allocates an ::ESL_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 @@ -436,11 +952,11 @@ EslSocketAccept ( 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. + @param [in, out] ppSocket The buffer to receive an ::ESL_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 + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create the child @retval other The child handle was not created @@ -450,12 +966,12 @@ EFIAPI EslSocketAllocate ( IN OUT EFI_HANDLE * pChildHandle, IN UINTN DebugFlags, - IN OUT DT_SOCKET ** ppSocket + IN OUT ESL_SOCKET ** ppSocket ) { UINTN LengthInBytes; - DT_LAYER * pLayer; - DT_SOCKET * pSocket; + ESL_LAYER * pLayer; + ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -465,12 +981,8 @@ EslSocketAllocate ( // Create a socket structure // LengthInBytes = sizeof ( *pSocket ); - Status = gBS->AllocatePool ( - EfiRuntimeServicesData, - LengthInBytes, - (VOID **) &pSocket - ); - if ( !EFI_ERROR ( Status )) { + pSocket = (ESL_SOCKET *) AllocateZeroPool ( LengthInBytes ); + if ( NULL != pSocket ) { DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, "0x%08x: Allocate pSocket, %d bytes\r\n", pSocket, @@ -479,8 +991,6 @@ EslSocketAllocate ( // // Initialize the socket protocol // - ZeroMem ( pSocket, LengthInBytes ); - pSocket->Signature = SOCKET_SIGNATURE; pSocket->SocketProtocol.pfnAccept = EslSocketAccept; pSocket->SocketProtocol.pfnBind = EslSocketBind; @@ -494,9 +1004,9 @@ EslSocketAllocate ( 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->SocketProtocol.pfnTransmit = EslSocketTransmit; pSocket->MaxRxBuf = MAX_RX_DATA; pSocket->MaxTxBuf = MAX_TX_DATA; @@ -558,9 +1068,7 @@ EslSocketAllocate ( } } else { - DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL | DEBUG_INIT, - "ERROR - Failed socket allocation, Status: %r\r\n", - Status )); + Status = EFI_OUT_OF_RESOURCES; } // @@ -574,11 +1082,13 @@ EslSocketAllocate ( /** 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. + This routine calls the network specific layer to save the network + address of the local connection point. - @param [in] pSocketProtocol Address of the socket protocol structure. + The ::bind routine calls this routine to connect a name + (network address and port) to a socket on the local machine. + + @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] pSockAddr Address of a sockaddr structure that contains the connection point on the local machine. An IPv4 address @@ -590,7 +1100,7 @@ EslSocketAllocate ( 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 [in] SockAddrLength Specifies the length in bytes of the sockaddr structure. @param [out] pErrno Address to receive the errno value upon completion. @@ -600,12 +1110,18 @@ EslSocketAllocate ( EFI_STATUS EslSocketBind ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, - IN const struct sockaddr * pSockAddr, + IN CONST struct sockaddr * pSockAddr, IN socklen_t SockAddrLength, OUT int * pErrno ) { - DT_SOCKET * pSocket; + EFI_HANDLE ChildHandle; + UINT8 * pBuffer; + ESL_PORT * pPort; + ESL_SERVICE ** ppServiceListHead; + ESL_SOCKET * pSocket; + ESL_SERVICE * pService; + EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -626,91 +1142,130 @@ EslSocketBind ( // // Validate the structure pointer // + pSocket->errno = 0; if ( NULL == pSockAddr ) { DEBUG (( DEBUG_BIND, "ERROR - pSockAddr is NULL!\r\n" )); Status = EFI_INVALID_PARAMETER; pSocket->errno = EFAULT; } - else{ + + // + // Validate the local address length + // + else if ( SockAddrLength < pSocket->pApi->MinimumAddressLength ) { + DEBUG (( DEBUG_BIND, + "ERROR - Invalid bind name length: %d\r\n", + SockAddrLength )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + } + + // + // Validate the shutdown state + // + else if ( pSocket->bRxDisable || pSocket->bTxDisable ) { + DEBUG (( DEBUG_BIND, + "ERROR - Shutdown has been called on socket 0x%08x\r\n", + pSocket )); + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + } + + // + // Verify the socket state + // + else if ( SOCKET_STATE_NOT_CONFIGURED != pSocket->State ) { + DEBUG (( DEBUG_BIND, + "ERROR - The socket 0x%08x is already configured!\r\n", + pSocket )); + pSocket->errno = EINVAL; + Status = EFI_ALREADY_STARTED; + } + else { // - // Validate the name length + // Synchronize with the socket layer // - 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; - } + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); - // - // Synchronize with the socket layer - // - RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + // + // Assume no ports are available + // + pSocket->errno = EADDRNOTAVAIL; + Status = EFI_INVALID_PARAMETER; + // + // Walk the list of services + // + pBuffer = (UINT8 *)&mEslLayer; + pBuffer = &pBuffer[ pSocket->pApi->ServiceListOffset ]; + ppServiceListHead = (ESL_SERVICE **)pBuffer; + pService = *ppServiceListHead; + while ( NULL != pService ) { // - // Validate the local address + // Create the port // - 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; + pServiceBinding = pService->pServiceBinding; + ChildHandle = NULL; + Status = pServiceBinding->CreateChild ( pServiceBinding, + &ChildHandle ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_BIND | DEBUG_POOL, + "0x%08x: %s port handle created\r\n", + ChildHandle, + pService->pSocketBinding->pName )); - case AF_INET: // - // Determine the connection point within the network stack + // Open the port // - 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; + Status = EslSocketPortAllocate ( pSocket, + pService, + ChildHandle, + pSockAddr, + TRUE, + DEBUG_BIND, + &pPort ); + } + else { + DEBUG (( DEBUG_BIND | DEBUG_POOL, + "ERROR - Failed to open %s port handle, Status: %r\r\n", + pService->pSocketBinding->pName, + Status )); } // - // Mark this socket as bound if successful + // Set the next service // - if ( !EFI_ERROR ( Status )) { - pSocket->State = SOCKET_STATE_BOUND; + pService = pService->pNext; + } + + // + // Verify that at least one network connection was found + // + if ( NULL == pSocket->pPortList ) { + if ( EADDRNOTAVAIL == pSocket->errno ) { + DEBUG (( DEBUG_BIND | DEBUG_POOL | DEBUG_INIT, + "ERROR - Socket address is not available!\r\n" )); + } + if ( EADDRINUSE == pSocket->errno ) { + DEBUG (( DEBUG_BIND | DEBUG_POOL | DEBUG_INIT, + "ERROR - Socket address is in use!\r\n" )); } + Status = EFI_INVALID_PARAMETER; + } - // - // Release the socket layer synchronization - // - RESTORE_TPL ( TplPrevious ); + // + // Mark this socket as bound if successful + // + if ( !EFI_ERROR ( Status )) { + pSocket->State = SOCKET_STATE_BOUND; + pSocket->errno = 0; } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); } } @@ -721,10 +1276,9 @@ EslSocketBind ( if ( NULL != pSocket ) { *pErrno = pSocket->errno; } - else - { + else { Status = EFI_INVALID_PARAMETER; - *pErrno = EBADF; + *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); @@ -733,50 +1287,117 @@ EslSocketBind ( /** - Determine if the socket is closed + Test the bind configuration. - 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. + @param [in] pPort Address of the ::ESL_PORT structure. + @param [in] ErrnoValue errno value if test fails - @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 + @retval EFI_SUCCESS The connection was successfully established. + @retval Others The connection attempt failed. -**/ + **/ EFI_STATUS -EslSocketClosePoll ( - IN EFI_SOCKET_PROTOCOL * pSocketProtocol, - IN int * pErrno +EslSocketBindTest ( + IN ESL_PORT * pPort, + IN int ErrnoValue ) { - int errno; - DT_LAYER * pLayer; - DT_SOCKET * pNextSocket; - DT_SOCKET * pSocket; + UINT8 * pBuffer; + VOID * pConfigData; EFI_STATUS Status; - EFI_TPL TplPrevious; DBG_ENTER ( ); // - // Assume success + // Locate the configuration data // - errno = 0; - Status = EFI_SUCCESS; + pBuffer = (UINT8 *)pPort; + pBuffer = &pBuffer [ pPort->pSocket->pApi->ConfigDataOffset ]; + pConfigData = (VOID *)pBuffer; // - // Synchronize with the socket layer + // Attempt to use this configuration // - RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + Status = pPort->pfnConfigure ( pPort->pProtocol.v, pConfigData ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_WARN | DEBUG_BIND, + "WARNING - Port 0x%08x failed configuration, Status: %r\r\n", + pPort, + Status )); + pPort->pSocket->errno = ErrnoValue; + } + else { + // + // Reset the port + // + Status = pPort->pfnConfigure ( pPort->pProtocol.v, NULL ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DEBUG_BIND, + "ERROR - Port 0x%08x failed configuration reset, Status: %r\r\n", + pPort, + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } // - // Locate the socket + // Return the operation status // - pLayer = &mEslLayer; - pNextSocket = pLayer->pSocketList; + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Determine if the socket is closed + + This routine checks the state of the socket to determine if + the network specific layer has completed the close operation. + + The ::close routine polls this routine to determine when the + close operation is complete. The close operation needs to + reverse the operations of the ::EslSocketAllocate routine. + + @param [in] pSocketProtocol Address of an ::EFI_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; + ESL_LAYER * pLayer; + ESL_SOCKET * pNextSocket; + ESL_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 ) { @@ -868,11 +1489,19 @@ EslSocketClosePoll ( /** 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. + This routine calls the network specific layer to initiate the + close state machine. This routine then calls the network + specific layer to determine if the close state machine has gone + to completion. The result from this poll is returned to the + caller. - @param [in] pSocketProtocol Address of the socket protocol structure. + The ::close routine calls this routine to start the close + operation which reverses the operations of the + ::EslSocketAllocate routine. The close routine then polls + the ::EslSocketClosePoll routine to determine when the + socket is closed. + + @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] bCloseNow Boolean to control close behavior @param [out] pErrno Address to receive the errno value upon completion. @@ -890,9 +1519,9 @@ EslSocketCloseStart ( ) { int errno; - DT_PORT * pNextPort; - DT_PORT * pPort; - DT_SOCKET * pSocket; + ESL_PORT * pNextPort; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -928,9 +1557,9 @@ EslSocketCloseStart ( // Start closing the ports // pNextPort = pPort->pLinkSocket; - Status = pPort->pfnCloseStart ( pPort, - bCloseNow, - DEBUG_CLOSE | DEBUG_LISTEN | DEBUG_CONNECTION ); + Status = EslSocketPortCloseStart ( pPort, + bCloseNow, + DEBUG_CLOSE | DEBUG_LISTEN | DEBUG_CONNECTION ); if (( EFI_SUCCESS != Status ) && ( EFI_NOT_READY != Status )) { errno = EIO; @@ -951,8 +1580,8 @@ EslSocketCloseStart ( } } else { - Status = EFI_ALREADY_STARTED; - errno = EALREADY; + Status = EFI_NOT_READY; + errno = EAGAIN; } // @@ -974,28 +1603,16 @@ EslSocketCloseStart ( /** 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. + This routine calls the network specific layer to establish + the remote system address and establish the connection to + the remote system. - 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. + The ::connect routine calls this routine to establish a + connection with the specified remote system. This routine + is designed to be polled by the connect routine for completion + of the network connection. + + @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] pSockAddr Network address of the remote system. @@ -1016,7 +1633,9 @@ EslSocketConnect ( IN int * pErrno ) { - DT_SOCKET * pSocket; + struct sockaddr_in6 LocalAddress; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -1037,12 +1656,10 @@ EslSocketConnect ( // // Validate the name length // - if (( SockAddrLength < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data ))) - || ( pSockAddr->sa_len < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data )))) { + if ( SockAddrLength < ( 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 )); + "ERROR - Invalid bind name length: %d\r\n", + SockAddrLength )); Status = EFI_INVALID_PARAMETER; pSocket->errno = EINVAL; } @@ -1052,13 +1669,6 @@ EslSocketConnect ( // pSocket->errno = 0; - // - // Set the socket address length - // - if ( SockAddrLength > pSockAddr->sa_len ) { - SockAddrLength = pSockAddr->sa_len; - } - // // Synchronize with the socket layer // @@ -1079,112 +1689,111 @@ EslSocketConnect ( case SOCKET_STATE_NOT_CONFIGURED: case SOCKET_STATE_BOUND: // - // Validate the local address + // Validate the address length // - 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: + if ( SockAddrLength >= pSocket->pApi->MinimumAddressLength ) { // - // Determine the connection point within the network stack + // Verify the API // - 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: + if ( NULL == pSocket->pApi->pfnRemoteAddrSet ) { // - // Start the connection processing + // Already connected // - Status = EslTcpConnectStart4 ( pSocket, - pSockAddr, - SockAddrLength ); - + pSocket->errno = ENOTSUP; + Status = EFI_UNSUPPORTED; + } + else { // - // Set the next state if connecting + // Determine if BIND was already called // - if ( EFI_NOT_READY == Status ) { - pSocket->State = SOCKET_STATE_CONNECTING; + if ( NULL == pSocket->pPortList ) { + // + // Allow any local port + // + ZeroMem ( &LocalAddress, sizeof ( LocalAddress )); + LocalAddress.sin6_len = (uint8_t)pSocket->pApi->MinimumAddressLength; + LocalAddress.sin6_family = pSocket->pApi->AddressFamily; + Status = EslSocketBind ( &pSocket->SocketProtocol, + (struct sockaddr *)&LocalAddress, + LocalAddress.sin6_len, + &pSocket->errno ); } - break; + if ( NULL != pSocket->pPortList ) { + // + // Walk the list of ports + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Set the remote address + // + Status = pSocket->pApi->pfnRemoteAddrSet ( pPort, + pSockAddr, + SockAddrLength ); + if ( EFI_ERROR ( Status )) { + break; + } - case SOCK_DGRAM: - Status = EslUdpConnect4 ( pSocket, - pSockAddr, - SockAddrLength ); - break; + // + // Set the next port + // + pPort = pPort->pLinkSocket; + } + + // + // Verify the API + // + if (( !EFI_ERROR ( Status )) + && ( NULL != pSocket->pApi->pfnConnectStart )) { + // + // Initiate the connection with the remote system + // + Status = pSocket->pApi->pfnConnectStart ( pSocket ); + + // + // Set the next state if connecting + // + if ( EFI_NOT_READY == Status ) { + pSocket->State = SOCKET_STATE_CONNECTING; + } + } + } } - break; + } + else { + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid address length: %d\r\n", + SockAddrLength )); + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; } break; case SOCKET_STATE_CONNECTING: // - // Validate the local address + // Poll for connection completion // - 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: + if ( NULL == pSocket->pApi->pfnConnectPoll ) { // - // Determine the connection point within the network stack + // Already connected // - 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 ); + pSocket->errno = EISCONN; + Status = EFI_ALREADY_STARTED; + } + else { + Status = pSocket->pApi->pfnConnectPoll ( 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; - } + // + // 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; @@ -1211,15 +1820,14 @@ EslSocketConnect ( if ( NULL != pSocket ) { *pErrno = pSocket->errno; } - else - { + else { // // Bad socket protocol // DEBUG (( DEBUG_ERROR | DEBUG_CONNECT, "ERROR - pSocketProtocol invalid!\r\n" )); Status = EFI_INVALID_PARAMETER; - *pErrno = EBADF; + *pErrno = ENOTSOCK; } } @@ -1232,213 +1840,96 @@ EslSocketConnect ( /** - 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 + Copy a fragmented buffer into a destination buffer. -**/ -EFI_STATUS -EFIAPI -EslSocketCreateChild ( - IN EFI_SERVICE_BINDING_PROTOCOL * pThis, - IN OUT EFI_HANDLE * pChildHandle - ) -{ - DT_SOCKET * pSocket; - EFI_STATUS Status; + This support routine copies a fragmented buffer to the caller specified buffer. - DBG_ENTER ( ); + This routine is called by ::EslIp4Receive and ::EslUdp4Receive. - // - // Create a socket structure - // - Status = EslSocketAllocate ( pChildHandle, - DEBUG_SOCKET, - &pSocket ); + @param [in] FragmentCount Number of fragments in the table - // - // Return the operation status - // - DBG_EXIT_STATUS ( Status ); - return Status; -} + @param [in] pFragmentTable Address of an EFI_IP4_FRAGMENT_DATA structure + @param [in] BufferLength Length of the the buffer -/** - 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] pBuffer Address of a buffer to receive the data. - @param [in] pThis Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. - @param [in] ChildHandle Handle of the child to destroy + @param [in] pDataLength Number of received data bytes in the buffer. - @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 + @return Returns the address of the next free byte in the buffer. **/ -EFI_STATUS -EFIAPI -EslSocketDestroyChild ( - IN EFI_SERVICE_BINDING_PROTOCOL * pThis, - IN EFI_HANDLE ChildHandle +UINT8 * +EslSocketCopyFragmentedBuffer ( + IN UINT32 FragmentCount, + IN EFI_IP4_FRAGMENT_DATA * pFragmentTable, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength ) { - DT_LAYER * pLayer; - DT_SOCKET * pSocket; - DT_SOCKET * pSocketPrevious; - EFI_SOCKET_PROTOCOL * pSocketProtocol; - EFI_STATUS Status; - EFI_TPL TplPrevious; + size_t BytesToCopy; + UINT32 Fragment; + UINT8 * pBufferEnd; + UINT8 * pData; DBG_ENTER ( ); // - // Locate the socket control structure + // Validate the IP and UDP structures are identical // - 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 ); + ASSERT ( OFFSET_OF ( EFI_IP4_FRAGMENT_DATA, FragmentLength ) + == OFFSET_OF ( EFI_UDP4_FRAGMENT_DATA, FragmentLength )); + ASSERT ( OFFSET_OF ( EFI_IP4_FRAGMENT_DATA, FragmentBuffer ) + == OFFSET_OF ( EFI_UDP4_FRAGMENT_DATA, FragmentBuffer )); + // + // Copy the received data + // + Fragment = 0; + pBufferEnd = &pBuffer [ BufferLength ]; + while (( pBufferEnd > pBuffer ) && ( FragmentCount > Fragment )) { // - // Walk the socket list + // Determine the amount of received data // - 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" )); + pData = pFragmentTable[Fragment].FragmentBuffer; + BytesToCopy = pFragmentTable[Fragment].FragmentLength; + if (((size_t)( pBufferEnd - pBuffer )) < BytesToCopy ) { + BytesToCopy = pBufferEnd - pBuffer; } // - // Release the socket layer synchronization - // - RESTORE_TPL ( TplPrevious ); - - // - // Determine if the socket was found + // Move the data into the buffer // - 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 )); + DEBUG (( DEBUG_RX, + "0x%08x --> 0x%08x: Copy data 0x%08x bytes\r\n", + pData, + pBuffer, + BytesToCopy )); + CopyMem ( pBuffer, pData, BytesToCopy ); + pBuffer += BytesToCopy; + Fragment += 1; } // - // Return the operation status + // Return the data length and the buffer address // - DBG_EXIT_STATUS ( Status ); - return Status; + *pDataLength = BufferLength - ( pBufferEnd - pBuffer ); + DBG_EXIT_HEX ( pBuffer ); + return pBuffer; } /** Get the local address. - @param [in] pSocketProtocol Address of the socket protocol structure. + This routine calls the network specific layer to get the network + address of the local host connection point. + + The ::getsockname routine calls this routine to obtain the network + address associated with the local host connection point. + + @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [out] pAddress Network address to receive the local system address @@ -1457,7 +1948,9 @@ EslSocketGetLocalAddress ( IN int * pErrno ) { - DT_SOCKET * pSocket; + socklen_t LengthInBytes; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -1476,78 +1969,78 @@ EslSocketGetLocalAddress ( pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // - // Verify the address buffer and length address + // Verify the socket state // - if (( NULL != pAddress ) && ( NULL != pAddressLength )) { + Status = EslSocketIsConfigured ( pSocket ); + if ( !EFI_ERROR ( Status )) { // - // Verify the socket state + // Verify the address buffer and length address // - if ( SOCKET_STATE_CONNECTED == pSocket->State ) { - // - // Synchronize with the socket layer - // - RAISE_TPL ( TplPrevious, TPL_SOCKETS ); - + if (( NULL != pAddress ) && ( NULL != pAddressLength )) { // - // Validate the local address + // Verify the socket state // - 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: + if ( SOCKET_STATE_CONNECTED == pSocket->State ) { // - // Determine the connection point within the network stack + // Verify the API // - 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: + if ( NULL == pSocket->pApi->pfnLocalAddrGet ) { + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTSUP; + } + else { // - // Get the local address + // Synchronize with the socket layer // - Status = EslTcpGetLocalAddress4 ( pSocket, - pAddress, - pAddressLength ); - break; + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); - case SOCK_DGRAM: // - // Get the local address + // Verify that there is just a single connection // - Status = EslUdpGetLocalAddress4 ( pSocket, - pAddress, - pAddressLength ); - break; + pPort = pSocket->pPortList; + if (( NULL != pPort ) && ( NULL == pPort->pLinkSocket )) { + // + // Verify the address length + // + LengthInBytes = pSocket->pApi->AddressLength; + if (( LengthInBytes <= *pAddressLength ) + && ( 255 >= LengthInBytes )) { + // + // Return the local address and address length + // + ZeroMem ( pAddress, LengthInBytes ); + pAddress->sa_len = (uint8_t)LengthInBytes; + *pAddressLength = pAddress->sa_len; + pSocket->pApi->pfnLocalAddrGet ( pPort, pAddress ); + pSocket->errno = 0; + Status = EFI_SUCCESS; + } + else { + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + } + } + else { + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); } - break; } - - // - // Release the socket layer synchronization - // - RESTORE_TPL ( TplPrevious ); + else { + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; + } } else { - pSocket->errno = ENOTCONN; - Status = EFI_NOT_STARTED; + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; } } - else { - pSocket->errno = EINVAL; - Status = EFI_INVALID_PARAMETER; - } } // @@ -1557,10 +2050,9 @@ EslSocketGetLocalAddress ( if ( NULL != pSocket ) { *pErrno = pSocket->errno; } - else - { + else { Status = EFI_INVALID_PARAMETER; - *pErrno = EBADF; + *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); @@ -1571,7 +2063,13 @@ EslSocketGetLocalAddress ( /** Get the peer address. - @param [in] pSocketProtocol Address of the socket protocol structure. + This routine calls the network specific layer to get the remote + system connection point. + + The ::getpeername routine calls this routine to obtain the network + address of the remote connection point. + + @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [out] pAddress Network address to receive the remote system address @@ -1590,7 +2088,9 @@ EslSocketGetPeerAddress ( IN int * pErrno ) { - DT_SOCKET * pSocket; + socklen_t LengthInBytes; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -1609,80 +2109,79 @@ EslSocketGetPeerAddress ( pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // - // Verify the address buffer and length address + // Verify the socket state // - if (( NULL != pAddress ) && ( NULL != pAddressLength )) { + Status = EslSocketIsConfigured ( pSocket ); + if ( !EFI_ERROR ( Status )) { // - // Verify the socket state + // Verify the API // - if ( SOCKET_STATE_CONNECTED == pSocket->State ) { - // - // Synchronize with the socket layer - // - RAISE_TPL ( TplPrevious, TPL_SOCKETS ); - + if ( NULL == pSocket->pApi->pfnRemoteAddrGet ) { + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTSUP; + } + else { // - // Validate the local address + // Verify the address buffer and length 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: + if (( NULL != pAddress ) && ( NULL != pAddressLength )) { // - // Determine the connection point within the network stack + // Verify the socket state // - switch ( pSocket->Type ) { - default: - DEBUG (( DEBUG_RX, - "ERROR - Invalid socket type: %d\r\n", - pSocket->Type)); - Status = EFI_INVALID_PARAMETER; - break; + if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); - case SOCK_STREAM: - case SOCK_SEQPACKET: // - // Verify the port state + // Verify that there is just a single connection // - Status = EslTcpGetRemoteAddress4 ( pSocket, - pAddress, - pAddressLength ); - break; + pPort = pSocket->pPortList; + if (( NULL != pPort ) && ( NULL == pPort->pLinkSocket )) { + // + // Verify the address length + // + LengthInBytes = pSocket->pApi->AddressLength; + if ( LengthInBytes <= *pAddressLength ) { + // + // Return the local address + // + ZeroMem ( pAddress, LengthInBytes ); + pAddress->sa_len = (uint8_t)LengthInBytes; + *pAddressLength = pAddress->sa_len; + pSocket->pApi->pfnRemoteAddrGet ( pPort, pAddress ); + pSocket->errno = 0; + Status = EFI_SUCCESS; + } + else { + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + } + } + else { + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; + } - case SOCK_DGRAM: // - // Verify the port state + // Release the socket layer synchronization // - Status = EslUdpGetRemoteAddress4 ( pSocket, - pAddress, - pAddressLength ); - break; + RESTORE_TPL ( TplPrevious ); + } + else { + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; } - 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; + } } } - else { - pSocket->errno = EINVAL; - Status = EFI_INVALID_PARAMETER; - } } - + // // Return the operation status // @@ -1690,10 +2189,9 @@ EslSocketGetPeerAddress ( if ( NULL != pSocket ) { *pErrno = pSocket->errno; } - else - { + else { Status = EFI_INVALID_PARAMETER; - *pErrno = EBADF; + *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); @@ -1702,39 +2200,35 @@ EslSocketGetPeerAddress ( /** - Establish the known port to listen for network connections. + Free the ESL_IO_MGMT event and structure - 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. + This support routine walks the free list to close the event in + the ESL_IO_MGMT structure and remove the structure from the free + list. - @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. + See the \ref TransmitEngine section. - @param [out] pErrno Address to receive the errno value upon completion. + @param [in] pPort Address of an ::ESL_PORT structure + @param [in] ppFreeQueue Address of the free queue head + @param [in] DebugFlags Flags for debug messages + @param [in] pEventName Zero terminated string containing the event name - @retval EFI_SUCCESS - Socket successfully created - @retval Other - Failed to enable the socket for listen + @retval EFI_SUCCESS - The structures were properly initialized **/ EFI_STATUS -EslSocketListen ( - IN EFI_SOCKET_PROTOCOL * pSocketProtocol, - IN INT32 Backlog, - OUT int * pErrno +EslSocketIoFree ( + IN ESL_PORT * pPort, + IN ESL_IO_MGMT ** ppFreeQueue, + IN UINTN DebugFlags, + IN CHAR8 * pEventName ) { - DT_SOCKET * pSocket; + UINT8 * pBuffer; + EFI_EVENT * pEvent; + ESL_IO_MGMT * pIo; + ESL_SOCKET * pSocket; EFI_STATUS Status; - EFI_STATUS TempStatus; - EFI_TPL TplPrevious; DBG_ENTER ( ); @@ -1744,281 +2238,385 @@ EslSocketListen ( Status = EFI_SUCCESS; // - // Validate the socket + // Walk the list of IO structures // - pSocket = NULL; - if ( NULL != pSocketProtocol ) { - pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); - + pSocket = pPort->pSocket; + while ( *ppFreeQueue ) { // - // Assume success + // Free the event for this structure // - pSocket->Status = EFI_SUCCESS; - pSocket->errno = 0; + pIo = *ppFreeQueue; + pBuffer = (UINT8 *)pIo; + pBuffer = &pBuffer[ pSocket->TxTokenEventOffset ]; + pEvent = (EFI_EVENT *)pBuffer; + Status = gBS->CloseEvent ( *pEvent ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the %a event, Status: %r\r\n", + pEventName, + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DebugFlags, + "0x%08x: Closed %a event 0x%08x\r\n", + pIo, + pEventName, + *pEvent )); // - // Verify that the bind operation was successful + // Remove this structure from the queue // - 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; - } + *ppFreeQueue = pIo->pNext; } // // 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 + Initialize the ESL_IO_MGMT structures - Retrieve the socket options one at a time by name. The - POSIX - documentation is available online. + This support routine initializes the ESL_IO_MGMT structure and + places them on to a free list. - @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. + This routine is called by ::EslSocketPortAllocate routines to prepare + the transmit engines. See the \ref TransmitEngine section. - @retval EFI_SUCCESS - Socket data successfully received + @param [in] pPort Address of an ::ESL_PORT structure + @param [in, out] ppIo Address containing the first structure address. Upon + return this buffer contains the next structure address. + @param [in] TokenCount Number of structures to initialize + @param [in] ppFreeQueue Address of the free queue head + @param [in] DebugFlags Flags for debug messages + @param [in] pEventName Zero terminated string containing the event name + @param [in] pfnCompletion Completion routine address - **/ + @retval EFI_SUCCESS - The structures were properly initialized + +**/ 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 +EslSocketIoInit ( + IN ESL_PORT * pPort, + IN ESL_IO_MGMT ** ppIo, + IN UINTN TokenCount, + IN ESL_IO_MGMT ** ppFreeQueue, + IN UINTN DebugFlags, + IN CHAR8 * pEventName, + IN EFI_EVENT_NOTIFY pfnCompletion ) { - int errno; - socklen_t LengthInBytes; - socklen_t MaxBytes; - UINT8 * pOptionData; - DT_SOCKET * pSocket; + ESL_IO_MGMT * pEnd; + EFI_EVENT * pEvent; + ESL_IO_MGMT * pIo; + ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); // - // Assume failure + // Assume success // - errno = EINVAL; - Status = EFI_INVALID_PARAMETER; + Status = EFI_SUCCESS; // - // Validate the socket + // Walk the list of IO structures // - 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; + pSocket = pPort->pSocket; + pIo = *ppIo; + pEnd = &pIo [ TokenCount ]; + while ( pEnd > pIo ) { + // + // Initialize the IO structure + // + pIo->pPort = pPort; + pIo->pPacket = NULL; - case SO_TYPE: - // - // Return the socket type - // - pOptionData = (UINT8 *)&pSocket->Type; - LengthInBytes = sizeof ( pSocket->Type ); - break; - } + // + // Allocate the event for this structure + // + pEvent = (EFI_EVENT *)&(((UINT8 *)pIo)[ pSocket->TxTokenEventOffset ]); + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)pfnCompletion, + pIo, + pEvent ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the %a event, Status: %r\r\n", + pEventName, + Status )); + pSocket->errno = ENOMEM; break; } + DEBUG (( DebugFlags, + "0x%08x: Created %a event 0x%08x\r\n", + pIo, + pEventName, + *pEvent )); // - // Return the option length + // Add this structure to the queue // - *pOptionLength = LengthInBytes; + pIo->pNext = *ppFreeQueue; + *ppFreeQueue = pIo; // - // Return the option value + // Set the next structure // - if ( NULL != pOptionData ) { - // - // Silently truncate the value length - // - if ( LengthInBytes > MaxBytes ) { - LengthInBytes = MaxBytes; + pIo += 1; + } + + // + // Save the next structure + // + *ppIo = pIo; + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Determine if the socket is configured + + This support routine is called to determine if the socket if the + configuration call was made to the network layer. The following + routines call this routine to verify that they may be successful + in their operations: + + + @param [in] pSocket Address of an ::ESL_SOCKET structure + + @retval EFI_SUCCESS - The socket is configured + +**/ +EFI_STATUS +EslSocketIsConfigured ( + IN ESL_SOCKET * pSocket + ) +{ + EFI_STATUS Status; + EFI_TPL TplPrevious; + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Verify the socket state + // + if ( !pSocket->bConfigured ) { + DBG_ENTER ( ); + + // + // Verify the API + // + if ( NULL == pSocket->pApi->pfnIsConfigured ) { + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTSUP; + } + else { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Determine if the socket is configured + // + Status = pSocket->pApi->pfnIsConfigured ( pSocket ); + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + + // + // Set errno if a failure occurs + // + if ( EFI_ERROR ( Status )) { + pSocket->errno = EADDRNOTAVAIL; + } + } + + DBG_EXIT_STATUS ( Status ); + } + + // + // Return the configuration status + // + return Status; +} + + +/** + Establish the known port to listen for network connections. + + This routine calls into the network protocol layer to establish + a handler that is called upon connection completion. The handler + is responsible for inserting the connection into the FIFO. + + The ::listen routine indirectly calls this routine to place the + socket into a state that enables connection attempts. Connections + are placed in a FIFO that is serviced by the application. The + application calls the ::accept (::EslSocketAccept) routine to + remove the next connection from the FIFO and get the associated + socket and address. + + @param [in] pSocketProtocol Address of an ::EFI_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 + ) +{ + ESL_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 ); + + // + // Verify the API + // + if ( NULL == pSocket->pApi->pfnListen ) { + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTSUP; + } + else { + // + // 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; + } + } + + // + // Initiate the connection attempt listen + // + Status = pSocket->pApi->pfnListen ( pSocket ); + + // + // Place the socket in the listen state if successful + // + if ( !EFI_ERROR ( Status )) { + pSocket->State = SOCKET_STATE_LISTENING; + pSocket->bListenCalled = TRUE; + } + 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 = ( SOCKET_STATE_NOT_CONFIGURED == pSocket->State ) ? EDESTADDRREQ + : EINVAL; + Status = EFI_NO_MAPPING; } - CopyMem ( pOptionValue, pOptionData, LengthInBytes ); - errno = 0; - Status = EFI_SUCCESS; } } @@ -2026,7 +2624,13 @@ EslSocketOptionGet ( // Return the operation status // if ( NULL != pErrno ) { - *pErrno = errno; + if ( NULL != pSocket ) { + *pErrno = pSocket->errno; + } + else { + Status = EFI_INVALID_PARAMETER; + *pErrno = ENOTSOCK; + } } DBG_EXIT_STATUS ( Status ); return Status; @@ -2034,74 +2638,137 @@ EslSocketOptionGet ( /** - Set the socket options + Get the socket options - Adjust the socket options one at a time by name. The - POSIX - documentation is available online. + This routine handles the socket level options and passes the + others to the network specific layer. - @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. + The ::getsockopt routine calls this routine to retrieve the + socket options one at a time by name. + + @param [in] pSocketProtocol Address of an ::EFI_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 -EslSocketOptionSet ( +EslSocketOptionGet ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN int level, IN int OptionName, - IN CONST void * pOptionValue, - IN socklen_t OptionLength, + 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; + ESL_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 )) { + if ( NULL == pSocketProtocol ) { + DEBUG (( DEBUG_OPTION, "ERROR - pSocketProtocol is NULL!\r\n" )); + } + else if ( NULL == pOptionValue ) { + DEBUG (( DEBUG_OPTION, "ERROR - No option buffer specified\r\n" )); + } + else if ( NULL == pOptionLength ) { + DEBUG (( DEBUG_OPTION, "ERROR - Option length not specified!\r\n" )); + } + else { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); LengthInBytes = 0; + MaxBytes = *pOptionLength; pOptionData = NULL; switch ( level ) { default: // - // Protocol level not supported + // See if the protocol will handle the option // - errno = ENOTSUP; - Status = EFI_UNSUPPORTED; + if ( NULL != pSocket->pApi->pfnOptionGet ) { + if ( pSocket->pApi->DefaultProtocol == level ) { + Status = pSocket->pApi->pfnOptionGet ( pSocket, + OptionName, + &pOptionData, + &LengthInBytes ); + errno = pSocket->errno; + break; + } + else { + // + // Protocol not supported + // + DEBUG (( DEBUG_OPTION, + "ERROR - The socket does not support this protocol!\r\n" )); + } + } + else { + // + // Protocol level not supported + // + DEBUG (( DEBUG_OPTION, + "ERROR - %a does not support any options!\r\n", + pSocket->pApi->pName )); + } + errno = ENOPROTOOPT; + Status = EFI_INVALID_PARAMETER; break; - + case SOL_SOCKET: switch ( OptionName ) { default: // - // Option not supported + // Socket option not supported // - errno = ENOTSUP; - Status = EFI_UNSUPPORTED; + DEBUG (( DEBUG_INFO | DEBUG_OPTION, "ERROR - Invalid socket option!\r\n" )); + errno = EINVAL; + Status = EFI_INVALID_PARAMETER; break; - + + case SO_ACCEPTCONN: + // + // Return the listen flag + // + pOptionData = (UINT8 *)&pSocket->bListenCalled; + LengthInBytes = sizeof ( pSocket->bListenCalled ); + break; + + case SO_DEBUG: + // + // Return the debug flags + // + pOptionData = (UINT8 *)&pSocket->bOobInLine; + LengthInBytes = sizeof ( pSocket->bOobInLine ); + break; + + case SO_OOBINLINE: + // + // Return the out-of-band inline flag + // + pOptionData = (UINT8 *)&pSocket->bOobInLine; + LengthInBytes = sizeof ( pSocket->bOobInLine ); + break; + case SO_RCVTIMEO: // // Return the receive timeout @@ -2109,104 +2776,1219 @@ EslSocketOptionSet ( pOptionData = (UINT8 *)&pSocket->RxTimeout; LengthInBytes = sizeof ( pSocket->RxTimeout ); break; - + case SO_RCVBUF: // - // Return the maximum transmit buffer size + // Return the maximum receive 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; - } + case SO_TYPE: + // + // Return the socket type + // + pOptionData = (UINT8 *)&pSocket->Type; + LengthInBytes = sizeof ( pSocket->Type ); + break; + } + break; + } + + // + // Return the option length + // + *pOptionLength = LengthInBytes; + + // + // Determine if the option is present + // + if ( 0 != LengthInBytes ) { + // + // Silently truncate the value length + // + if ( LengthInBytes > MaxBytes ) { + DEBUG (( DEBUG_OPTION, + "INFO - Truncating option from %d to %d bytes\r\n", + LengthInBytes, + MaxBytes )); + LengthInBytes = MaxBytes; + } + + // + // Return the value + // + CopyMem ( pOptionValue, pOptionData, LengthInBytes ); + + // + // Zero fill any remaining space + // + if ( LengthInBytes < MaxBytes ) { + ZeroMem ( &((UINT8 *)pOptionValue)[LengthInBytes], MaxBytes - 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 + + This routine handles the socket level options and passes the + others to the network specific layer. + + The ::setsockopt routine calls this routine to adjust the socket + options one at a time by name. + + @param [in] pSocketProtocol Address of an ::EFI_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 - Option successfully set + + **/ +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 + ) +{ + BOOLEAN bTrueFalse; + int errno; + socklen_t LengthInBytes; + UINT8 * pOptionData; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume failure + // + errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + + // + // Validate the socket + // + pSocket = NULL; + if ( NULL == pSocketProtocol ) { + DEBUG (( DEBUG_OPTION, "ERROR - pSocketProtocol is NULL!\r\n" )); + } + else if ( NULL == pOptionValue ) { + DEBUG (( DEBUG_OPTION, "ERROR - No option buffer specified\r\n" )); + } + else + { + pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); + if ( pSocket->bRxDisable || pSocket->bTxDisable ) { + DEBUG (( DEBUG_OPTION, "ERROR - Socket has been shutdown!\r\n" )); + } + else { + LengthInBytes = 0; + pOptionData = NULL; + switch ( level ) { + default: + // + // See if the protocol will handle the option + // + if ( NULL != pSocket->pApi->pfnOptionSet ) { + if ( pSocket->pApi->DefaultProtocol == level ) { + Status = pSocket->pApi->pfnOptionSet ( pSocket, + OptionName, + pOptionValue, + OptionLength ); + errno = pSocket->errno; + break; + } + else { + // + // Protocol not supported + // + DEBUG (( DEBUG_OPTION, + "ERROR - The socket does not support this protocol!\r\n" )); + } + } + else { + // + // Protocol level not supported + // + DEBUG (( DEBUG_OPTION, + "ERROR - %a does not support any options!\r\n", + pSocket->pApi->pName )); + } + errno = ENOPROTOOPT; + Status = EFI_INVALID_PARAMETER; + break; + + case SOL_SOCKET: + switch ( OptionName ) { + default: + // + // Option not supported + // + DEBUG (( DEBUG_OPTION, + "ERROR - Sockets does not support this option!\r\n" )); + errno = EINVAL; + Status = EFI_INVALID_PARAMETER; + break; + + case SO_DEBUG: + // + // Set the debug flags + // + pOptionData = (UINT8 *)&pSocket->bOobInLine; + LengthInBytes = sizeof ( pSocket->bOobInLine ); + break; + + case SO_OOBINLINE: + pOptionData = (UINT8 *)&pSocket->bOobInLine; + LengthInBytes = sizeof ( pSocket->bOobInLine ); + + // + // Validate the option length + // + if ( sizeof ( UINT32 ) == OptionLength ) { + // + // Restrict the input to TRUE or FALSE + // + bTrueFalse = TRUE; + if ( 0 == *(UINT32 *)pOptionValue ) { + bTrueFalse = FALSE; + } + pOptionValue = &bTrueFalse; + } + else { + // + // Force an invalid option length error + // + OptionLength = LengthInBytes - 1; + } + break; + + case SO_RCVTIMEO: + // + // Return the receive timeout + // + pOptionData = (UINT8 *)&pSocket->RxTimeout; + LengthInBytes = sizeof ( pSocket->RxTimeout ); + break; + + case SO_RCVBUF: + // + // Return the maximum receive 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; + } + + // + // Determine if an option was found + // + if ( 0 != LengthInBytes ) { + // + // Validate the option length + // + if ( LengthInBytes <= OptionLength ) { + // + // Set the option value + // + CopyMem ( pOptionData, pOptionValue, LengthInBytes ); + errno = 0; + Status = EFI_SUCCESS; + } + else { + DEBUG (( DEBUG_OPTION, + "ERROR - Buffer to small, %d bytes < %d bytes!\r\n", + OptionLength, + LengthInBytes )); + } + } + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + *pErrno = errno; + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Allocate a packet for a receive or transmit operation + + This support routine is called by ::EslSocketRxStart and the + network specific TxBuffer routines to get buffer space for the + next operation. + + @param [in] ppPacket Address to receive the ::ESL_PACKET structure + @param [in] LengthInBytes Length of the packet structure + @param [in] ZeroBytes Length of packet to zero + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - The packet was allocated successfully + + **/ +EFI_STATUS +EslSocketPacketAllocate ( + IN ESL_PACKET ** ppPacket, + IN size_t LengthInBytes, + IN size_t ZeroBytes, + IN UINTN DebugFlags + ) +{ + ESL_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 )); + if ( 0 != ZeroBytes ) { + ZeroMem ( &pPacket->Op, ZeroBytes ); + } + 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 + + This support routine is called by the network specific Close + and TxComplete routines and during error cases in RxComplete + and TxBuffer. Note that the network layers typically place + receive packets on the ESL_SOCKET::pRxFree list for reuse. + + @param [in] pPacket Address of an ::ESL_PACKET structure + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - The packet was allocated successfully + + **/ +EFI_STATUS +EslSocketPacketFree ( + IN ESL_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. + + This routine builds a detected event mask which is returned to + the caller in the buffer provided. + + The ::poll routine calls this routine to determine if the socket + needs to be serviced as a result of connection, error, receive or + transmit activity. + + @param [in] pSocketProtocol Address of an ::EFI_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; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + 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 + // + Status = EslSocketIsConfigured ( pSocket ); + 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; +} + + +/** + Allocate and initialize a ESL_PORT structure. + + This routine initializes an ::ESL_PORT structure for use by + the socket. This routine calls a routine via + ESL_PROTOCOL_API::pfnPortAllocate to initialize the network + specific resources. The resources are released later by the + \ref PortCloseStateMachine. + + This support routine is called by: + + to connect the socket with the underlying network adapter + to the socket. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + @param [in] pService Address of an ::ESL_SERVICE structure. + @param [in] ChildHandle Network protocol child handle + @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] bBindTest TRUE if EslSocketBindTest should be called + @param [in] DebugFlags Flags for debug messages + @param [out] ppPort Buffer to receive new ::ESL_PORT structure address + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslSocketPortAllocate ( + IN ESL_SOCKET * pSocket, + IN ESL_SERVICE * pService, + IN EFI_HANDLE ChildHandle, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest, + IN UINTN DebugFlags, + OUT ESL_PORT ** ppPort + ) +{ + UINTN LengthInBytes; + UINT8 * pBuffer; + ESL_IO_MGMT * pIo; + ESL_LAYER * pLayer; + ESL_PORT * pPort; + EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding; + CONST ESL_SOCKET_BINDING * pSocketBinding; + EFI_STATUS Status; + EFI_STATUS TempStatus; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Use for/break instead of goto + pSocketBinding = pService->pSocketBinding; + for ( ; ; ) { + // + // Allocate a port structure + // + pLayer = &mEslLayer; + LengthInBytes = sizeof ( *pPort ) + + ESL_STRUCTURE_ALIGNMENT_BYTES + + (( pSocketBinding->RxIo + + pSocketBinding->TxIoNormal + + pSocketBinding->TxIoUrgent ) + * sizeof ( ESL_IO_MGMT )); + pPort = (ESL_PORT *) AllocateZeroPool ( LengthInBytes ); + if ( NULL == pPort ) { + Status = EFI_OUT_OF_RESOURCES; + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, + "0x%08x: Allocate pPort, %d bytes\r\n", + pPort, + LengthInBytes )); + + // + // Initialize the port + // + pPort->DebugFlags = DebugFlags; + pPort->Handle = ChildHandle; + pPort->pService = pService; + pPort->pServiceBinding = pService->pServiceBinding; + pPort->pSocket = pSocket; + pPort->pSocketBinding = pService->pSocketBinding; + pPort->Signature = PORT_SIGNATURE; + + // + // Open the port protocol + // + Status = gBS->OpenProtocol ( pPort->Handle, + pSocketBinding->pNetworkProtocolGuid, + &pPort->pProtocol.v, + pLayer->ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to open network protocol GUID on controller 0x%08x\r\n", + pPort->Handle )); + pSocket->errno = EEXIST; + break; + } + DEBUG (( DebugFlags, + "0x%08x: Network protocol GUID opened on controller 0x%08x\r\n", + pPort->pProtocol.v, + pPort->Handle )); + + // + // Initialize the port specific resources + // + Status = pSocket->pApi->pfnPortAllocate ( pPort, + DebugFlags ); + if ( EFI_ERROR ( Status )) { + break; + } + + // + // Set the local address + // + Status = pSocket->pApi->pfnLocalAddrSet ( pPort, pSockAddr, bBindTest ); + if ( EFI_ERROR ( Status )) { + break; + } + + // + // Test the address/port configuration + // + if ( bBindTest ) { + Status = EslSocketBindTest ( pPort, pSocket->pApi->BindTestErrno ); + if ( EFI_ERROR ( Status )) { + break; + } + } + + // + // Initialize the receive structures + // + pBuffer = (UINT8 *)&pPort[ 1 ]; + pBuffer = &pBuffer[ ESL_STRUCTURE_ALIGNMENT_BYTES ]; + pBuffer = (UINT8 *)( ESL_STRUCTURE_ALIGNMENT_MASK & (UINTN)pBuffer ); + pIo = (ESL_IO_MGMT *)pBuffer; + if (( 0 != pSocketBinding->RxIo ) + && ( NULL != pSocket->pApi->pfnRxComplete )) { + Status = EslSocketIoInit ( pPort, + &pIo, + pSocketBinding->RxIo, + &pPort->pRxFree, + DebugFlags | DEBUG_POOL, + "receive", + pSocket->pApi->pfnRxComplete ); + if ( EFI_ERROR ( Status )) { + break; + } + } + + // + // Initialize the urgent transmit structures + // + if (( 0 != pSocketBinding->TxIoUrgent ) + && ( NULL != pSocket->pApi->pfnTxOobComplete )) { + Status = EslSocketIoInit ( pPort, + &pIo, + pSocketBinding->TxIoUrgent, + &pPort->pTxOobFree, + DebugFlags | DEBUG_POOL, + "urgent transmit", + pSocket->pApi->pfnTxOobComplete ); + if ( EFI_ERROR ( Status )) { + break; + } + } + + // + // Initialize the normal transmit structures + // + if (( 0 != pSocketBinding->TxIoNormal ) + && ( NULL != pSocket->pApi->pfnTxComplete )) { + Status = EslSocketIoInit ( pPort, + &pIo, + pSocketBinding->TxIoNormal, + &pPort->pTxFree, + DebugFlags | DEBUG_POOL, + "normal transmit", + pSocket->pApi->pfnTxComplete ); + if ( EFI_ERROR ( Status )) { + break; + } + } + + // + // 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 )) { + if ( NULL != pPort ) { + // + // Close the port + // + EslSocketPortClose ( pPort ); + } + else { + // + // Close the port if necessary + // + pServiceBinding = pService->pServiceBinding; + TempStatus = pServiceBinding->DestroyChild ( pServiceBinding, + ChildHandle ); + if ( !EFI_ERROR ( TempStatus )) { + DEBUG (( DEBUG_BIND | DEBUG_POOL, + "0x%08x: %s port handle destroyed\r\n", + ChildHandle, + pSocketBinding->pName )); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_BIND | DEBUG_POOL, + "ERROR - Failed to destroy the %s port handle 0x%08x, Status: %r\r\n", + pSocketBinding->pName, + ChildHandle, + TempStatus )); + ASSERT ( EFI_SUCCESS == TempStatus ); + } + } + } + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Close a port. + + This routine releases the resources allocated by ::EslSocketPortAllocate. + This routine calls ESL_PROTOCOL_API::pfnPortClose to release the network + specific resources. + + This routine is called by: + + See the \ref PortCloseStateMachine section. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @retval EFI_SUCCESS The port is closed + @retval other Port close error + +**/ +EFI_STATUS +EslSocketPortClose ( + IN ESL_PORT * pPort + ) +{ + UINTN DebugFlags; + ESL_LAYER * pLayer; + ESL_PACKET * pPacket; + ESL_PORT * pPreviousPort; + ESL_SERVICE * pService; + EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding; + CONST ESL_SOCKET_BINDING * pSocketBinding; + ESL_SOCKET * pSocket; + 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 + // Note that the port may not be in the service list + // if the service has been shutdown. + // + pService = pPort->pService; + if ( NULL != 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 + // + while ( NULL != pSocket->pRxOobPacketListHead ) { + pPacket = pSocket->pRxOobPacketListHead; + pSocket->pRxOobPacketListHead = pPacket->pNext; + pSocket->pApi->pfnPacketFree ( pPacket, &pSocket->RxOobBytes ); + 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->pApi->pfnPacketFree ( pPacket, &pSocket->RxBytes ); + 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 ); + } + + // + // Release the network specific resources + // + if ( NULL != pSocket->pApi->pfnPortClose ) { + Status = pSocket->pApi->pfnPortClose ( pPort ); + } + + // + // Done with the normal transmit events + // + Status = EslSocketIoFree ( pPort, + &pPort->pTxFree, + DebugFlags | DEBUG_POOL, + "normal transmit" ); + + // + // Done with the urgent transmit events + // + Status = EslSocketIoFree ( pPort, + &pPort->pTxOobFree, + DebugFlags | DEBUG_POOL, + "urgent transmit" ); + + // + // Done with the receive events + // + Status = EslSocketIoFree ( pPort, + &pPort->pRxFree, + DebugFlags | DEBUG_POOL, + "receive" ); + + // + // Done with the lower layer network protocol + // + pSocketBinding = pPort->pSocketBinding; + if ( NULL != pPort->pProtocol.v ) { + Status = gBS->CloseProtocol ( pPort->Handle, + pSocketBinding->pNetworkProtocolGuid, + pLayer->ImageHandle, + NULL ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags, + "0x%08x: Network protocol GUID closed on controller 0x%08x\r\n", + pPort->pProtocol.v, + pPort->Handle )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close network protocol GUID on controller 0x%08x, Status: %r\r\n", + pPort->Handle, + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the network port + // + pServiceBinding = pPort->pServiceBinding; + if ( NULL != pPort->Handle ) { + Status = pServiceBinding->DestroyChild ( pServiceBinding, + pPort->Handle ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: %s port handle destroyed\r\n", + pPort->Handle, + pSocketBinding->pName )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL, + "ERROR - Failed to destroy the %s port handle, Status: %r\r\n", + pSocketBinding->pName, + 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; +} + + +/** + Port close state 3 + + This routine attempts to complete the port close operation. + + This routine is called by the TCP layer upon completion of + the close operation and by ::EslSocketPortCloseTxDone. + See the \ref PortCloseStateMachine section. + + @param [in] Event The close completion event + + @param [in] pPort Address of an ::ESL_PORT structure. + +**/ +VOID +EslSocketPortCloseComplete ( + IN EFI_EVENT Event, + IN ESL_PORT * pPort + ) +{ + ESL_IO_MGMT * pIo; + EFI_STATUS Status; + + DBG_ENTER ( ); + VERIFY_AT_TPL ( TPL_SOCKETS ); + + // + // 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 )); + + // + // Shutdown the receive operation on the port + // + if ( NULL != pPort->pfnRxCancel ) { + pIo = pPort->pRxActive; + while ( NULL != pIo ) { + EslSocketRxCancel ( pPort, pIo ); + pIo = pIo->pNext; } } - + // - // Return the operation status + // Determine if the receive operation is pending // - if ( NULL != pErrno ) { - *pErrno = errno; - } + Status = EslSocketPortCloseRxDone ( pPort ); DBG_EXIT_STATUS ( Status ); - return Status; } /** - Allocate a packet for a receive or transmit operation + Port close state 4 - @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 + This routine determines the state of the receive operations and + continues the close operation after the pending receive operations + are cancelled. - @retval EFI_SUCCESS - The packet was allocated successfully + This routine is called by + + to determine the state of the receive operations. + See the \ref PortCloseStateMachine section. - **/ + @param [in] pPort Address of an ::ESL_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 -EslSocketPacketAllocate ( - IN DT_PACKET ** ppPacket, - IN size_t LengthInBytes, - IN UINTN DebugFlags +EslSocketPortCloseRxDone ( + IN ESL_PORT * pPort ) { - DT_PACKET * pPacket; EFI_STATUS Status; DBG_ENTER ( ); // - // Allocate a packet structure + // Verify the socket layer synchronization // - 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; - } + VERIFY_TPL ( TPL_SOCKETS ); // - // Return the packet + // Verify that the port is closing // - *ppPacket = pPacket; + Status = EFI_ALREADY_STARTED; + if ( PORT_STATE_CLOSE_DONE == pPort->State ) { + // + // Determine if the receive operation is pending + // + Status = EFI_NOT_READY; + if ( NULL == pPort->pRxActive ) { + // + // 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 )); + + // + // Complete the port close operation + // + Status = EslSocketPortClose ( pPort ); + } + else { + DEBUG_CODE_BEGIN (); + { + ESL_IO_MGMT * pIo; + // + // Display the outstanding receive operations + // + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close: Receive still pending!\r\n", + pPort )); + pIo = pPort->pRxActive; + while ( NULL != pIo ) { + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Packet pending on network adapter\r\n", + pIo->pPacket )); + pIo = pIo->pNext; + } + } + DEBUG_CODE_END ( ); + } + } // // Return the operation status @@ -2217,41 +3999,64 @@ EslSocketPacketAllocate ( /** - Free a packet used for receive or transmit operation + Start the close operation on a port, state 1. + + This routine marks the port as closed and initiates the \ref + PortCloseStateMachine. The first step is to allow the \ref + TransmitEngine to run down. - @param [in] pPacket Address of the DT_PACKET structure + This routine is called by ::EslSocketCloseStart to initiate the socket + network specific close operation on the socket. + + @param [in] pPort Address of an ::ESL_PORT structure. + @param [in] bCloseNow Set TRUE to abort active transfers @param [in] DebugFlags Flags for debug messages - @retval EFI_SUCCESS - The packet was allocated successfully + @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 -EslSocketPacketFree ( - IN DT_PACKET * pPacket, +EslSocketPortCloseStart ( + IN ESL_PORT * pPort, + IN BOOLEAN bCloseNow, IN UINTN DebugFlags ) { - UINTN LengthInBytes; + ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); // - // Allocate a packet structure + // Verify the socket layer synchronization // - 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 )); + 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 = EslSocketPortCloseTxDone ( pPort ); } // @@ -2263,210 +4068,155 @@ EslSocketPacketFree ( /** - 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. + Port close state 2 - @param [in] Events Events of interest for this socket + This routine determines the state of the transmit engine and + continue the close operation after the transmission is complete. + The next step is to stop the \ref ReceiveEngine. + See the \ref PortCloseStateMachine section. - @param [in] pEvents Address to receive the detected events + This routine is called by ::EslSocketPortCloseStart to determine + if the transmission is complete. - @param [out] pErrno Address to receive the errno value upon completion. + @param [in] pPort Address of an ::ESL_PORT structure. - @retval EFI_SUCCESS - Socket successfully polled - @retval EFI_INVALID_PARAMETER - When pEvents is NULL + @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 -EslSocketPoll ( - IN EFI_SOCKET_PROTOCOL * pSocketProtocol, - IN short Events, - IN short * pEvents, - IN int * pErrno +EslSocketPortCloseTxDone ( + IN ESL_PORT * pPort ) { - short DetectedEvents; - DT_SOCKET * pSocket; + ESL_IO_MGMT * pIo; + ESL_SOCKET * pSocket; EFI_STATUS Status; - EFI_TPL TplPrevious; - short ValidEvents; - DEBUG (( DEBUG_POLL, "Entering SocketPoll\r\n" )); + DBG_ENTER ( ); // - // Assume success + // Verify the socket layer synchronization // - Status = EFI_SUCCESS; - DetectedEvents = 0; - pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); - pSocket->errno = 0; + VERIFY_TPL ( TPL_SOCKETS ); // - // Verify the socket state + // All transmissions are complete or must be stopped + // Mark the port as TX complete // - if ( !pSocket->bConfigured ) { + Status = EFI_ALREADY_STARTED; + if ( PORT_STATE_CLOSE_STARTED == pPort->State ) { // - // Synchronize with the socket layer + // Verify that the transmissions are complete // - 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: + pSocket = pPort->pSocket; + if ( pPort->bCloseNow + || ( EFI_SUCCESS != pSocket->TxError ) + || (( NULL == pPort->pTxActive ) + && ( NULL == pPort->pTxOobActive ))) { // - // Determine the connection point within the network stack + // Update the port state // - 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; - } + 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 )); - // - // 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 + // Close the port + // Skip the close operation if the port is not configured // - 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; + Status = EFI_SUCCESS; + pSocket = pPort->pSocket; + if (( pPort->bConfigured ) + && ( NULL != pSocket->pApi->pfnPortCloseOp )) { + // + // Start the close operation + // + Status = pSocket->pApi->pfnPortCloseOp ( pPort ); + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close: Close operation still pending!\r\n", + pPort )); + ASSERT ( EFI_SUCCESS == Status ); } - if ( pSocket->bConnected ) { + else { // - // A connection is present - // See posix connect documentation at - // http://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.htm + // The receive operation is complete + // Update the port state // - DetectedEvents |= POLLOUT | POLLWRNORM; + EslSocketPortCloseComplete ( NULL, pPort ); } - - // - // The following bits are set based upon the POSIX poll documentation at - // http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html - // - + } + else { // - // Check for urgent receive data + // Transmissions are still active, exit // - if ( 0 < pSocket->RxOobBytes ) { - DetectedEvents |= POLLRDBAND | POLLPRI | POLLIN; - } + Status = EFI_NOT_READY; + pSocket->errno = EAGAIN; + DEBUG_CODE_BEGIN ( ); + { + ESL_PACKET * pPacket; - // - // Check for normal receive data - // - if (( 0 < pSocket->RxBytes ) - || ( EFI_SUCCESS != pSocket->RxError )) { - DetectedEvents |= POLLRDNORM | POLLIN; - } + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port Close: Transmits are still pending!\r\n", + pPort )); - // - // Handle the receive errors - // - if (( EFI_SUCCESS != pSocket->RxError ) - && ( 0 == ( DetectedEvents & POLLIN ))) { - DetectedEvents |= POLLERR | POLLIN | POLLRDNORM | POLLRDBAND; - } + // + // Display the pending urgent transmit packets + // + pPacket = pSocket->pTxOobPacketListHead; + while ( NULL != pPacket ) { + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Packet pending on urgent TX list, %d bytes\r\n", + pPacket, + pPacket->PacketSize )); + pPacket = pPacket->pNext; + } - // - // Check for urgent transmit data buffer space - // - if (( MAX_TX_DATA > pSocket->TxOobBytes ) - || ( EFI_SUCCESS != pSocket->TxError )) { - DetectedEvents |= POLLWRBAND; - } + pIo = pPort->pTxOobActive; + while ( NULL != pIo ) { + pPacket = pIo->pPacket; + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Packet active %d bytes, pIo: 0x%08x\r\n", + pPacket, + pPacket->PacketSize, + pIo )); + pIo = pIo->pNext; + } - // - // Check for normal transmit data buffer space - // - if (( MAX_TX_DATA > pSocket->TxBytes ) - || ( EFI_SUCCESS != pSocket->TxError )) { - DetectedEvents |= POLLWRNORM; - } + // + // Display the pending normal transmit packets + // + pPacket = pSocket->pTxPacketListHead; + while ( NULL != pPacket ) { + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Packet pending on normal TX list, %d bytes\r\n", + pPacket, + pPacket->PacketSize )); + pPacket = pPacket->pNext; + } - // - // Handle the transmit error - // - if ( EFI_ERROR ( pSocket->TxError )) { - DetectedEvents |= POLLERR; + pIo = pPort->pTxActive; + while ( NULL != pIo ) { + pPacket = pIo->pPacket; + DEBUG (( DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Packet active %d bytes, pIo: 0x%08x\r\n", + pPacket, + pPacket->PacketSize, + pIo )); + pIo = pIo->pNext; + } } - } - } - - // - // Return the detected events - // - *pEvents = DetectedEvents & ( Events - | POLLERR - | POLLHUP - | POLLNVAL ); + DEBUG_CODE_END (); + } + } // // Return the operation status // - DEBUG (( DEBUG_POLL, "Exiting SocketPoll, Status: %r\r\n", Status )); + DBG_EXIT_STATUS ( Status ); return Status; } @@ -2474,8 +4224,15 @@ EslSocketPoll ( /** Receive data from a network connection. + This routine calls the network specific routine to remove the + next portion of data from the receive queue and return it to the + caller. - @param [in] pSocketProtocol Address of the socket protocol structure. + The ::recvfrom routine calls this routine to determine if any data + is received from the remote system. Note that the other routines + ::recv and ::read are layered on top of ::recvfrom. + + @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] Flags Message control flags @@ -2506,7 +4263,23 @@ EslSocketReceive ( IN int * pErrno ) { - DT_SOCKET * pSocket; + union { + struct sockaddr_in v4; + struct sockaddr_in6 v6; + } Addr; + socklen_t AddressLength; + BOOLEAN bConsumePacket; + BOOLEAN bUrgentQueue; + size_t DataLength; + ESL_PACKET * pNextPacket; + ESL_PACKET * pPacket; + ESL_PORT * pPort; + ESL_PACKET ** ppQueueHead; + ESL_PACKET ** ppQueueTail; + struct sockaddr * pRemoteAddress; + size_t * pRxDataBytes; + ESL_SOCKET * pSocket; + size_t SkipBytes; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -2525,188 +4298,748 @@ EslSocketReceive ( pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // - // Return the transmit error if necessary + // Validate the return address parameters // - if ( EFI_SUCCESS != pSocket->TxError ) { - pSocket->errno = EIO; - Status = pSocket->TxError; - pSocket->TxError = EFI_SUCCESS; - } - else { + if (( NULL == pAddress ) || ( NULL != pAddressLength )) { // - // Verify the socket state + // Return the transmit error if necessary // - if ( !pSocket->bConfigured ) { - // - // Synchronize with the socket layer - // - RAISE_TPL ( TplPrevious, TPL_SOCKETS ); - + if ( EFI_SUCCESS != pSocket->TxError ) { + pSocket->errno = EIO; + Status = pSocket->TxError; + pSocket->TxError = EFI_SUCCESS; + } + else { // - // Validate the local address + // Verify the socket state // - 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: + Status = EslSocketIsConfigured ( pSocket ); + if ( !EFI_ERROR ( Status )) { // - // Determine the connection point within the network stack + // Validate the buffer length // - switch ( pSocket->Type ) { - default: - DEBUG (( DEBUG_RX, - "ERROR - Invalid socket type: %d\r\n", - pSocket->Type)); + if (( NULL == pDataLength ) + || ( NULL == pBuffer )) { + if ( NULL == pDataLength ) { + DEBUG (( DEBUG_RX, + "ERROR - pDataLength is NULL!\r\n" )); + } + else { + DEBUG (( DEBUG_RX, + "ERROR - pBuffer is NULL!\r\n" )); + } Status = EFI_INVALID_PARAMETER; - break; - - case SOCK_STREAM: - case SOCK_SEQPACKET: + pSocket->errno = EFAULT; + } + else { // - // Verify the port state + // Verify the API // - Status = EslTcpSocketIsConfigured4 ( pSocket ); - break; + if ( NULL == pSocket->pApi->pfnReceive ) { + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTSUP; + } + else { + // + // Zero the receive address if being returned + // + pRemoteAddress = NULL; + if ( NULL != pAddress ) { + pRemoteAddress = (struct sockaddr *)&Addr; + ZeroMem ( pRemoteAddress, sizeof ( Addr )); + pRemoteAddress->sa_family = pSocket->pApi->AddressFamily; + pRemoteAddress->sa_len = (UINT8)pSocket->pApi->AddressLength; + } + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); - case SOCK_DGRAM: - // - // Verify the port state - // - Status = EslUdpSocketIsConfigured4 ( pSocket ); + // + // Assume failure + // + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTCONN; + + // + // 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 + // + bUrgentQueue = (BOOLEAN)( 0 != ( Flags & MSG_OOB )); + if ( bUrgentQueue ) { + 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 + // + *pDataLength = 0; + pPacket = *ppQueueHead; + if ( NULL != pPacket ) { + // + // Copy the received data + // + do { + // + // Attempt to receive a packet + // + SkipBytes = 0; + bConsumePacket = (BOOLEAN)( 0 == ( Flags & MSG_PEEK )); + pBuffer = pSocket->pApi->pfnReceive ( pPort, + pPacket, + &bConsumePacket, + BufferLength, + pBuffer, + &DataLength, + (struct sockaddr *)&Addr, + &SkipBytes ); + *pDataLength += DataLength; + BufferLength -= DataLength; + + // + // Determine if the data is being read + // + pNextPacket = pPacket->pNext; + if ( bConsumePacket ) { + // + // All done with this packet + // Account for any discarded data + // + pSocket->pApi->pfnPacketFree ( pPacket, pRxDataBytes ); + if ( 0 != SkipBytes ) { + DEBUG (( DEBUG_RX, + "0x%08x: Port, packet read, skipping over 0x%08x bytes\r\n", + pPort, + SkipBytes )); + } + + // + // 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 the receive operation if necessary + // + if (( NULL != pPort->pRxFree ) + && ( MAX_RX_DATA > pSocket->RxBytes )) { + EslSocketRxStart ( pPort ); + } + } + + // + // Get the next packet + // + pPacket = pNextPacket; + } while (( SOCK_STREAM == pSocket->Type ) + && ( NULL != pPacket ) + && ( 0 < BufferLength )); + + // + // Successful operation + // + Status = EFI_SUCCESS; + pSocket->errno = 0; + } + 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; + pSocket->RxError = EFI_SUCCESS; + switch ( Status ) { + default: + pSocket->errno = EIO; + break; + + case EFI_CONNECTION_FIN: + // + // Continue to return zero bytes received when the + // peer has successfully closed the connection + // + pSocket->RxError = EFI_CONNECTION_FIN; + *pDataLength = 0; + pSocket->errno = 0; + Status = EFI_SUCCESS; + 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; + } + } + else { + Status = EFI_NOT_READY; + pSocket->errno = EAGAIN; + } + } + } + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + + if (( !EFI_ERROR ( Status )) && ( NULL != pAddress )) { + // + // Return the remote address if requested, truncate if necessary + // + AddressLength = pRemoteAddress->sa_len; + if ( AddressLength > *pAddressLength ) { + AddressLength = *pAddressLength; + } + DEBUG (( DEBUG_RX, + "Returning the remote address, 0x%016x bytes --> 0x%16x\r\n", *pAddressLength, pAddress )); + ZeroMem ( pAddress, *pAddressLength ); + CopyMem ( pAddress, &Addr, AddressLength ); + + // + // Update the address length + // + *pAddressLength = pRemoteAddress->sa_len; + } + } + } + } + } + + + } + else { + // + // Bad return address pointer and length + // + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + } + } + + // + // Return the operation status + // + if ( NULL != pErrno ) { + if ( NULL != pSocket ) { + *pErrno = pSocket->errno; + } + else { + Status = EFI_INVALID_PARAMETER; + *pErrno = ENOTSOCK; + } + } + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Cancel the receive operations + + This routine cancels a pending receive operation. + See the \ref ReceiveEngine section. + + This routine is called by ::EslSocketShutdown when the socket + layer is being shutdown. + + @param [in] pPort Address of an ::ESL_PORT structure + @param [in] pIo Address of an ::ESL_IO_MGMT structure + + **/ +VOID +EslSocketRxCancel ( + IN ESL_PORT * pPort, + IN ESL_IO_MGMT * pIo + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Cancel the outstanding receive + // + Status = pPort->pfnRxCancel ( pPort->pProtocol.v, + &pIo->Token ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Packet receive aborted on port: 0x%08x\r\n", + pIo->pPacket, + pPort )); + } + else { + DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Packet receive pending on Port 0x%08x, Status: %r\r\n", + pIo->pPacket, + pPort, + Status )); + } + DBG_EXIT ( ); +} + + +/** + Process the receive completion + + This routine queues the data in FIFO order in either the urgent + or normal data queues depending upon the type of data received. + See the \ref ReceiveEngine section. + + This routine is called when some data is received by: + + + @param [in] pIo Address of an ::ESL_IO_MGMT structure + @param [in] Status Receive status + @param [in] LengthInBytes Length of the receive data + @param [in] bUrgent TRUE if urgent data is received and FALSE + for normal data. + +**/ +VOID +EslSocketRxComplete ( + IN ESL_IO_MGMT * pIo, + IN EFI_STATUS Status, + IN UINTN LengthInBytes, + IN BOOLEAN bUrgent + ) +{ + BOOLEAN bUrgentQueue; + ESL_IO_MGMT * pIoNext; + ESL_PACKET * pPacket; + ESL_PORT * pPort; + ESL_PACKET * pPrevious; + ESL_PACKET ** ppQueueHead; + ESL_PACKET ** ppQueueTail; + size_t * pRxBytes; + ESL_SOCKET * pSocket; + + DBG_ENTER ( ); + VERIFY_AT_TPL ( TPL_SOCKETS ); + + // + // Locate the active receive packet + // + pPacket = pIo->pPacket; + pPort = pIo->pPort; + pSocket = pPort->pSocket; + + // + // pPort->pRxActive + // | + // V + // +-------------+ +-------------+ +-------------+ + // Active | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL + // +-------------+ +-------------+ +-------------+ + // + // +-------------+ +-------------+ +-------------+ + // Free | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL + // +-------------+ +-------------+ +-------------+ + // ^ + // | + // pPort->pRxFree + // + // + // Remove the IO structure from the active list + // The following code searches for the entry in the list and does not + // assume that the receive operations complete in the order they were + // issued to the UEFI network layer. + // + pIoNext = pPort->pRxActive; + while (( NULL != pIoNext ) && ( pIoNext != pIo ) && ( pIoNext->pNext != pIo )) + { + pIoNext = pIoNext->pNext; + } + ASSERT ( NULL != pIoNext ); + if ( pIoNext == pIo ) { + pPort->pRxActive = pIo->pNext; // Beginning of list + } + else { + pIoNext->pNext = pIo->pNext; // Middle of list + } + + // + // Free the IO structure + // + pIo->pNext = pPort->pRxFree; + pPort->pRxFree = pIo; + + // + // pRxOobPacketListHead pRxOobPacketListTail + // | | + // V V + // +------------+ +------------+ +------------+ + // Urgent Data | ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL + // +------------+ +------------+ +------------+ + // + // +------------+ +------------+ +------------+ + // Normal Data | ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL + // +------------+ +------------+ +------------+ + // ^ ^ + // | | + // pRxPacketListHead pRxPacketListTail + // + // + // Determine the queue to use + // + bUrgentQueue = (BOOLEAN)( bUrgent + && pSocket->pApi->bOobSupported + && ( !pSocket->bOobInLine )); + if ( bUrgentQueue ) { + ppQueueHead = &pSocket->pRxOobPacketListHead; + ppQueueTail = &pSocket->pRxOobPacketListTail; + pRxBytes = &pSocket->RxOobBytes; + } + else { + ppQueueHead = &pSocket->pRxPacketListHead; + ppQueueTail = &pSocket->pRxPacketListTail; + pRxBytes = &pSocket->RxBytes; + } + + // + // Determine if this receive was successful + // + if (( !EFI_ERROR ( Status )) + && ( PORT_STATE_CLOSE_STARTED > pPort->State ) + && ( !pSocket->bRxDisable )) { + // + // Account for the received data + // + *pRxBytes += LengthInBytes; + + // + // Log the received data + // + DEBUG (( DEBUG_RX | DEBUG_INFO, + "0x%08x: Packet queued on %s queue of port 0x%08x with 0x%08x bytes of %s data\r\n", + pPacket, + bUrgentQueue ? L"urgent" : L"normal", + pPort, + LengthInBytes, + bUrgent ? L"urgent" : L"normal" )); + + // + // Add the packet to the list tail. + // + pPacket->pNext = NULL; + pPrevious = *ppQueueTail; + if ( NULL == pPrevious ) { + *ppQueueHead = pPacket; + } + else { + pPrevious->pNext = pPacket; + } + *ppQueueTail = pPacket; + + // + // Attempt to restart this receive operation + // + if ( pSocket->MaxRxBuf > pSocket->RxBytes ) { + EslSocketRxStart ( pPort ); + } + else { + DEBUG (( DEBUG_RX, + "0x%08x: Port RX suspended, 0x%08x bytes queued\r\n", + pPort, + pSocket->RxBytes )); + } + } + else { + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "ERROR - Receive error on port 0x%08x, packet 0x%08x, Status:%r\r\n", + pPort, + pPacket, + Status )); + } + + // + // Account for the receive bytes and release the driver's buffer + // + if ( !EFI_ERROR ( Status )) { + *pRxBytes += LengthInBytes; + pSocket->pApi->pfnPacketFree ( pPacket, pRxBytes ); + } + + // + // 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 ) { + if ( PORT_STATE_CLOSE_DONE == pPort->State ) { + EslSocketPortCloseRxDone ( pPort ); + } + } + else { + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "0x%08x: Port state: PORT_STATE_RX_ERROR, Status: %r\r\n", + pPort, + Status )); + pPort->State = PORT_STATE_RX_ERROR; + } + } + } + + DBG_EXIT ( ); +} + + +/** + Start a receive operation + + This routine posts a receive buffer to the network adapter. + See the \ref ReceiveEngine section. + + This support routine is called by: + + + @param [in] pPort Address of an ::ESL_PORT structure. + + **/ +VOID +EslSocketRxStart ( + IN ESL_PORT * pPort + ) +{ + UINT8 * pBuffer; + ESL_IO_MGMT * pIo; + ESL_PACKET * pPacket; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if a receive is already pending + // + Status = EFI_SUCCESS; + pPacket = NULL; + pSocket = pPort->pSocket; + if ( !EFI_ERROR ( pPort->pSocket->RxError )) { + if (( NULL != pPort->pRxFree ) + && ( !pSocket->bRxDisable ) + && ( PORT_STATE_CLOSE_STARTED > pPort->State )) { + // + // Start all of the pending receive operations + // + while ( NULL != pPort->pRxFree ) { + // + // 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, + pSocket->pApi->RxPacketBytes, + pSocket->pApi->RxZeroBytes, + 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 )); break; } - break; } // - // Release the socket layer synchronization + // Connect the IO and packet structures // - RESTORE_TPL ( TplPrevious ); + pIo = pPort->pRxFree; + pIo->pPacket = pPacket; + + // + // Eliminate the need for IP4 and UDP4 specific routines by + // clearing the RX data pointer here. + // + // No driver buffer for this packet + // + // +--------------------+ + // | ESL_IO_MGMT | + // | | + // | +---------------+ + // | | Token | + // | | RxData --> NULL + // +----+---------------+ + // + pBuffer = (UINT8 *)pIo; + pBuffer = &pBuffer[ pSocket->pApi->RxBufferOffset ]; + *(VOID **)pBuffer = NULL; // - // Set errno if a failure occurs + // Network specific receive packet initialization // - if ( EFI_ERROR ( Status )) { - pSocket->errno = EADDRNOTAVAIL; + if ( NULL != pSocket->pApi->pfnRxStart ) { + pSocket->pApi->pfnRxStart ( pPort, pIo ); } - } - if ( !EFI_ERROR ( Status )) { + // - // Validate the buffer length + // Start the receive on the packet // - 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{ + Status = pPort->pfnRxStart ( pPort->pProtocol.v, &pIo->Token ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "0x%08x: Packet receive pending on port 0x%08x\r\n", + pPacket, + pPort )); // - // Synchronize with the socket layer + // Allocate the receive control structure // - RAISE_TPL ( TplPrevious, TPL_SOCKETS ); - + pPort->pRxFree = pIo->pNext; + // - // Validate the local address + // Mark this receive as pending // - 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: + pIo->pNext = pPort->pRxActive; + pPort->pRxActive = pIo; + + } + 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 )) { // - // Determine the connection point within the network stack + // Save the error status // - 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; + pSocket->RxError = Status; } // - // Release the socket layer synchronization + // Free the packet // - RESTORE_TPL ( TplPrevious ); + pIo->pPacket = NULL; + pPacket->pNext = pSocket->pRxFree; + pSocket->pRxFree = pPacket; + break; } } } - } - - // - // Return the operation status - // - if ( NULL != pErrno ) { - if ( NULL != pSocket ) { - *pErrno = pSocket->errno; - } - else - { - Status = EFI_INVALID_PARAMETER; - *pErrno = EBADF; + else { + if ( NULL == pPort->pRxFree ) { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "0x%08x: Port, no available ESL_IO_MGMT structures\r\n", + pPort)); + } + if ( pSocket->bRxDisable ) { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "0x%08x: Port, receive disabled!\r\n", + pPort )); + } + if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { + DEBUG (( DEBUG_RX | DEBUG_INFO, + "0x%08x: Port, is closing!\r\n", + pPort )); + } } } - DBG_EXIT_STATUS ( Status ); - return Status; + else { + DEBUG (( DEBUG_ERROR | DEBUG_RX, + "ERROR - Previous receive error, Status: %r\r\n", + pPort->pSocket->RxError )); + } + + DBG_EXIT ( ); } /** Shutdown the socket receive and transmit operations - The SocketShutdown routine stops the socket receive and transmit - operations. + This routine sets a flag to stop future transmissions and calls + the network specific layer to cancel the pending receive operation. - @param [in] pSocketProtocol Address of the socket protocol structure. + The ::shutdown routine calls this routine to stop receive and transmit + operations on the socket. + + @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] How Which operations to stop @@ -2722,7 +5055,9 @@ EslSocketShutdown ( IN int * pErrno ) { - DT_SOCKET * pSocket; + ESL_IO_MGMT * pIo; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -2768,47 +5103,29 @@ EslSocketShutdown ( } // - // Validate the local address + // Cancel the pending receive operations // - 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: + if ( pSocket->bRxDisable ) { // - // Determine the connection point within the network stack + // Walk the list of ports // - 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: + pPort = pSocket->pPortList; + while ( NULL != pPort ) { // - // Cancel the pending receive operation + // Walk the list of active receive operations // - Status = EslTcpRxCancel4 ( pSocket ); - break; - - case SOCK_DGRAM: + pIo = pPort->pRxActive; + while ( NULL != pIo ) { + EslSocketRxCancel ( pPort, pIo ); + } + // - // Cancel the pending receive operation + // Set the next port // - Status = EslUdpRxCancel4 ( pSocket ); - break; + pPort = pPort->pLinkSocket; } - break; } - + // // Release the socket layer synchronization // @@ -2816,18 +5133,18 @@ EslSocketShutdown ( } else { // - // The socket is not connected + // Invalid How value // - pSocket->errno = ENOTCONN; - Status = EFI_NOT_STARTED; + pSocket->errno = EINVAL; + Status = EFI_INVALID_PARAMETER; } } else { // - // Invalid How value + // The socket is not connected // - pSocket->errno = EINVAL; - Status = EFI_INVALID_PARAMETER; + pSocket->errno = ENOTCONN; + Status = EFI_NOT_STARTED; } } @@ -2838,10 +5155,9 @@ EslSocketShutdown ( if ( NULL != pSocket ) { *pErrno = pSocket->errno; } - else - { + else { Status = EFI_INVALID_PARAMETER; - *pErrno = EBADF; + *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); @@ -2852,10 +5168,17 @@ EslSocketShutdown ( /** Send data using a network connection. - The SocketTransmit routine queues the data for transmission to the - remote network connection. + This routine calls the network specific layer to queue the data + for transmission. Eventually the buffer will reach the head of + the queue and will get transmitted over the network by the + \ref TransmitEngine. For datagram + sockets (SOCK_DGRAM and SOCK_RAW) there is no guarantee that + the data reaches the application running on the remote system. - @param [in] pSocketProtocol Address of the socket protocol structure. + The ::sendto routine calls this routine to send data to the remote + system. Note that ::send and ::write are layered on top of ::sendto. + + @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] Flags Message control flags @@ -2886,7 +5209,7 @@ EslSocketTransmit ( IN int * pErrno ) { - DT_SOCKET * pSocket; + ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -2916,66 +5239,7 @@ EslSocketTransmit ( // // 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; - } - } + Status = EslSocketIsConfigured ( pSocket ); if ( !EFI_ERROR ( Status )) { // // Verify that transmit is still allowed @@ -3015,61 +5279,34 @@ EslSocketTransmit ( } else { // - // Synchronize with the socket layer - // - RAISE_TPL ( TplPrevious, TPL_SOCKETS ); - + // Verify the API // - // 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: + if ( NULL == pSocket->pApi->pfnTransmit ) { + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTSUP; + } + else { // - // Determine the connection point within the network stack + // Synchronize with the socket layer // - switch ( pSocket->Type ) { - default: - DEBUG (( DEBUG_RX, - "ERROR - Invalid socket type: %d\r\n", - pSocket->Type)); - Status = EFI_INVALID_PARAMETER; - pSocket->errno = EADDRNOTAVAIL; - break; + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); - case SOCK_STREAM: - case SOCK_SEQPACKET: - Status = EslTcpTxBuffer4 ( pSocket, - Flags, - BufferLength, - pBuffer, - pDataLength ); - break; + // + // Attempt to buffer the packet for transmission + // + Status = pSocket->pApi->pfnTransmit ( pSocket, + Flags, + BufferLength, + pBuffer, + pDataLength, + pAddress, + AddressLength ); - case SOCK_DGRAM: - Status = EslUdpTxBuffer4 ( pSocket, - Flags, - BufferLength, - pBuffer, - pDataLength, - pAddress, - AddressLength ); - break; - } - break; + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); } - - // - // Release the socket layer synchronization - // - RESTORE_TPL ( TplPrevious ); } } } @@ -3091,10 +5328,9 @@ EslSocketTransmit ( if ( NULL != pSocket ) { *pErrno = pSocket->errno; } - else - { + else { Status = EFI_INVALID_PARAMETER; - *pErrno = EBADF; + *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); @@ -3103,9 +5339,325 @@ EslSocketTransmit ( /** - Socket layer's service binding protocol delcaration. -**/ -EFI_SERVICE_BINDING_PROTOCOL mEfiServiceBinding = { - EslSocketCreateChild, - EslSocketDestroyChild -}; + Complete the transmit operation + + This support routine handles the transmit completion processing for + the various network layers. It frees the ::ESL_IO_MGMT structure + and and frees packet resources by calling ::EslSocketPacketFree. + Transmit errors are logged in ESL_SOCKET::TxError. + See the \ref TransmitEngine section. + + This routine is called by: + + + @param [in] pIo Address of an ::ESL_IO_MGMT structure + @param [in] LengthInBytes Length of the data in bytes + @param [in] Status Transmit operation status + @param [in] pQueueType Zero terminated string describing queue type + @param [in] ppQueueHead Transmit queue head address + @param [in] ppQueueTail Transmit queue tail address + @param [in] ppActive Active transmit queue address + @param [in] ppFree Free transmit queue address + + **/ +VOID +EslSocketTxComplete ( + IN ESL_IO_MGMT * pIo, + IN UINT32 LengthInBytes, + IN EFI_STATUS Status, + IN CONST CHAR8 * pQueueType, + IN ESL_PACKET ** ppQueueHead, + IN ESL_PACKET ** ppQueueTail, + IN ESL_IO_MGMT ** ppActive, + IN ESL_IO_MGMT ** ppFree + ) +{ + ESL_PACKET * pCurrentPacket; + ESL_IO_MGMT * pIoNext; + ESL_PACKET * pNextPacket; + ESL_PACKET * pPacket; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; + + DBG_ENTER ( ); + VERIFY_AT_TPL ( TPL_SOCKETS ); + + // + // Locate the active transmit packet + // + pPacket = pIo->pPacket; + pPort = pIo->pPort; + pSocket = pPort->pSocket; + + // + // No more packet + // + pIo->pPacket = NULL; + + // + // Remove the IO structure from the active list + // + pIoNext = *ppActive; + while (( NULL != pIoNext ) && ( pIoNext != pIo ) && ( pIoNext->pNext != pIo )) + { + pIoNext = pIoNext->pNext; + } + ASSERT ( NULL != pIoNext ); + if ( pIoNext == pIo ) { + *ppActive = pIo->pNext; // Beginning of list + } + else { + pIoNext->pNext = pIo->pNext; // Middle of list + } + + // + // Free the IO structure + // + pIo->pNext = *ppFree; + *ppFree = pIo; + + // + // Display the results + // + DEBUG (( DEBUG_TX | DEBUG_INFO, + "0x%08x: pIo Released\r\n", + pIo )); + + // + // Save any transmit error + // + if ( EFI_ERROR ( Status )) { + if ( !EFI_ERROR ( pSocket->TxError )) { + pSocket->TxError = Status; + } + DEBUG (( DEBUG_TX | DEBUG_INFO, + "ERROR - Transmit failure for %apacket 0x%08x, Status: %r\r\n", + pQueueType, + pPacket, + Status )); + + // + // Empty the normal transmit list + // + pCurrentPacket = pPacket; + pNextPacket = *ppQueueHead; + while ( NULL != pNextPacket ) { + pPacket = pNextPacket; + pNextPacket = pPacket->pNext; + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + *ppQueueHead = NULL; + *ppQueueTail = NULL; + pPacket = pCurrentPacket; + } + else { + DEBUG (( DEBUG_TX | DEBUG_INFO, + "0x%08x: %apacket transmitted %d bytes successfully\r\n", + pPacket, + pQueueType, + LengthInBytes )); + + // + // Verify the transmit engine is still running + // + if ( !pPort->bCloseNow ) { + // + // Start the next packet transmission + // + EslSocketTxStart ( pPort, + ppQueueHead, + ppQueueTail, + ppActive, + ppFree ); + } + } + + // + // 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 + // + EslSocketPortCloseTxDone ( pPort ); + } + + DBG_EXIT ( ); +} + + +/** + Transmit data using a network connection. + + This support routine starts a transmit operation on the + underlying network layer. + + The network specific code calls this routine to start a + transmit operation. See the \ref TransmitEngine section. + + @param [in] pPort Address of an ::ESL_PORT structure + @param [in] ppQueueHead Transmit queue head address + @param [in] ppQueueTail Transmit queue tail address + @param [in] ppActive Active transmit queue address + @param [in] ppFree Free transmit queue address + + **/ +VOID +EslSocketTxStart ( + IN ESL_PORT * pPort, + IN ESL_PACKET ** ppQueueHead, + IN ESL_PACKET ** ppQueueTail, + IN ESL_IO_MGMT ** ppActive, + IN ESL_IO_MGMT ** ppFree + ) +{ + UINT8 * pBuffer; + ESL_IO_MGMT * pIo; + ESL_PACKET * pNextPacket; + ESL_PACKET * pPacket; + VOID ** ppTokenData; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Get the packet from the queue head + // + pPacket = *ppQueueHead; + pIo = *ppFree; + if (( NULL != pPacket ) && ( NULL != pIo )) { + pSocket = pPort->pSocket; + // + // *ppQueueHead: pSocket->pRxPacketListHead or pSocket->pRxOobPacketListHead + // | + // V + // +------------+ +------------+ +------------+ + // Data | ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL + // +------------+ +------------+ +------------+ + // ^ + // | + // *ppQueueTail: pSocket->pRxPacketListTail or pSocket->pRxOobPacketListTail + // + // + // Remove the packet from the queue + // + pNextPacket = pPacket->pNext; + *ppQueueHead = pNextPacket; + if ( NULL == pNextPacket ) { + *ppQueueTail = NULL; + } + pPacket->pNext = NULL; + + // + // Eliminate the need for IP4 and UDP4 specific routines by + // connecting the token with the TX data control structure here. + // + // +--------------------+ +--------------------+ + // | ESL_IO_MGMT | | ESL_PACKET | + // | | | | + // | +---------------+ +----------------+ | + // | | Token | | Buffer Length | | + // | | TxData --> | Buffer Address | | + // | | | +----------------+---+ + // | | Event | | Data Buffer | + // +----+---------------+ | | + // +--------------------+ + // + // Compute the address of the TxData pointer in the token + // + pBuffer = (UINT8 *)&pIo->Token; + pBuffer = &pBuffer[ pSocket->TxTokenOffset ]; + ppTokenData = (VOID **)pBuffer; + + // + // Compute the address of the TX data control structure in the packet + // + // * EFI_IP4_TRANSMIT_DATA + // * EFI_TCP4_TRANSMIT_DATA + // * EFI_UDP4_TRANSMIT_DATA + // + pBuffer = (UINT8 *)pPacket; + pBuffer = &pBuffer[ pSocket->TxPacketOffset ]; + + // + // Connect the token to the transmit data control structure + // + *ppTokenData = (VOID **)pBuffer; + + // + // Display the results + // + DEBUG (( DEBUG_TX | DEBUG_INFO, + "0x%08x: pIo allocated for pPacket: 0x%08x\r\n", + pIo, + pPacket )); + + // + // Start the transmit operation + // + Status = pPort->pfnTxStart ( pPort->pProtocol.v, + &pIo->Token ); + if ( !EFI_ERROR ( Status )) { + // + // Connect the structures + // + pIo->pPacket = pPacket; + + // + // +-------------+ +-------------+ +-------------+ + // Free | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL + // +-------------+ +-------------+ +-------------+ + // ^ + // | + // *ppFree: pPort->pTxFree or pTxOobFree + // + // + // Remove the IO structure from the queue + // + *ppFree = pIo->pNext; + + // + // *ppActive: pPort->pTxActive or pTxOobActive + // | + // V + // +-------------+ +-------------+ +-------------+ + // Active | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL + // +-------------+ +-------------+ +-------------+ + // + // + // Mark this packet as active + // + pIo->pPacket = pPacket; + pIo->pNext = *ppActive; + *ppActive = pIo; + } + else { + if ( EFI_SUCCESS == pSocket->TxError ) { + pSocket->TxError = Status; + } + + // + // Discard the transmit buffer + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + } + + DBG_EXIT ( ); +} diff --git a/StdLib/EfiSocketLib/Socket.h b/StdLib/EfiSocketLib/Socket.h index 42377eb29c..25133e6f89 100644 --- a/StdLib/EfiSocketLib/Socket.h +++ b/StdLib/EfiSocketLib/Socket.h @@ -30,27 +30,26 @@ #define DEBUG_TX 0x00400000 ///< Display transmit messages #define DEBUG_CLOSE 0x00200000 ///< Display close messages #define DEBUG_CONNECT 0x00100000 ///< Display connect messages +#define DEBUG_OPTION 0x00080000 ///< Display option 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 MAX_TX_DATA ( MAX_RX_DATA * 2 ) ///< Maximum buffered transmit data in bytes #define RX_PACKET_DATA 16384 ///< Maximum number of bytes in a RX packet +#define MAX_UDP_RETRANSMIT 16 ///< UDP retransmit attempts to handle address not mapped -#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 +#define ESL_STRUCTURE_ALIGNMENT_BYTES 15 ///< Number of bytes for structure alignment +#define ESL_STRUCTURE_ALIGNMENT_MASK ( ~ESL_STRUCTURE_ALIGNMENT_BYTES ) ///< Mask to align structures -typedef enum -{ - NETWORK_TYPE_UNKNOWN = 0, - NETWORK_TYPE_RAW, - NETWORK_TYPE_TCP4, - NETWORK_TYPE_TCP6, - NETWORK_TYPE_UDP4, - NETWORK_TYPE_UDP6 -} NETWORK_TYPE; +#define LAYER_SIGNATURE SIGNATURE_32 ('S','k','t','L') ///< ESL_LAYER memory signature +#define SERVICE_SIGNATURE SIGNATURE_32 ('S','k','t','S') ///< ESL_SERVICE memory signature +#define SOCKET_SIGNATURE SIGNATURE_32 ('S','c','k','t') ///< ESL_SOCKET memory signature +#define PORT_SIGNATURE SIGNATURE_32 ('P','o','r','t') ///< ESL_PORT memory signature + +/** + Socket states +**/ typedef enum { SOCKET_STATE_NOT_CONFIGURED = 0, ///< socket call was successful @@ -67,6 +66,10 @@ typedef enum SOCKET_STATE_CLOSED ///< Close call was successful } SOCKET_STATE; + +/** + Port states +**/ typedef enum { PORT_STATE_ALLOCATED = 0, ///< Port allocated @@ -74,102 +77,164 @@ typedef enum PORT_STATE_RX_ERROR, ///< Receive error detected // - // Close state must be last in the list + // Close state must be last in the list! + // + // Using < <= > >= in tests code to detect port close state + // machine has started // 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_CLOSE_DONE, ///< Port close operation complete + PORT_STATE_CLOSE_RX_DONE ///< Receives shutdown } 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 _ESL_IO_MGMT ESL_IO_MGMT;///< Forward declaration +typedef struct _ESL_PACKET ESL_PACKET; ///< Forward declaration +typedef struct _ESL_PORT ESL_PORT; ///< Forward declaration +typedef struct _ESL_SOCKET ESL_SOCKET; ///< Forward declaration + +/** + Receive context for SOCK_RAW sockets using IPv4. +**/ +typedef struct +{ + EFI_IP4_RECEIVE_DATA * pRxData; ///< Receive operation description +} ESL_IP4_RX_DATA; + + +/** + Transmit context for SOCK_RAW sockets using IPv4. +**/ +typedef struct +{ + EFI_IP4_OVERRIDE_DATA Override; ///< Override data + EFI_IP4_TRANSMIT_DATA TxData; ///< Transmit operation description + UINT8 Buffer[ 1 ]; ///< Data buffer +} ESL_IP4_TX_DATA; + +/** + Receive context for SOCK_STREAM and SOCK_SEQPACKET sockets using TCPv4. +**/ 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; + UINT8 Buffer[ RX_PACKET_DATA ]; ///< Data buffer +} ESL_TCP4_RX_DATA; + +/** + Transmit context for SOCK_STREAM and SOCK_SEQPACKET sockets using TCPv4. +**/ typedef struct { EFI_TCP4_TRANSMIT_DATA TxData; ///< Transmit operation description - UINT8 Buffer [ 1 ]; ///< Data buffer -} DT_TCP4_TX_DATA; + UINT8 Buffer[ 1 ]; ///< Data buffer +} ESL_TCP4_TX_DATA; + +/** + Receive context for SOCK_DGRAM sockets using UDPv4. +**/ typedef struct { - EFI_UDP4_SESSION_DATA Session; ///< * Remote network address - EFI_UDP4_RECEIVE_DATA * pRxData; ///< * Receive operation description -} DT_UDP4_RX_DATA; + EFI_UDP4_SESSION_DATA Session; ///< Remote network address + EFI_UDP4_RECEIVE_DATA * pRxData; ///< Receive operation description +} ESL_UDP4_RX_DATA; + +/** + Transmit context for SOCK_DGRAM sockets using UDPv4. +**/ 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; + UINTN RetransmitCount; ///< Retransmit to handle ARP negotiation + UINT8 Buffer[ 1 ]; ///< Data buffer +} ESL_UDP4_TX_DATA; + -typedef struct _DT_PACKET { - DT_PACKET * pNext; ///< Next packet in the receive list +/** + Network specific context for transmit and receive packets. +**/ +typedef struct _ESL_PACKET { + ESL_PACKET * pNext; ///< Next packet in the receive list size_t PacketSize; ///< Size of this data structure + size_t ValidBytes; ///< Length of valid data in bytes + UINT8 * pBuffer; ///< Current data pointer 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; + ESL_IP4_RX_DATA Ip4Rx; ///< Receive operation description + ESL_IP4_TX_DATA Ip4Tx; ///< Transmit operation description + ESL_TCP4_RX_DATA Tcp4Rx; ///< Receive operation description + ESL_TCP4_TX_DATA Tcp4Tx; ///< Transmit operation description + ESL_UDP4_RX_DATA Udp4Rx; ///< Receive operation description + ESL_UDP4_TX_DATA Udp4Tx; ///< Transmit operation description + } Op; ///< Network specific context +} GCC_ESL_PACKET; /** Service control structure The driver uses this structure to manage the network devices. **/ -typedef struct _DT_SERVICE { +typedef struct _ESL_SERVICE { UINTN Signature; ///< Structure identification // // Links // - DT_SERVICE * pNext; ///< Next service in the service list + ESL_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 + CONST ESL_SOCKET_BINDING * pSocketBinding; ///< Name and shutdown routine + EFI_HANDLE Controller; ///< Controller for the service + EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding; ///< 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; + ESL_PORT * pPortList; ///< List of ports using this service +}GCC_ESL_SERVICE; /** - Start the close operation on a TCP4 port. + IO management structure - @param [in] pPort Address of the port structure. - @param [in] bAbort Set TRUE to abort active transfers - @param [in] DebugFlags Flags for debug messages + This structure manages a single operation with the network. +**/ +typedef struct _ESL_IO_MGMT { + ESL_IO_MGMT * pNext; ///< Next TX management structure + ESL_PORT * pPort; ///< Port structure address + ESL_PACKET * pPacket; ///< Packet structure address + union { + EFI_IP4_COMPLETION_TOKEN Ip4Rx; ///< IP4 receive token + EFI_IP4_COMPLETION_TOKEN Ip4Tx; ///< IP4 transmit token + EFI_TCP4_IO_TOKEN Tcp4Rx; ///< TCP4 receive token + EFI_TCP4_IO_TOKEN Tcp4Tx; ///< TCP4 transmit token + EFI_UDP4_COMPLETION_TOKEN Udp4Rx; ///< UDP4 receive token + EFI_UDP4_COMPLETION_TOKEN Udp4Tx; ///< UDP4 transmit token + } Token; ///< Completion token for the network operation +}; + +/** + IP4 context structure + The driver uses this structure to manage the IP4 connections. **/ -typedef -EFI_STATUS -PFN_PORT_CLOSE_START ( - IN DT_PORT * pPort, - IN BOOLEAN bAbort, - IN UINTN DebugFlags - ); +typedef struct { + // + // IP4 context + // + EFI_IP4_MODE_DATA ModeData; ///< IP4 mode data, includes configuration data + EFI_IPv4_ADDRESS DestinationAddress; ///< Default destination address +} ESL_IP4_CONTEXT; + /** TCP4 context structure @@ -180,34 +245,16 @@ 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 + EFI_TCP4_CONFIG_DATA ConfigData; ///< TCP4 configuration data + EFI_TCP4_OPTION Option; ///< TCP4 port options // // Tokens // - EFI_TCP4_LISTEN_TOKEN ListenToken; ///< Listen control + 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; + EFI_TCP4_CLOSE_TOKEN CloseToken; ///< Close control +} ESL_TCP4_CONTEXT; /** UDP4 context structure @@ -218,24 +265,41 @@ 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 +} ESL_UDP4_CONTEXT; - // - // 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; +/** + Configure the network layer. + + @param [in] pProtocol Protocol structure address + @param [in] pConfigData Address of the confiuration data + + @return Returns EFI_SUCCESS if the operation is successfully + started. +**/ +typedef +EFI_STATUS +(* PFN_NET_CONFIGURE) ( + IN VOID * pProtocol, + IN VOID * pConfigData + ); + +/** + Hand an I/O operation to the network layer. + + @param [in] pProtocol Protocol structure address + @param [in] pToken Completion token address + @return Returns EFI_SUCCESS if the operation is successfully + started. +**/ +typedef +EFI_STATUS +(* PFN_NET_IO_START) ( + IN VOID * pProtocol, + IN VOID * pToken + ); /** Port control structure @@ -243,240 +307,75 @@ typedef struct { The driver uses this structure to manager the socket's connection with the network driver. **/ -typedef struct _DT_PORT { +typedef struct _ESL_PORT { UINTN Signature; ///< Structure identification // // List links // - DT_PORT * pLinkService; ///< Link in service port list - DT_PORT * pLinkSocket; ///< Link in socket port list + ESL_PORT * pLinkService; ///< Link in service port list + ESL_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 + ESL_SERVICE * pService; ///< Service for this port + ESL_SOCKET * pSocket; ///< Socket for this port // - // Network use + // Eliminate the pService references during port close // - DT_PORT * pPortList; ///< List of ports managed by this socket - EFI_EVENT WaitAccept; ///< Wait for accept completion + EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding; ///< Service binding for network layer + CONST ESL_SOCKET_BINDING * pSocketBinding; ///< Socket binding for network layer // - // Receive data management + // Port 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 + EFI_HANDLE Handle; ///< Network port handle + PORT_STATE State; ///< State of the port + UINTN DebugFlags; ///< Debug flags used to close the port + BOOLEAN bCloseNow; ///< TRUE = Close the port immediately + BOOLEAN bConfigured; ///< TRUE = Configure call made to network layer + PFN_NET_CONFIGURE pfnConfigure; ///< Configure the network layer // // 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 + BOOLEAN bTxFlowControl; ///< TX flow control applied + PFN_NET_IO_START pfnTxStart; ///< Start a transmit on the network + ESL_IO_MGMT * pTxActive; ///< Normal data queue + ESL_IO_MGMT * pTxFree; ///< Normal free queue - // - // Image data - // - EFI_HANDLE ImageHandle; ///< Image handle + ESL_IO_MGMT * pTxOobActive; ///< Urgent data queue + ESL_IO_MGMT * pTxOobFree; ///< Urgent free queue // - // Network services + // Receive data management // - DT_SERVICE * pTcp4List; ///< List of Tcp4 services - DT_SERVICE * pUdp4List; ///< List of Udp4 services + PFN_NET_IO_START pfnRxCancel; ///< Cancel a receive on the network + PFN_NET_IO_START pfnRxStart; ///< Start a receive on the network + ESL_IO_MGMT * pRxActive; ///< Active receive operation queue + ESL_IO_MGMT * pRxFree; ///< Free structure queue // - // Socket management - // - DT_SOCKET * pSocketList; ///< List of sockets - - // - // TCP4 service + // Protocol specific management data // - 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 -//------------------------------------------------------------------------------ + union { + VOID * v; ///< VOID pointer + EFI_IP4_PROTOCOL * IPv4; ///< IP4 protocol pointer + EFI_TCP4_PROTOCOL * TCPv4; ///< TCP4 protocol pointer + EFI_UDP4_PROTOCOL * UDPv4; ///< UDP4 protocol pointer + } pProtocol; ///< Protocol structure address + union { + ESL_IP4_CONTEXT Ip4; ///< IPv4 management data + ESL_TCP4_CONTEXT Tcp4; ///< TCPv4 management data + ESL_UDP4_CONTEXT Udp4; ///< UDPv4 management data + } Context; ///< Network specific context +}GCC_ESL_PORT; /** 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 @@ -490,293 +389,276 @@ EslSocketPacketFree ( @retval Others Remote address not available **/ +typedef EFI_STATUS -EslTcpAccept4 ( - IN DT_SOCKET * pSocket, +(* PFN_API_ACCEPT) ( + IN ESL_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. + Poll for completion of the connection attempt. - @param [in] SockAddrLen Specifies the length in bytes of the sockaddr structure. + @param [in] pSocket Address of an ::ESL_SOCKET structure. - @retval EFI_SUCCESS - Socket successfully created + @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. **/ +typedef EFI_STATUS -EslTcpBind4 ( - IN DT_SOCKET * pSocket, - IN const struct sockaddr * pSockAddr, - IN socklen_t SockAddrLength +(* PFN_API_CONNECT_POLL) ( + IN ESL_SOCKET * pSocket ); /** - Poll for completion of the connection attempt. + Attempt to connect to a remote TCP port + + This routine starts the connection processing for a SOCK_STREAM + or SOCK_SEQPAKCET socket using the TCP network layer. - The ::TcpConnectPoll4 routine determines when the connection - attempt transitions from being in process to being complete. + This routine is called by ::EslSocketConnect to initiate the TCP + network specific connect operations. - @param [in] pSocket Address of the socket structure. + @param [in] pSocket Address of an ::ESL_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. **/ +typedef EFI_STATUS -EslTcpConnectPoll4 ( - IN DT_SOCKET * pSocket +(* PFN_API_CONNECT_START) ( + IN ESL_SOCKET * pSocket ); /** - Connect to a remote system via the network. + Get the local socket address - The ::TcpConnectStart4= routine starts the connection processing - for a TCP4 port. + @param [in] pPort Address of an ::ESL_PORT structure. - @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 + @param [out] pAddress Network address to receive the local system address **/ -EFI_STATUS -EFIAPI -EslTcpInitialize4 ( - IN DT_SERVICE * pService +typedef +VOID +(* PFN_API_LOCAL_ADDR_GET) ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress ); /** - Get the local socket address + Set the local port address. - @param [in] pSocket Address of the socket structure. + This routine sets the local port address. - @param [out] pAddress Network address to receive the local system address + This support routine is called by ::EslSocketPortAllocate. - @param [in,out] pAddressLength Length of the local network address structure + @param [in] ppPort Address of an ESL_PORT 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] bBindTest TRUE = run bind testing - @retval EFI_SUCCESS - Address available - @retval Other - Failed to get the address + @retval EFI_SUCCESS The operation was successful -**/ + **/ +typedef EFI_STATUS -EslTcpGetLocalAddress4 ( - IN DT_SOCKET * pSocket, - OUT struct sockaddr * pAddress, - IN OUT socklen_t * pAddressLength +(* PFN_API_LOCAL_ADDR_SET) ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest ); /** - Get the remote socket address - - @param [in] pSocket Address of the socket structure. - - @param [out] pAddress Network address to receive the remote system address + Determine if the socket is configured. - @param [in,out] pAddressLength Length of the remote network address structure - @retval EFI_SUCCESS - Address available - @retval Other - Failed to get the address + @param [in] pSocket Address of a ESL_SOCKET structure + + @retval EFI_SUCCESS - The port is connected + @retval EFI_NOT_STARTED - The port is not connected -**/ -EFI_STATUS -EslTcpGetRemoteAddress4 ( - IN DT_SOCKET * pSocket, - OUT struct sockaddr * pAddress, - IN OUT socklen_t * pAddressLength + **/ + typedef + EFI_STATUS + (* PFN_API_IS_CONFIGURED) ( + IN ESL_SOCKET * pSocket ); /** 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 **/ +typedef EFI_STATUS -EslTcpListen4 ( - IN DT_SOCKET * pSocket +(* PFN_API_LISTEN) ( + IN ESL_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. + Get the option value - @param Event The listeen completion event + Retrieve the protocol options one at a time by name. - @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 a ESL_SOCKET structure + @param [in] OptionName Name of the option + @param [out] ppOptionData Buffer to receive address of option value + @param [out] pOptionLength Buffer to receive the option length - @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 + @retval EFI_SUCCESS - Socket data successfully received **/ +typedef 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 +(* PFN_API_OPTION_GET) ( + IN ESL_SOCKET * pSocket, + IN int OptionName, + OUT CONST void ** __restrict ppOptionData, + OUT socklen_t * __restrict pOptionLength ); /** - Close a TCP4 port. + Set the option value - This routine releases the resources allocated by - ::TcpPortAllocate4(). - - @param [in] pPort Address of the port structure. + Adjust the protocol options one at a time by name. - @retval EFI_SUCCESS The port is closed - @retval other Port close error + @param [in] pSocket Address of a ESL_SOCKET structure + @param [in] OptionName Name of the option + @param [in] pOptionValue Buffer containing the option value + @param [in] OptionLength Length of the buffer in bytes -**/ + @retval EFI_SUCCESS - Option successfully set + + **/ +typedef EFI_STATUS -EslTcpPortClose4 ( - IN DT_PORT * pPort +(* PFN_API_OPTION_SET) ( + IN ESL_SOCKET * pSocket, + IN int OptionName, + IN CONST void * pOptionValue, + IN socklen_t OptionLength ); /** - Process the port close completion + Free a receive packet - @param Event The close completion event + This routine performs the network specific operations necessary + to free a receive packet. - @param pPort The DT_PORT structure address + This routine is called by ::EslSocketPortCloseTxDone to free a + receive packet. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + @param [in, out] pRxBytes Address of the count of RX bytes **/ +typedef VOID -EslTcpPortCloseComplete4 ( - IN EFI_EVENT Event, - IN DT_PORT * pPort +(* PFN_API_PACKET_FREE) ( + IN ESL_PACKET * pPacket, + IN OUT size_t * pRxBytes ); /** - Port close state 3 + Initialize the network specific portions of an ::ESL_PORT structure. - Continue the close operation after the receive is complete. + This routine initializes the network specific portions of an + ::ESL_PORT structure for use by the socket. - @param [in] pPort Address of the port structure. + This support routine is called by ::EslSocketPortAllocate + to connect the socket with the underlying network adapter + running the IPv4 protocol. - @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. + @param [in] ppPort Address of an ESL_PORT structure + @param [in] DebugFlags Flags for debug messages -**/ + @retval EFI_SUCCESS - Socket successfully created + + **/ +typedef EFI_STATUS -EslTcpPortCloseRxDone4 ( - IN DT_PORT * pPort +(* PFN_API_PORT_ALLOC) ( + IN ESL_PORT * pPort, + IN UINTN DebugFlags ); /** - Start the close operation on a TCP4 port. + Close a network specific 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 + This routine releases the resources allocated by the + network specific PortAllocate routine. + + This routine is called by ::EslSocketPortCloseRxDone as + the last step of closing processing. + See the \ref PortCloseStateMachine section. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @retval EFI_SUCCESS The port is closed + @retval other Port close error **/ +typedef EFI_STATUS -EslTcpPortCloseStart4 ( - IN DT_PORT * pPort, - IN BOOLEAN bAbort, - IN UINTN DebugFlags +(* PFN_API_PORT_CLOSE) ( + IN ESL_PORT * pPort ); /** - Port close state 2 + Perform the network specific close operation on the port. - Continue the close operation after the transmission is complete. + This routine performs the network specific operation to + shutdown receive operations on the port. - @param [in] pPort Address of the port structure. + This routine is called by the ::EslSocketPortCloseTxDone + routine after the port completes all of the transmission. + @param [in] pPort Address of an ::ESL_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. **/ +typedef EFI_STATUS -EslTcpPortCloseTxDone4 ( - IN DT_PORT * pPort +(* PFN_API_PORT_CLOSE_OP) ( + IN ESL_PORT * pPort ); /** Receive data from a network connection. + This routine attempts to return buffered data to the caller. The + data is removed from the urgent queue if the message flag MSG_OOB + is specified, otherwise data is removed from the normal queue. + See the \ref ReceiveEngine section. - @param [in] pSocket Address of a DT_SOCKET structure + This routine is called by ::EslSocketReceive to handle the network + specific receive operation. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pPacket Address of an ::ESL_PACKET structure. - @param [in] Flags Message control flags + @param [in] pbConsumePacket Address of a BOOLEAN indicating if the packet is to be consumed @param [in] BufferLength Length of the the buffer @@ -786,550 +668,850 @@ EslTcpPortCloseTxDone4 ( @param [out] pAddress Network address to receive the remote system address - @param [in,out] pAddressLength Length of the remote network address structure + @param [out] pSkipBytes Address to receive the number of bytes skipped - @retval EFI_SUCCESS - Socket data successfully received + @return Returns the address of the next free byte in the buffer. **/ -EFI_STATUS -EslTcpReceive4 ( - IN DT_SOCKET * pSocket, - IN INT32 Flags, +typedef +UINT8 * +(* PFN_API_RECEIVE) ( + IN ESL_PORT * pPort, + IN ESL_PACKET * pPacket, + IN BOOLEAN * pbConsumePacket, IN size_t BufferLength, IN UINT8 * pBuffer, OUT size_t * pDataLength, OUT struct sockaddr * pAddress, - IN OUT socklen_t * pAddressLength + OUT size_t * pSkipBytes ); /** - Cancel the receive operations + Get the remote socket address - @param [in] pSocket Address of a DT_SOCKET structure - - @retval EFI_SUCCESS - The cancel was successful + @param [in] pPort Address of an ::ESL_PORT structure. - **/ -EFI_STATUS -EslTcpRxCancel4 ( - IN DT_SOCKET * pSocket + @param [out] pAddress Network address to receive the remote system address + +**/ +typedef +VOID +(* PFN_API_REMOTE_ADDR_GET) ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress ); /** - Process the receive completion + Set the remote address - Buffer the data that was just received. + This routine sets the remote address in the port. - @param Event The receive completion event + This routine is called by ::EslSocketConnect to specify the + remote network address. - @param pPort The DT_PORT structure address + @param [in] pPort Address of an ::ESL_PORT structure. -**/ -VOID -EslTcpRxComplete4 ( - IN EFI_EVENT Event, - IN DT_PORT * pPort - ); + @param [in] pSockAddr Network address of the remote system. -/** - Start a receive operation + @param [in] SockAddrLength Length in bytes of the network address. - @param [in] pPort Address of the DT_PORT structure. + @retval EFI_SUCCESS The operation was successful **/ -VOID -EslTcpRxStart4 ( - IN DT_PORT * pPort +typedef +EFI_STATUS +(* PFN_API_REMOTE_ADDR_SET) ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength ); /** - Shutdown the TCP4 service. + Process the receive completion - This routine undoes the work performed by ::TcpInitialize4. + This routine handles the receive completion event. - @param [in] pService DT_SERVICE structure address + This routine is called by the low level network driver when + data is received. + + @param [in] Event The receive completion event + + @param [in] pIo The address of an ::ESL_IO_MGMT structure **/ +typedef VOID -EFIAPI -EslTcpShutdown4 ( - IN DT_SERVICE * pService +(* PFN_API_RX_COMPLETE) ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo ); /** - Determine if the socket is configured. + Start a receive operation + This routine prepares a packet for the receive operation. + See the \ref ReceiveEngine section. - @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 + This support routine is called by EslSocketRxStart. + + @param [in] pPort Address of an ::ESL_PORT structure. + @param [in] pIo Address of an ::ESL_IO_MGMT structure. **/ - EFI_STATUS - EslTcpSocketIsConfigured4 ( - IN DT_SOCKET * pSocket +typedef +VOID +(* PFN_API_RX_START) ( + IN ESL_PORT * pPort, + IN ESL_IO_MGMT * pIo ); /** 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 ESL_SOCKET structure - @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 - **/ +**/ +typedef EFI_STATUS -EslTcpTxBuffer4 ( - IN DT_SOCKET * pSocket, +(* PFN_API_TRANSMIT) ( + IN ESL_SOCKET * pSocket, IN int Flags, IN size_t BufferLength, IN CONST UINT8 * pBuffer, - OUT size_t * pDataLength + OUT size_t * pDataLength, + IN const struct sockaddr * pAddress, + IN socklen_t AddressLength ); /** - Process the normal data transmit completion + Process the transmit completion - @param Event The normal transmit completion event + This routine calls ::EslSocketTxComplete to handle the + transmit completion. - @param pPort The DT_PORT structure address + This routine is called by the network layers upon the completion + of a transmit operation. + + @param [in] Event The urgent transmit completion event + + @param [in] pIo The ESL_IO_MGMT structure address **/ +typedef VOID -EslTcpTxComplete4 ( +(* PFN_API_TX_COMPLETE) ( IN EFI_EVENT Event, - IN DT_PORT * pPort + IN ESL_IO_MGMT * pIo ); /** - Process the urgent data transmit completion + Socket type control structure + + This driver uses this structure to define the API for the socket type. +**/ +typedef struct { + CONST CHAR8 * pName; ///< Protocol name + int DefaultProtocol; ///< Default protocol + UINTN ConfigDataOffset; ///< Offset in ::ESL_PORT to the configuration data + UINTN ServiceListOffset; ///< Offset in ::ESL_LAYER for the list of services + socklen_t MinimumAddressLength; ///< Minimum address length in bytes + socklen_t AddressLength; ///< Address length in bytes + sa_family_t AddressFamily; ///< Address family + UINTN RxPacketBytes; ///< Length of the RX packet allocation + UINTN RxZeroBytes; ///< Number of bytes to zero in RX packet + UINTN RxBufferOffset; ///< Offset of buffer address in ESL_IO_MGMT structure + BOOLEAN bOobSupported; ///< TRUE if out-of-band messages are supported + int BindTestErrno; ///< errno value if EslSocketBindTest fails + PFN_API_ACCEPT pfnAccept; ///< Accept a network connection + PFN_API_CONNECT_POLL pfnConnectPoll; ///< Poll for connection complete + PFN_API_CONNECT_START pfnConnectStart; ///< Start the connection to a remote system + PFN_API_IS_CONFIGURED pfnIsConfigured; ///< Determine if the socket is configured + PFN_API_LOCAL_ADDR_GET pfnLocalAddrGet; ///< Get the local address + PFN_API_LOCAL_ADDR_SET pfnLocalAddrSet; ///< Set the local address + PFN_API_LISTEN pfnListen; ///< Listen for connections on known server port + PFN_API_OPTION_GET pfnOptionGet; ///< Get the option value + PFN_API_OPTION_SET pfnOptionSet; ///< Set the option value + PFN_API_PACKET_FREE pfnPacketFree; ///< Free the receive packet + PFN_API_PORT_ALLOC pfnPortAllocate; ///< Allocate the network specific resources for the port + PFN_API_PORT_CLOSE pfnPortClose; ///< Close the network specific resources for the port + PFN_API_PORT_CLOSE_OP pfnPortCloseOp; ///< Perform the close operation on the port + BOOLEAN bPortCloseComplete; ///< TRUE = Close is complete after close operation + PFN_API_RECEIVE pfnReceive; ///< Attempt to receive some data + PFN_API_REMOTE_ADDR_GET pfnRemoteAddrGet; ///< Get remote address + PFN_API_REMOTE_ADDR_SET pfnRemoteAddrSet; ///< Set the remote system address + PFN_API_RX_COMPLETE pfnRxComplete; ///< RX completion + PFN_API_RX_START pfnRxStart; ///< Start a network specific receive operation + PFN_API_TRANSMIT pfnTransmit; ///< Attempt to buffer a packet for transmit + PFN_API_TX_COMPLETE pfnTxComplete; ///< TX completion for normal data + PFN_API_TX_COMPLETE pfnTxOobComplete; ///< TX completion for urgent data +} ESL_PROTOCOL_API; - @param Event The urgent transmit completion event - @param pPort The DT_PORT structure address +/** + Socket control structure + The driver uses this structure to manage the socket. **/ -VOID -EslTcpTxOobComplete4 ( - IN EFI_EVENT Event, - IN DT_PORT * pPort - ); +typedef struct _ESL_SOCKET { + UINTN Signature; ///< Structure identification -/** - Transmit data using a network connection. + // + // Protocol binding + // + EFI_SOCKET_PROTOCOL SocketProtocol; ///< Socket protocol declaration + CONST ESL_PROTOCOL_API * pApi; ///< API for the protocol + + // + // Socket management + // + ESL_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 + UINT32 DebugFlags; ///< Debug flags + // + // Socket options + // + BOOLEAN bListenCalled; ///< TRUE if listen was successfully called + BOOLEAN bOobInLine; ///< TRUE if out-of-band messages are to be received inline with normal data + BOOLEAN bIncludeHeader; ///< TRUE if including the IP header - @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 + // + // 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 - **/ -VOID -EslTcpTxStart4 ( - IN DT_PORT * pPort, - IN EFI_TCP4_IO_TOKEN * pToken, - IN DT_PACKET ** ppQueueHead, - IN DT_PACKET ** ppQueueTail, - IN DT_PACKET ** ppPacket - ); + BOOLEAN bRxDisable; ///< Receive disabled via shutdown + size_t RxBytes; ///< Total Rx bytes + size_t RxOobBytes; ///< Urgent Rx bytes + EFI_STATUS RxError; ///< Error during receive -//------------------------------------------------------------------------------ -// Udp4 Routines -//------------------------------------------------------------------------------ + 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 + ESL_SOCKET * pFifoHead; ///< Head of the FIFO + ESL_SOCKET * pFifoTail; ///< Tail of the FIFO + ESL_SOCKET * pNextConnection; ///< Link in the FIFO + + // + // Network use + // + ESL_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 + ESL_PACKET * pRxFree; ///< Free packet list + ESL_PACKET * pRxOobPacketListHead;///< Urgent data list head + ESL_PACKET * pRxOobPacketListTail;///< Urgent data list tail + ESL_PACKET * pRxPacketListHead; ///< Normal data list head + ESL_PACKET * pRxPacketListTail; ///< Normal data list tail + + // + // Transmit data management + // + UINTN TxPacketOffset; ///< Offset for data pointer in ::ESL_PACKET + UINTN TxTokenEventOffset; ///< Offset to the Event in the TX token + UINTN TxTokenOffset; ///< Offset for data pointer in TX token + UINT32 MaxTxBuf; ///< Maximum size of the transmit buffer + ESL_PACKET * pTxOobPacketListHead;///< Urgent data list head + ESL_PACKET * pTxOobPacketListTail;///< Urgent data list tail + ESL_PACKET * pTxPacketListHead; ///< Normal data list head + ESL_PACKET * pTxPacketListTail; ///< Normal data list tail +}GCC_ESL_SOCKET; + +#define SOCKET_FROM_PROTOCOL(a) CR (a, ESL_SOCKET, SocketProtocol, SOCKET_SIGNATURE) ///< Locate ESL_SOCKET from protocol /** - Bind a name to a socket. + Socket layer control structure - The ::UdpBind4 routine connects a name to a UDP4 stack on the local machine. + The driver uses this structure to manage the driver. +**/ +typedef struct { + UINTN Signature; ///< Structure identification - @param [in] pSocket Address of the socket structure. + // + // Service binding interface + // + CONST EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding; ///< Driver's binding - @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. + // + // Image data + // + EFI_HANDLE ImageHandle; ///< Image handle - @param [in] SockAddrLen Specifies the length in bytes of the sockaddr structure. + // + // Network services + // + ESL_SERVICE * pIp4List; ///< List of Ip4 services + ESL_SERVICE * pTcp4List; ///< List of Tcp4 services + ESL_SERVICE * pUdp4List; ///< List of Udp4 services - @retval EFI_SUCCESS - Socket successfully created + // + // Socket management + // + ESL_SOCKET * pSocketList; ///< List of sockets +} ESL_LAYER; - **/ -EFI_STATUS -EslUdpBind4 ( - IN DT_SOCKET * pSocket, - IN const struct sockaddr * pSockAddr, - IN socklen_t SockAddrLength - ); +#define LAYER_FROM_SERVICE(a) CR (a, ESL_LAYER, ServiceBinding, LAYER_SIGNATURE) ///< Locate ESL_LAYER from service binding -/** - Initialize the UDP4 service. +//------------------------------------------------------------------------------ +// Data +//------------------------------------------------------------------------------ + +extern ESL_LAYER mEslLayer; + +extern CONST ESL_PROTOCOL_API cEslIp4Api; +extern CONST ESL_PROTOCOL_API cEslTcp4Api; +extern CONST ESL_PROTOCOL_API cEslUdp4Api; - This routine initializes the UDP4 service after its service binding - protocol was located on a controller. +extern CONST EFI_SERVICE_BINDING_PROTOCOL mEfiServiceBinding; - @param [in] pService DT_SERVICE structure address +//------------------------------------------------------------------------------ +// Socket Support Routines +//------------------------------------------------------------------------------ + +/** + Allocate and initialize a ESL_SOCKET structure. + + This support function allocates an ::ESL_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. - @retval EFI_SUCCESS The service was properly initialized - @retval other A failure occurred during the service initialization + @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 an ::ESL_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 available to create + the child + @retval other The child handle was not created + **/ EFI_STATUS EFIAPI -EslUdpInitialize4 ( - IN DT_SERVICE * pService +EslSocketAllocate ( + IN OUT EFI_HANDLE * pChildHandle, + IN UINTN DebugFlags, + IN OUT ESL_SOCKET ** ppSocket ); /** - Allocate and initialize a DT_PORT structure. + Test the bind configuration. - @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 + @param [in] pPort Address of the ::ESL_PORT structure. + @param [in] ErrnoValue errno value if test fails - @retval EFI_SUCCESS - Socket successfully created + @retval EFI_SUCCESS The connection was successfully established. + @retval Others The connection attempt failed. **/ 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 +EslSocketBindTest ( + IN ESL_PORT * pPort, + IN int ErrnoValue ); /** - Close a UDP4 port. + Copy a fragmented buffer into a destination buffer. - This routine releases the resources allocated by - ::UdpPortAllocate4(). - - @param [in] pPort Address of the port structure. + This support routine copies a fragmented buffer to the caller specified buffer. - @retval EFI_SUCCESS The port is closed - @retval other Port close error + This routine is called by ::EslIp4Receive and ::EslUdp4Receive. -**/ -EFI_STATUS -EslUdpPortClose4 ( - IN DT_PORT * pPort - ); + @param [in] FragmentCount Number of fragments in the table -/** - Start the close operation on a UDP4 port, state 1. + @param [in] pFragmentTable Address of an EFI_IP4_FRAGMENT_DATA structure - 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 + @param [in] BufferLength Length of the the buffer - @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. + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @return Returns the address of the next free byte in the buffer. **/ -EFI_STATUS -EslUdpPortCloseStart4 ( - IN DT_PORT * pPort, - IN BOOLEAN bCloseNow, - IN UINTN DebugFlags +UINT8 * +EslSocketCopyFragmentedBuffer ( + IN UINT32 FragmentCount, + IN EFI_IP4_FRAGMENT_DATA * pFragmentTable, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength ); /** - Port close state 2 + Free the ESL_IO_MGMT event and structure - Continue the close operation after the transmission is complete. + This support routine walks the free list to close the event in + the ESL_IO_MGMT structure and remove the structure from the free + list. - @param [in] pPort Address of the port structure. + See the \ref TransmitEngine section. - @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. + @param [in] pPort Address of an ::ESL_PORT structure + @param [in] ppFreeQueue Address of the free queue head + @param [in] DebugFlags Flags for debug messages + @param [in] pEventName Zero terminated string containing the event name + + @retval EFI_SUCCESS - The structures were properly initialized **/ EFI_STATUS -EslUdpPortCloseTxDone4 ( - IN DT_PORT * pPort +EslSocketIoFree ( + IN ESL_PORT * pPort, + IN ESL_IO_MGMT ** ppFreeQueue, + IN UINTN DebugFlags, + IN CHAR8 * pEventName ); /** - Connect to a remote system via the network. + Initialize the ESL_IO_MGMT structures - The ::UdpConnectStart4= routine sets the remote address for the connection. + This support routine initializes the ESL_IO_MGMT structure and + places them on to a free list. - @param [in] pSocket Address of the socket structure. + This routine is called by the PortAllocate routines to prepare + the transmit engines. See the \ref TransmitEngine section. - @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. + @param [in] pPort Address of an ::ESL_PORT structure + @param [in, out] ppIo Address containing the first structure address. Upon + return this buffer contains the next structure address. + @param [in] TokenCount Number of structures to initialize + @param [in] ppFreeQueue Address of the free queue head + @param [in] DebugFlags Flags for debug messages + @param [in] pEventName Zero terminated string containing the event name + @param [in] pfnCompletion Completion routine address - **/ + @retval EFI_SUCCESS - The structures were properly initialized + +**/ EFI_STATUS -EslUdpConnect4 ( - IN DT_SOCKET * pSocket, - IN const struct sockaddr * pSockAddr, - IN socklen_t SockAddrLength +EslSocketIoInit ( + IN ESL_PORT * pPort, + IN ESL_IO_MGMT ** ppIo, + IN UINTN TokenCount, + IN ESL_IO_MGMT ** ppFreeQueue, + IN UINTN DebugFlags, + IN CHAR8 * pEventName, + IN EFI_EVENT_NOTIFY pfnCompletion ); /** - Get the local socket address + Determine if the socket is configured - @param [in] pSocket Address of the socket structure. + This support routine is called to determine if the socket if the + configuration call was made to the network layer. The following + routines call this routine to verify that they may be successful + in their operations: + - @param [out] pAddress Network address to receive the local system address + @param [in] pSocket Address of an ::ESL_SOCKET structure - @param [in,out] pAddressLength Length of the local network address structure - - @retval EFI_SUCCESS - Address available - @retval Other - Failed to get the address + @retval EFI_SUCCESS - The socket is configured **/ EFI_STATUS -EslUdpGetLocalAddress4 ( - IN DT_SOCKET * pSocket, - OUT struct sockaddr * pAddress, - IN OUT socklen_t * pAddressLength +EslSocketIsConfigured ( + IN ESL_SOCKET * pSocket ); /** - Get the remote socket address - - @param [in] pSocket Address of the socket structure. + Allocate a packet for a receive or transmit operation - @param [out] pAddress Network address to receive the remote system address + This support routine is called by ::EslSocketRxStart and the + network specific TxBuffer routines to get buffer space for the + next operation. - @param [in,out] pAddressLength Length of the remote network address structure + @param [in] ppPacket Address to receive the ::ESL_PACKET structure + @param [in] LengthInBytes Length of the packet structure + @param [in] ZeroBytes Length of packet to zero + @param [in] DebugFlags Flags for debug messages - @retval EFI_SUCCESS - Address available - @retval Other - Failed to get the address + @retval EFI_SUCCESS - The packet was allocated successfully -**/ + **/ EFI_STATUS -EslUdpGetRemoteAddress4 ( - IN DT_SOCKET * pSocket, - OUT struct sockaddr * pAddress, - IN OUT socklen_t * pAddressLength +EslSocketPacketAllocate ( + IN ESL_PACKET ** ppPacket, + IN size_t LengthInBytes, + IN size_t ZeroBytes, + IN UINTN DebugFlags ); /** - 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. + Free a packet used for receive or transmit operation - 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. + This support routine is called by the network specific Close + and TxComplete routines and during error cases in RxComplete + and TxBuffer. Note that the network layers typically place + receive packets on the ESL_SOCKET::pRxFree list for reuse. - @param [in] pSocket Address of a DT_SOCKET structure + @param [in] pPacket Address of an ::ESL_PACKET structure + @param [in] DebugFlags Flags for debug messages - @param [in] Flags Message control flags + @retval EFI_SUCCESS - The packet was allocated successfully - @param [in] BufferLength Length of the the buffer + **/ +EFI_STATUS +EslSocketPacketFree ( + IN ESL_PACKET * pPacket, + IN UINTN DebugFlags + ); - @param [in] pBuffer Address of a buffer to receive the data. +/** + Allocate and initialize a ESL_PORT structure. - @param [in] pDataLength Number of received data bytes in the buffer. + This routine initializes an ::ESL_PORT structure for use by + the socket. This routine calls a routine via + ESL_PROTOCOL_API::pfnPortAllocate to initialize the network + specific resources. The resources are released later by the + \ref PortCloseStateMachine. - @param [out] pAddress Network address to receive the remote system address + This support routine is called by ::EslSocketBind and + ::EslTcp4ListenComplete to connect the socket with the + underlying network adapter to the socket. - @param [in,out] pAddressLength Length of the remote network address structure + @param [in] pSocket Address of an ::ESL_SOCKET structure. + @param [in] pService Address of an ::ESL_SERVICE structure. + @param [in] ChildHandle TCP4 child handle + @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] bBindTest TRUE if EslSocketBindTest should be called + @param [in] DebugFlags Flags for debug messages + @param [out] ppPort Buffer to receive new ::ESL_PORT structure address - @retval EFI_SUCCESS - Socket data successfully received + @retval EFI_SUCCESS - Socket successfully created -**/ + **/ 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 +EslSocketPortAllocate ( + IN ESL_SOCKET * pSocket, + IN ESL_SERVICE * pService, + IN EFI_HANDLE ChildHandle, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest, + IN UINTN DebugFlags, + OUT ESL_PORT ** ppPort ); /** - Cancel the receive operations - - @param [in] pSocket Address of a DT_SOCKET structure + Close a port. + + This routine releases the resources allocated by ::EslSocketPortAllocate. + This routine calls ESL_PROTOCOL_API::pfnPortClose to release the network + specific resources. + + This routine is called by: + + See the \ref PortCloseStateMachine section. - @retval EFI_SUCCESS - The cancel was successful + @param [in] pPort Address of an ::ESL_PORT structure. - **/ + @retval EFI_SUCCESS The port is closed + @retval other Port close error + +**/ EFI_STATUS -EslUdpRxCancel4 ( - IN DT_SOCKET * pSocket +EslSocketPortClose ( + IN ESL_PORT * pPort ); /** - Process the receive completion + Process the port close completion event - 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. + This routine attempts to complete the port close operation. - @param Event The receive completion event + This routine is called by the TCP layer upon completion of + the close operation. + See the \ref PortCloseStateMachine section. - @param pPort The DT_PORT structure address + @param [in] Event The close completion event + + @param [in] pPort Address of an ::ESL_PORT structure. **/ VOID -EslUdpRxComplete4 ( +EslSocketPortCloseComplete ( IN EFI_EVENT Event, - IN DT_PORT * pPort + IN ESL_PORT * pPort ); /** - Start a receive operation + Port close state 3 - @param [in] pPort Address of the DT_PORT structure. + This routine determines the state of the receive operations and + continues the close operation after the pending receive operations + are cancelled. - **/ -VOID -EslUdpRxStart4 ( - IN DT_PORT * pPort + This routine is called by + + to determine the state of the receive operations. + See the \ref PortCloseStateMachine section. + + @param [in] pPort Address of an ::ESL_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 +EslSocketPortCloseRxDone ( + IN ESL_PORT * pPort ); /** - Determine if the socket is configured. + Start the close operation on a port, state 1. + This routine marks the port as closed and initiates the \ref + PortCloseStateMachine. The first step is to allow the \ref + TransmitEngine to run down. - @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 + This routine is called by ::EslSocketCloseStart to initiate the socket + network specific close operation on the socket. - **/ - EFI_STATUS - EslUdpSocketIsConfigured4 ( - IN DT_SOCKET * pSocket + @param [in] pPort Address of an ::ESL_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 +EslSocketPortCloseStart ( + IN ESL_PORT * pPort, + IN BOOLEAN bCloseNow, + IN UINTN DebugFlags ); /** - Process the transmit completion + Port close state 2 + + This routine determines the state of the transmit engine and + continue the close operation after the transmission is complete. + The next step is to stop the \ref ReceiveEngine. + See the \ref PortCloseStateMachine section. - @param Event The normal transmit completion event + This routine is called by ::EslSocketPortCloseStart to determine + if the transmission is complete. - @param pPort The DT_PORT structure address + @param [in] pPort Address of an ::ESL_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. **/ -VOID -EslUdpTxComplete4 ( - IN EFI_EVENT Event, - IN DT_PORT * pPort +EFI_STATUS +EslSocketPortCloseTxDone ( + IN ESL_PORT * pPort ); /** - Shutdown the UDP4 service. + Cancel the receive operations - This routine undoes the work performed by ::UdpInitialize4. + This routine cancels a pending receive operation. + See the \ref ReceiveEngine section. - @param [in] pService DT_SERVICE structure address + This routine is called by ::EslSocketShutdown when the socket + layer is being shutdown. -**/ + @param [in] pPort Address of an ::ESL_PORT structure + @param [in] pIo Address of an ::ESL_IO_MGMT structure + + **/ VOID -EFIAPI -EslUdpShutdown4 ( - IN DT_SERVICE * pService +EslSocketRxCancel ( + IN ESL_PORT * pPort, + IN ESL_IO_MGMT * pIo ); /** - Buffer data for transmission over a network connection. + Process the receive completion - 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. + This routine queues the data in FIFO order in either the urgent + or normal data queues depending upon the type of data received. + See the \ref ReceiveEngine section. - Transmission errors are returned during the next transmission or - during the close operation. Only buffering errors are returned - during the current transmission attempt. + This routine is called when some data is received by: + - @param [in] pSocket Address of a DT_SOCKET structure + @param [in] pIo Address of an ::ESL_IO_MGMT structure + @param [in] Status Receive status + @param [in] LengthInBytes Length of the receive data + @param [in] bUrgent TRUE if urgent data is received and FALSE + for normal data. - @param [in] Flags Message control flags +**/ +VOID +EslSocketRxComplete ( + IN ESL_IO_MGMT * pIo, + IN EFI_STATUS Status, + IN UINTN LengthInBytes, + IN BOOLEAN bUrgent + ); - @param [in] BufferLength Length of the the buffer +/** + Start a receive operation - @param [in] pBuffer Address of a buffer to receive the data. + This routine posts a receive buffer to the network adapter. + See the \ref ReceiveEngine section. - @param [in] pDataLength Number of received data bytes in the buffer. + This support routine is called by: + - @param [in] pAddress Network address of the remote system address + @param [in] pPort Address of an ::ESL_PORT structure. - @param [in] AddressLength Length of the remote network address structure + **/ +VOID +EslSocketRxStart ( + IN ESL_PORT * pPort + ); - @retval EFI_SUCCESS - Socket data successfully buffered +/** + Complete the transmit operation + + This support routine handles the transmit completion processing for + the various network layers. It frees the ::ESL_IO_MGMT structure + and and frees packet resources by calling ::EslSocketPacketFree. + Transmit errors are logged in ESL_SOCKET::TxError. + See the \ref TransmitEngine section. + + This routine is called by: + + + @param [in] pIo Address of an ::ESL_IO_MGMT structure + @param [in] LengthInBytes Length of the data in bytes + @param [in] Status Transmit operation status + @param [in] pQueueType Zero terminated string describing queue type + @param [in] ppQueueHead Transmit queue head address + @param [in] ppQueueTail Transmit queue tail address + @param [in] ppActive Active transmit queue address + @param [in] ppFree Free transmit queue address -**/ -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 + **/ +VOID +EslSocketTxComplete ( + IN ESL_IO_MGMT * pIo, + IN UINT32 LengthInBytes, + IN EFI_STATUS Status, + IN CONST CHAR8 * pQueueType, + IN ESL_PACKET ** ppQueueHead, + IN ESL_PACKET ** ppQueueTail, + IN ESL_IO_MGMT ** ppActive, + IN ESL_IO_MGMT ** ppFree ); /** Transmit data using a network connection. - @param [in] pPort Address of a DT_PORT structure + This support routine starts a transmit operation on the + underlying network layer. + + The network specific code calls this routine to start a + transmit operation. See the \ref TransmitEngine section. + + @param [in] pPort Address of an ::ESL_PORT structure + @param [in] ppQueueHead Transmit queue head address + @param [in] ppQueueTail Transmit queue tail address + @param [in] ppActive Active transmit queue address + @param [in] ppFree Free transmit queue address **/ VOID -EslUdpTxStart4 ( - IN DT_PORT * pPort +EslSocketTxStart ( + IN ESL_PORT * pPort, + IN ESL_PACKET ** ppQueueHead, + IN ESL_PACKET ** ppQueueTail, + IN ESL_IO_MGMT ** ppActive, + IN ESL_IO_MGMT ** ppFree ); //------------------------------------------------------------------------------ diff --git a/StdLib/EfiSocketLib/Tcp4.c b/StdLib/EfiSocketLib/Tcp4.c index b489608a5b..ec03b96bc1 100644 --- a/StdLib/EfiSocketLib/Tcp4.c +++ b/StdLib/EfiSocketLib/Tcp4.c @@ -10,19 +10,88 @@ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + \section ConnectionManagement Connection Management + + The ::EslTcp4Listen routine initially places the SOCK_STREAM or + SOCK_SEQPACKET socket into a listen state. When a remote machine + makes a connection to the socket, the TCPv4 network layer calls + ::EslTcp4ListenComplete to complete the connection processing. + EslTcp4ListenComplete manages the connections by placing them in + FIFO order in a queue to be serviced by the application. When the + number of connections exceeds the backlog (ESL_SOCKET::MaxFifoDepth), + the new connection is closed. Eventually, the application indirectly + calls ::EslTcp4Accept to remove the next connection from the queue + and get the associated socket. + **/ #include "Socket.h" +/** + Attempt to connect to a remote TCP port + + This routine starts the connection processing for a SOCK_STREAM + or SOCK_SEQPAKCET socket using the TCPv4 network layer. It + configures the local TCPv4 connection point and then attempts to + connect to a remote system. Upon completion, the + ::EslTcp4ConnectComplete routine gets called with the connection + status. + + This routine is called by ::EslSocketConnect to initiate the TCPv4 + network specific connect operations. The connection processing is + initiated by this routine and finished by ::EslTcp4ConnectComplete. + This pair of routines walks through the list of local TCPv4 + connection points until a connection to the remote system is + made. + + @param [in] pSocket Address of an ::ESL_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 +EslTcp4ConnectStart ( + IN ESL_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. + + The TCPv4 layer calls this routine when a connection is made to + the socket in the listen state. See the + \ref ConnectionManagement section. + + @param [in] Event The listen completion event + + @param [in] pPort Address of an ::ESL_PORT structure. + +**/ +VOID +EslTcp4ListenComplete ( + IN EFI_EVENT Event, + IN ESL_PORT * pPort + ); + + /** 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. + This routine waits for a network connection to the socket and + returns the remote network address to the caller if requested. + + This routine is called by ::EslSocketAccept to handle the TCPv4 protocol + specific accept operations for SOCK_STREAM and SOCK_SEQPACKET sockets. + See the \ref ConnectionManagement section. - @param [in] pSocket Address of the socket structure. + @param [in] pSocket Address of an ::ESL_SOCKET structure. @param [in] pSockAddr Address of a buffer to receive the remote network address. @@ -36,15 +105,15 @@ **/ EFI_STATUS -EslTcpAccept4 ( - IN DT_SOCKET * pSocket, +EslTcp4Accept ( + IN ESL_SOCKET * pSocket, IN struct sockaddr * pSockAddr, IN OUT socklen_t * pSockAddrLength ) { - DT_PORT * pPort; + ESL_PORT * pPort; struct sockaddr_in * pRemoteAddress; - DT_TCP4_CONTEXT * pTcp4; + ESL_TCP4_CONTEXT * pTcp4; UINT32 RemoteAddress; EFI_STATUS Status; @@ -102,302 +171,37 @@ EslTcpAccept4 ( /** - 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 + Process the remote connection completion event. - A connection attempt to a remote system has just completed when - this routine is invoked. Release the port in the case of an + This routine handles the completion of a connection attempt. It + releases the port (TCPv4 adapter connection) 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. + connection attempt was successful then this routine releases all + of the other ports. - @param Event The connect completion event + This routine is called by the TCPv4 layer when a connect request + completes. It sets the ESL_SOCKET::bConnected flag to notify the + ::EslTcp4ConnectComplete routine that the connection is available. + The flag is set when the connection is established or no more ports + exist in the list. The connection status is passed via + ESL_SOCKET::ConnectStatus. - @param pPort The DT_PORT structure address + @param [in] Event The connect completion event + + @param [in] pPort Address of an ::ESL_PORT structure. **/ VOID -EslTcpConnectComplete4 ( +EslTcp4ConnectComplete ( IN EFI_EVENT Event, - IN DT_PORT * pPort + IN ESL_PORT * pPort ) { BOOLEAN bRemoveFirstPort; BOOLEAN bRemovePorts; - DT_PORT * pNextPort; - DT_SOCKET * pSocket; - DT_TCP4_CONTEXT * pTcp4; + ESL_PORT * pNextPort; + ESL_SOCKET * pSocket; + ESL_TCP4_CONTEXT * pTcp4; EFI_STATUS Status; DBG_ENTER ( ); @@ -422,10 +226,10 @@ EslTcpConnectComplete4 ( 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.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 )); // @@ -440,17 +244,17 @@ EslTcpConnectComplete4 ( 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.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 ); + Status = EslSocketPortClose ( pPort ); if ( !EFI_ERROR ( Status )) { DEBUG (( DEBUG_CONNECT, "0x%08x: Port closed\r\n", @@ -466,7 +270,7 @@ EslTcpConnectComplete4 ( // // Try to connect using the next port // - Status = EslTcpConnectAttempt4 ( pSocket ); + Status = EslTcp4ConnectStart ( pSocket ); if ( EFI_NOT_READY != Status ) { pSocket->ConnectStatus = Status; bRemoveFirstPort = TRUE; @@ -490,7 +294,7 @@ EslTcpConnectComplete4 ( // while ( NULL != pPort ) { pNextPort = pPort->pLinkSocket; - EslTcpPortClose4 ( pPort ); + EslSocketPortClose ( pPort ); if ( !EFI_ERROR ( Status )) { DEBUG (( DEBUG_CONNECT, "0x%08x: Port closed\r\n", @@ -518,10 +322,16 @@ EslTcpConnectComplete4 ( /** Poll for completion of the connection attempt. - The ::TcpConnectPoll4 routine determines when the connection - attempt transitions from being in process to being complete. + This routine polls the ESL_SOCKET::bConnected flag to determine + when the connection attempt is complete. + + This routine is called from ::EslSocketConnect to determine when + the connection is complete. The ESL_SOCKET::bConnected flag is + set by ::EslTcp4ConnectComplete when the TCPv4 layer establishes + a connection or runs out of local network adapters. This routine + gets the connection status from ESL_SOCKET::ConnectStatus. - @param [in] pSocket Address of the socket structure. + @param [in] pSocket Address of an ::ESL_SOCKET structure. @retval EFI_SUCCESS The connection was successfully established. @retval EFI_NOT_READY The connection is in progress, call this routine again. @@ -529,8 +339,8 @@ EslTcpConnectComplete4 ( **/ EFI_STATUS -EslTcpConnectPoll4 ( - IN DT_SOCKET * pSocket +EslTcp4ConnectPoll ( + IN ESL_SOCKET * pSocket ) { EFI_STATUS Status; @@ -612,292 +422,165 @@ EslTcpConnectPoll4 ( /** - Connect to a remote system via the network. + Attempt to connect to a remote TCP port - The ::TcpConnectStart4= routine starts the connection processing - for a TCP4 port. + This routine starts the connection processing for a SOCK_STREAM + or SOCK_SEQPAKCET socket using the TCPv4 network layer. It + configures the local TCPv4 connection point and then attempts to + connect to a remote system. Upon completion, the + ::EslTcp4ConnectComplete routine gets called with the connection + status. - @param [in] pSocket Address of the socket structure. + This routine is called by ::EslSocketConnect to initiate the TCPv4 + network specific connect operations. The connection processing is + initiated by this routine and finished by ::EslTcp4ConnectComplete. + This pair of routines walks through the list of local TCPv4 + connection points until a connection to the remote system is + made. + + @param [in] pSocket Address of an ::ESL_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 +EslTcp4ConnectStart ( + IN ESL_SOCKET * pSocket ) { - struct sockaddr_in LocalAddress; - DT_PORT * pPort; - DT_TCP4_CONTEXT * pTcp4; - CONST struct sockaddr_in * pIp4Address; + ESL_PORT * pPort; + ESL_TCP4_CONTEXT * pTcp4; + EFI_TCP4_PROTOCOL * pTcp4Protocol; EFI_STATUS Status; DBG_ENTER ( ); - + // - // Validate the address length + // Determine if any more local adapters are available // - Status = EFI_SUCCESS; - pIp4Address = (CONST struct sockaddr_in *) pSockAddr; - if ( SockAddrLength >= ( sizeof ( *pIp4Address ) - - sizeof ( pIp4Address->sin_zero ))) { + pPort = pSocket->pPortList; + if ( NULL != pPort ) { // - // Determine if BIND was already called + // Configure the port // - 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; + pTcp4 = &pPort->Context.Tcp4; + pTcp4->ConfigData.AccessPoint.ActiveFlag = TRUE; + pTcp4->ConfigData.TimeToLive = 255; + pTcp4Protocol = pPort->pProtocol.TCPv4; + 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 { - 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. + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port configured\r\n", + pPort )); + pPort->bConfigured = TRUE; - @param [out] pAddress Network address to receive the remote system address + // + // 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 + // + DEBUG (( DEBUG_CONNECT, + "ERROR - Port 0x%08x not connected, Status: %r\r\n", + pPort, + Status )); + // + // Determine the errno value + // + switch ( Status ) { + default: + pSocket->errno = EIO; + break; - @param [in,out] pAddressLength Length of the remote network address structure + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; - @retval EFI_SUCCESS - Address available - @retval Other - Failed to get the address + case EFI_TIMEOUT: + pSocket->errno = ETIMEDOUT; + break; -**/ -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; + case EFI_NETWORK_UNREACHABLE: + pSocket->errno = ENETDOWN; + break; - DBG_ENTER ( ); + case EFI_HOST_UNREACHABLE: + pSocket->errno = EHOSTUNREACH; + break; - // - // Verify the socket layer synchronization - // - VERIFY_TPL ( TPL_SOCKETS ); + case EFI_PORT_UNREACHABLE: + case EFI_PROTOCOL_UNREACHABLE: + case EFI_CONNECTION_REFUSED: + pSocket->errno = ECONNREFUSED; + break; - // - // 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; + case EFI_CONNECTION_RESET: + pSocket->errno = ECONNRESET; + break; + } + } } } else { - pSocket->errno = ENOTCONN; - Status = EFI_NOT_STARTED; + // + // No more local adapters available + // + pSocket->errno = ENETUNREACH; + Status = EFI_NO_RESPONSE; } - + // // Return the operation status // @@ -909,27 +592,27 @@ EslTcpGetRemoteAddress4 ( /** 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. + This routine places the port into a state that enables connection + attempts. + + This routine is called by ::EslSocketListen to handle the network + specifics of the listen operation for SOCK_STREAM and SOCK_SEQPACKET + sockets. See the \ref ConnectionManagement section. - @param [in] pSocket Address of the socket structure. + @param [in] pSocket Address of an ::ESL_SOCKET structure. @retval EFI_SUCCESS - Socket successfully created @retval Other - Failed to enable the socket for listen **/ EFI_STATUS -EslTcpListen4 ( - IN DT_SOCKET * pSocket +EslTcp4Listen ( + IN ESL_SOCKET * pSocket ) { - DT_PORT * pNextPort; - DT_PORT * pPort; - DT_TCP4_CONTEXT * pTcp4; + ESL_PORT * pNextPort; + ESL_PORT * pPort; + ESL_TCP4_CONTEXT * pTcp4; EFI_TCP4_PROTOCOL * pTcp4Protocol; EFI_STATUS Status; @@ -970,7 +653,7 @@ EslTcpListen4 ( pTcp4 = &pPort->Context.Tcp4; Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_SOCKETS, - (EFI_EVENT_NOTIFY)EslTcpListenComplete4, + (EFI_EVENT_NOTIFY)EslTcp4ListenComplete, pPort, &pTcp4->ListenToken.CompletionToken.Event ); if ( EFI_ERROR ( Status )) { @@ -987,7 +670,7 @@ EslTcpListen4 ( // // Configure the port // - pTcp4Protocol = pTcp4->pProtocol; + pTcp4Protocol = pPort->pProtocol.TCPv4; Status = pTcp4Protocol->Configure ( pTcp4Protocol, &pTcp4->ConfigData ); if ( EFI_ERROR ( Status )) { @@ -1025,7 +708,7 @@ EslTcpListen4 ( DEBUG (( DEBUG_LISTEN, "0x%08x: Port configured\r\n", pPort )); - pTcp4->bConfigured = TRUE; + pPort->bConfigured = TRUE; // // Start the listen operation on the port @@ -1078,9 +761,8 @@ EslTcpListen4 ( // // Close the port upon error // - if ( EFI_ERROR ( Status )) - { - EslTcpPortCloseStart4 ( pPort, TRUE, DEBUG_LISTEN ); + if ( EFI_ERROR ( Status )) { + EslSocketPortCloseStart ( pPort, TRUE, DEBUG_LISTEN ); } // @@ -1132,30 +814,36 @@ EslTcpListen4 ( 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 + The TCPv4 layer calls this routine when a connection is made to + the socket in the listen state. See the + \ref ConnectionManagement section. + + @param [in] Event The listen completion event - @param pPort The DT_PORT structure address + @param [in] pPort Address of an ::ESL_PORT structure. **/ VOID -EslTcpListenComplete4 ( +EslTcp4ListenComplete ( IN EFI_EVENT Event, - IN DT_PORT * pPort + IN ESL_PORT * pPort ) { EFI_HANDLE ChildHandle; + struct sockaddr_in LocalAddress; EFI_TCP4_CONFIG_DATA * pConfigData; - DT_LAYER * pLayer; - DT_PORT * pNewPort; - DT_SOCKET * pNewSocket; - DT_SOCKET * pSocket; - DT_TCP4_CONTEXT * pTcp4; + ESL_LAYER * pLayer; + ESL_PORT * pNewPort; + ESL_SOCKET * pNewSocket; + ESL_SOCKET * pSocket; + ESL_TCP4_CONTEXT * pTcp4; EFI_TCP4_PROTOCOL * pTcp4Protocol; EFI_STATUS Status; EFI_HANDLE TcpPortHandle; EFI_STATUS TempStatus; DBG_ENTER ( ); + VERIFY_AT_TPL ( TPL_SOCKETS ); // // Assume success @@ -1181,26 +869,36 @@ EslTcpListenComplete4 ( // // Clone the socket parameters // + pNewSocket->pApi = pSocket->pApi; pNewSocket->Domain = pSocket->Domain; pNewSocket->Protocol = pSocket->Protocol; pNewSocket->Type = pSocket->Type; // - // Allocate a port for this connection + // Build the local address // pTcp4 = &pPort->Context.Tcp4; - Status = EslTcpPortAllocate4 ( pNewSocket, - pPort->pService, - TcpPortHandle, - &pTcp4->ConfigData.AccessPoint.StationAddress.Addr[0], - 0, - DEBUG_CONNECTION, - &pNewPort ); + LocalAddress.sin_len = (uint8_t)pNewSocket->pApi->MinimumAddressLength; + LocalAddress.sin_family = AF_INET; + LocalAddress.sin_port = 0; + LocalAddress.sin_addr.s_addr = *(UINT32 *)&pTcp4->ConfigData.AccessPoint.StationAddress.Addr[0]; + + // + // Allocate a port for this connection + // Note in this instance Configure may not be called with NULL! + // + Status = EslSocketPortAllocate ( pNewSocket, + pPort->pService, + TcpPortHandle, + (struct sockaddr *)&LocalAddress, + FALSE, + DEBUG_CONNECTION, + &pNewPort ); if ( !EFI_ERROR ( Status )) { // // Restart the listen operation on the port // - pTcp4Protocol = pTcp4->pProtocol; + pTcp4Protocol = pPort->pProtocol.TCPv4; Status = pTcp4Protocol->Accept ( pTcp4Protocol, &pTcp4->ListenToken ); @@ -1209,7 +907,6 @@ EslTcpListenComplete4 ( // TcpPortHandle = NULL; pTcp4 = &pNewPort->Context.Tcp4; - pTcp4->bConfigured = TRUE; // // Check for an accept call error @@ -1218,9 +915,10 @@ EslTcpListenComplete4 ( // // Get the port configuration // + pNewPort->bConfigured = TRUE; pConfigData = &pTcp4->ConfigData; pConfigData->ControlOption = &pTcp4->Option; - pTcp4Protocol = pTcp4->pProtocol; + pTcp4Protocol = pNewPort->pProtocol.TCPv4; Status = pTcp4Protocol->GetModeData ( pTcp4Protocol, NULL, pConfigData, @@ -1276,7 +974,7 @@ EslTcpListenComplete4 ( // // Start the receive operation // - EslTcpRxStart4 ( pNewPort ); + EslSocketRxStart ( pNewPort ); } else { DEBUG (( DEBUG_ERROR | DEBUG_CONNECTION | DEBUG_INFO, @@ -1297,7 +995,7 @@ EslTcpListenComplete4 ( // // Close the listening port // - EslTcpPortCloseStart4 ( pPort, TRUE, DEBUG_LISTEN ); + EslSocketPortCloseStart ( pPort, TRUE, DEBUG_LISTEN ); } } @@ -1343,137 +1041,228 @@ EslTcpListenComplete4 ( /** - Allocate and initialize a DT_PORT structure. + Get the local socket address. - @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 + This routine returns the IPv4 address and TCP port number associated + with the local socket. - @retval EFI_SUCCESS - Socket successfully created + This routine is called by ::EslSocketGetLocalAddress to determine the + network address for the SOCK_STREAM or SOCK_SEQPACKET socket. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pSockAddr Network address to receive the local system address + +**/ +VOID +EslTcp4LocalAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pSockAddr + ) +{ + struct sockaddr_in * pLocalAddress; + ESL_TCP4_CONTEXT * pTcp4; + + DBG_ENTER ( ); + + // + // Return the local address + // + pTcp4 = &pPort->Context.Tcp4; + pLocalAddress = (struct sockaddr_in *)pSockAddr; + pLocalAddress->sin_family = AF_INET; + pLocalAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.StationPort ); + CopyMem ( &pLocalAddress->sin_addr, + &pTcp4->ConfigData.AccessPoint.StationAddress.Addr[0], + sizeof ( pLocalAddress->sin_addr )); + + DBG_EXIT ( ); +} + + +/** + Set the local port address. + + This routine sets the local port address. + + This support routine is called by ::EslSocketPortAllocate. + + @param [in] pPort Address of an ESL_PORT 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] bBindTest TRUE = run bind testing + + @retval EFI_SUCCESS The operation was successful **/ 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 +EslTcp4LocalAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest ) { - UINTN LengthInBytes; EFI_TCP4_ACCESS_POINT * pAccessPoint; - DT_LAYER * pLayer; - DT_PORT * pPort; - DT_TCP4_CONTEXT * pTcp4; + CONST struct sockaddr_in * pIpAddress; + CONST UINT8 * pIpv4Address; EFI_STATUS Status; DBG_ENTER ( ); // - // Use for/break instead of goto - for ( ; ; ) { + // Validate the address + // + pIpAddress = (struct sockaddr_in *)pSockAddr; + if ( INADDR_BROADCAST == pIpAddress->sin_addr.s_addr ) { // - // Allocate a port structure + // The local address must not be the broadcast address // - 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 )); - + Status = EFI_INVALID_PARAMETER; + pPort->pSocket->errno = EADDRNOTAVAIL; + } + else { // - // Initialize the port + // Set the local address // - ZeroMem ( pPort, LengthInBytes ); - pPort->Signature = PORT_SIGNATURE; - pPort->pService = pService; - pPort->pSocket = pSocket; - pPort->pfnCloseStart = EslTcpPortCloseStart4; - pPort->DebugFlags = DebugFlags; + pIpv4Address = (UINT8 *)&pIpAddress->sin_addr.s_addr; + pAccessPoint = &pPort->Context.Tcp4.ConfigData.AccessPoint; + pAccessPoint->StationAddress.Addr[0] = pIpv4Address[0]; + pAccessPoint->StationAddress.Addr[1] = pIpv4Address[1]; + pAccessPoint->StationAddress.Addr[2] = pIpv4Address[2]; + pAccessPoint->StationAddress.Addr[3] = pIpv4Address[3]; // - // Allocate the receive event + // Determine if the default address is used // - 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 )); - + pAccessPoint->UseDefaultAddress = (BOOLEAN)( 0 == pIpAddress->sin_addr.s_addr ); + // - // Allocate the urgent transmit event + // Set the subnet mask // - 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; + if ( pAccessPoint->UseDefaultAddress ) { + pAccessPoint->SubnetMask.Addr[0] = 0; + pAccessPoint->SubnetMask.Addr[1] = 0; + pAccessPoint->SubnetMask.Addr[2] = 0; + pAccessPoint->SubnetMask.Addr[3] = 0; + } + else { + pAccessPoint->SubnetMask.Addr[0] = 0xff; + pAccessPoint->SubnetMask.Addr[1] = 0xff; + pAccessPoint->SubnetMask.Addr[2] = 0xff; + pAccessPoint->SubnetMask.Addr[3] = 0xff; } - DEBUG (( DEBUG_CLOSE | DEBUG_POOL, - "0x%08x: Created urgent transmit event\r\n", - pTcp4->TxOobToken.CompletionToken.Event )); // - // Allocate the normal transmit event + // Validate the IP address // - 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; + pAccessPoint->StationPort = 0; + Status = bBindTest ? EslSocketBindTest ( pPort, EADDRNOTAVAIL ) + : EFI_SUCCESS; + if ( !EFI_ERROR ( Status )) { + // + // Set the port number + // + pAccessPoint->StationPort = SwapBytes16 ( pIpAddress->sin_port ); + + // + // Display the local address + // + DEBUG (( DEBUG_BIND, + "0x%08x: Port, Local TCP4 Address: %d.%d.%d.%d:%d\r\n", + pPort, + pAccessPoint->StationAddress.Addr[0], + pAccessPoint->StationAddress.Addr[1], + pAccessPoint->StationAddress.Addr[2], + pAccessPoint->StationAddress.Addr[3], + pAccessPoint->StationPort )); } - DEBUG (( DEBUG_CLOSE | DEBUG_POOL, - "0x%08x: Created normal transmit event\r\n", - pTcp4->TxToken.CompletionToken.Event )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Free a receive packet + + This routine performs the network specific operations necessary + to free a receive packet. + + This routine is called by ::EslSocketPortCloseTxDone to free a + receive packet. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + @param [in, out] pRxBytes Address of the count of RX bytes + +**/ +VOID +EslTcp4PacketFree ( + IN ESL_PACKET * pPacket, + IN OUT size_t * pRxBytes + ) +{ + DBG_ENTER ( ); + + // + // Account for the receive bytes + // + *pRxBytes -= pPacket->Op.Tcp4Rx.RxData.DataLength; + DBG_EXIT ( ); +} + +/** + Initialize the network specific portions of an ::ESL_PORT structure. + + This routine initializes the network specific portions of an + ::ESL_PORT structure for use by the socket. + + This support routine is called by ::EslSocketPortAllocate + to connect the socket with the underlying network adapter + running the TCPv4 protocol. + + @param [in] pPort Address of an ESL_PORT structure + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslTcp4PortAllocate ( + IN ESL_PORT * pPort, + IN UINTN DebugFlags + ) +{ + EFI_TCP4_ACCESS_POINT * pAccessPoint; + ESL_SOCKET * pSocket; + ESL_TCP4_CONTEXT * pTcp4; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Use for/break instead of goto + for ( ; ; ) { // // Allocate the close event // + pSocket = pPort->pSocket; + pTcp4 = &pPort->Context.Tcp4; Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_SOCKETS, - (EFI_EVENT_NOTIFY)EslTcpPortCloseComplete4, + (EFI_EVENT_NOTIFY)EslSocketPortCloseComplete, pPort, &pTcp4->CloseToken.CompletionToken.Event); if ( EFI_ERROR ( Status )) { @@ -1492,7 +1281,7 @@ EslTcpPortAllocate4 ( // Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_SOCKETS, - (EFI_EVENT_NOTIFY)EslTcpConnectComplete4, + (EFI_EVENT_NOTIFY)EslTcp4ConnectComplete, pPort, &pTcp4->ConnectToken.CompletionToken.Event); if ( EFI_ERROR ( Status )) { @@ -1507,89 +1296,29 @@ EslTcpPortAllocate4 ( 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 + // Initialize the port // - pPort->pLinkSocket = pSocket->pPortList; - pSocket->pPortList = pPort; - DEBUG (( DebugFlags, - "0x%08x: Socket adding port: 0x%08x\r\n", - pSocket, - pPort )); + pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Tcp4Tx.TxData ); + pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Tcp4Tx.CompletionToken.Event ); + pSocket->TxTokenOffset = OFFSET_OF ( EFI_TCP4_IO_TOKEN, Packet.TxData ); // - // Add this port to the service + // Save the cancel, receive and transmit addresses + // pPort->pfnRxCancel = NULL; since the UEFI implementation returns EFI_UNSUPPORTED // - pPort->pLinkService = pService->pPortList; - pService->pPortList = pPort; + pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.TCPv4->Configure; + pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv4->Receive; + pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv4->Transmit; // - // Return the port + // Set the configuration flags // - *ppPort = pPort; + pAccessPoint = &pPort->Context.Tcp4.ConfigData.AccessPoint; + pAccessPoint->ActiveFlag = FALSE; + pTcp4->ConfigData.TimeToLive = 255; break; } - // - // Clean up after the error if necessary - // - if (( EFI_ERROR ( Status )) && ( NULL != pPort )) { - // - // Close the port - // - EslTcpPortClose4 ( pPort ); - } // // Return the operation status // @@ -1601,127 +1330,35 @@ EslTcpPortAllocate4 ( /** Close a TCP4 port. - This routine releases the resources allocated by - ::TcpPortAllocate4(). + This routine releases the network specific resources allocated by + ::EslTcp4PortAllocate. + + This routine is called by ::EslSocketPortClose. + See the \ref PortCloseStateMachine section. - @param [in] pPort Address of the port structure. + @param [in] pPort Address of an ::ESL_PORT structure. @retval EFI_SUCCESS The port is closed @retval other Port close error **/ EFI_STATUS -EslTcpPortClose4 ( - IN DT_PORT * pPort +EslTcp4PortClose ( + IN ESL_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; + ESL_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 @@ -1777,121 +1414,6 @@ EslTcpPortClose4 ( } } - // - // 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 // @@ -1901,312 +1423,79 @@ EslTcpPortClose4 ( /** - Process the port close completion + Perform the network specific close operation on the port. - @param Event The close completion event + This routine performs a cancel operations on the TCPv4 port to + shutdown the receive operations on the port. - @param pPort The DT_PORT structure address + This routine is called by the ::EslSocketPortCloseTxDone + routine after the port completes all of the transmission. + + @param [in] pPort Address of an ::ESL_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. **/ -VOID -EslTcpPortCloseComplete4 ( - IN EFI_EVENT Event, - IN DT_PORT * pPort +EFI_STATUS +EslTcp4PortCloseOp ( + IN ESL_PORT * pPort ) { + ESL_TCP4_CONTEXT * pTcp4; + EFI_TCP4_PROTOCOL * pTcp4Protocol; EFI_STATUS Status; DBG_ENTER ( ); // - // Update the port state + // Close the configured port // - pPort->State = PORT_STATE_CLOSE_DONE; + Status = EFI_SUCCESS; + pTcp4 = &pPort->Context.Tcp4; + pTcp4Protocol = pPort->pProtocol.TCPv4; + pTcp4->CloseToken.AbortOnClose = pPort->bCloseNow; + 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 )); + } + else { + DEBUG (( DEBUG_ERROR | pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "ERROR - Close failed on port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } // - // Release the resources once the receive operation completes + // Return the operation status // - Status = EslTcpPortCloseRxDone4 ( pPort ); DBG_EXIT_STATUS ( Status ); + return Status; } /** - Start the close operation on a TCP4 port, state 1. + Receive data from a network connection. - 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 + This routine attempts to return buffered data to the caller. The + data is removed from the urgent queue if the message flag MSG_OOB + is specified, otherwise data is removed from the normal queue. + See the \ref ReceiveEngine section. - @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 )); + This routine is called by ::EslSocketReceive to handle the network + specific receive operation to support SOCK_STREAM and SOCK_SEQPACKET + sockets. - // - // 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 + @param [in] pPort Address of an ::ESL_PORT structure. - 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] pPacket Address of an ::ESL_PACKET structure. - @param [in] Flags Message control flags + @param [in] pbConsumePacket Address of a BOOLEAN indicating if the packet is to be consumed @param [in] BufferLength Length of the the buffer @@ -2216,674 +1505,313 @@ EslTcpPortCloseTxDone4 ( @param [out] pAddress Network address to receive the remote system address - @param [in,out] pAddressLength Length of the remote network address structure + @param [out] pSkipBytes Address to receive the number of bytes skipped - @retval EFI_SUCCESS - Socket data successfully received + @return Returns the address of the next free byte in the buffer. **/ -EFI_STATUS -EslTcpReceive4 ( - IN DT_SOCKET * pSocket, - IN INT32 Flags, +UINT8 * +EslTcp4Receive ( + IN ESL_PORT * pPort, + IN ESL_PACKET * pPacket, + IN BOOLEAN * pbConsumePacket, IN size_t BufferLength, IN UINT8 * pBuffer, OUT size_t * pDataLength, OUT struct sockaddr * pAddress, - IN OUT socklen_t * pAddressLength + OUT size_t * pSkipBytes ) { - 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; + size_t DataLength; 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; - pSocket->RxError = EFI_SUCCESS; - switch ( Status ) { - default: - pSocket->errno = EIO; - break; - - case EFI_CONNECTION_FIN: - // - // Continue to return zero bytes received when the - // peer has successfully closed the connection - // - pSocket->RxError = EFI_CONNECTION_FIN; - *pDataLength = 0; - pSocket->errno = 0; - Status = EFI_SUCCESS; - 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; - } - } - 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; + ESL_TCP4_CONTEXT * pTcp4; DBG_ENTER ( ); // - // Assume failure - // - Status = EFI_NOT_FOUND; - - // - // Locate the port + // Return the remote system address if requested // - pPort = pSocket->pPortList; - if ( NULL != pPort ) { + if ( NULL != pAddress ) { // - // Determine if a receive is pending + // Build the remote address // 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; - } - } + DEBUG (( DEBUG_RX, + "Getting packet remote address: %d.%d.%d.%d:%d\r\n", + 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 )); + pRemoteAddress = (struct sockaddr_in *)pAddress; + CopyMem ( &pRemoteAddress->sin_addr, + &pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin_addr )); + pRemoteAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.RemotePort ); } // - // Return the operation status + // Determine the amount of received data // - 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; + DataLength = pPacket->ValidBytes; + if ( BufferLength < DataLength ) { + DataLength = BufferLength; + } // - // 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; - } + // 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, + DataLength )); + CopyMem ( pBuffer, pPacket->pBuffer, DataLength ); + // + // Determine if the data is being read + // + if ( *pbConsumePacket ) { // - // Log the received data + // Account for the bytes consumed // - DEBUG (( DEBUG_RX | DEBUG_INFO, - "0x%08x: Packet queued on port 0x%08x with 0x%08x bytes of %s data\r\n", - pPacket, + pPacket->pBuffer += DataLength; + pPacket->ValidBytes -= DataLength; + DEBUG (( DEBUG_RX, + "0x%08x: Port account for 0x%08x bytes\r\n", pPort, - LengthInBytes, - bUrgent ? L"urgent" : L"normal" )); + DataLength )); // - // Attempt to restart this receive operation + // Determine if the entire packet was consumed // - if ( pSocket->MaxRxBuf > pSocket->RxBytes ) { - EslTcpRxStart4 ( pPort ); + if (( 0 == pPacket->ValidBytes ) + || ( SOCK_STREAM != pPort->pSocket->Type )) { + // + // All done with this packet + // Account for any discarded data + // + *pSkipBytes = pPacket->ValidBytes; } - else { - DEBUG (( DEBUG_RX, - "0x%08x: Port RX suspended, 0x%08x bytes queued\r\n", - pPort, - pSocket->RxBytes )); + else + { + // + // More data to consume later + // + *pbConsumePacket = FALSE; } } - 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; - } + // + // Return the data length and the buffer address + // + *pDataLength = DataLength; + DBG_EXIT_HEX ( pBuffer ); + return pBuffer; +} - // - // Update the port state - // - if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { - EslTcpPortCloseRxDone4 ( pPort ); - } - else { - if ( EFI_ERROR ( Status )) { - pPort->State = PORT_STATE_RX_ERROR; - } - } - } + +/** + Get the remote socket address. + + This routine returns the address of the remote connection point + associated with the SOCK_STREAM or SOCK_SEQPACKET socket. + + This routine is called by ::EslSocketGetPeerAddress to detemine + the TCPv4 address and por number associated with the network adapter. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pAddress Network address to receive the remote system address + +**/ +VOID +EslTcp4RemoteAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress + ) +{ + struct sockaddr_in * pRemoteAddress; + ESL_TCP4_CONTEXT * pTcp4; + + DBG_ENTER ( ); + + // + // Return the remote address + // + pTcp4 = &pPort->Context.Tcp4; + pRemoteAddress = (struct sockaddr_in *)pAddress; + pRemoteAddress->sin_family = AF_INET; + pRemoteAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.RemotePort ); + CopyMem ( &pRemoteAddress->sin_addr, + &pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin_addr )); DBG_EXIT ( ); } /** - Start a receive operation + Set the remote address + + This routine sets the remote address in the port. + + This routine is called by ::EslSocketConnect to specify the + remote network address. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. - @param [in] pPort Address of the DT_PORT structure. + @retval EFI_SUCCESS The operation was successful **/ -VOID -EslTcpRxStart4 ( - IN DT_PORT * pPort +EFI_STATUS +EslTcp4RemoteAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength ) { - size_t LengthInBytes; - DT_PACKET * pPacket; - DT_SOCKET * pSocket; - DT_TCP4_CONTEXT * pTcp4; - EFI_TCP4_PROTOCOL * pTcp4Protocol; + CONST struct sockaddr_in * pRemoteAddress; + ESL_TCP4_CONTEXT * pTcp4; EFI_STATUS Status; DBG_ENTER ( ); // - // Determine if a receive is already pending + // Set the remote address // - 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; - } - } - } - } + pRemoteAddress = (struct sockaddr_in *)pSockAddr; + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0] = (UINT8)( pRemoteAddress->sin_addr.s_addr ); + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 ); + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 ); + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 ); + pTcp4->ConfigData.AccessPoint.RemotePort = SwapBytes16 ( pRemoteAddress->sin_port ); + Status = EFI_SUCCESS; + if ( INADDR_BROADCAST == pRemoteAddress->sin_addr.s_addr ) { + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid remote address\r\n" )); + Status = EFI_INVALID_PARAMETER; + pPort->pSocket->errno = EAFNOSUPPORT; } - DBG_EXIT ( ); + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; } /** - Shutdown the TCP4 service. + Process the receive completion + + This routine queues the data in FIFO order in either the urgent + or normal data queues depending upon the type of data received. + See the \ref ReceiveEngine section. + + This routine is called by the TCPv4 driver when some data is + received. - This routine undoes the work performed by ::TcpInitialize4. + Buffer the data that was just received. + + @param [in] Event The receive completion event - @param [in] pService DT_SERVICE structure address + @param [in] pIo Address of an ::ESL_IO_MGMT structure **/ VOID -EFIAPI -EslTcpShutdown4 ( - IN DT_SERVICE * pService +EslTcp4RxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo ) { - DT_LAYER * pLayer; - DT_PORT * pPort; - DT_SERVICE * pPreviousService; + BOOLEAN bUrgent; + size_t LengthInBytes; + ESL_PACKET * pPacket; + EFI_STATUS Status; DBG_ENTER ( ); // - // Verify the socket layer synchronization + // Get the operation status. // - VERIFY_TPL ( TPL_SOCKETS ); + Status = pIo->Token.Tcp4Rx.CompletionToken.Status; // - // Walk the list of ports + // +--------------------+ +---------------------------+ + // | ESL_IO_MGMT | | ESL_PACKET | + // | | | | + // | +---------------+ +-----------------------+ | + // | | Token | | EFI_TCP4_RECEIVE_DATA | | + // | | RxData --> | | | + // | | | +-----------------------+---+ + // | | Event | | Data Buffer | + // +----+---------------+ | | + // | | + // +---------------------------+ // - do { - pPort = pService->pPortList; - if ( NULL != pPort ) { - // - // Remove the port from the port list - // - pService->pPortList = pPort->pLinkService; + // + // Duplicate the buffer address and length for use by the + // buffer handling code in EslTcp4Receive. These fields are + // used when a partial read is done of the data from the + // packet. + // + pPacket = pIo->pPacket; + pPacket->pBuffer = pPacket->Op.Tcp4Rx.RxData.FragmentTable[0].FragmentBuffer; + LengthInBytes = pPacket->Op.Tcp4Rx.RxData.DataLength; + pPacket->ValidBytes = LengthInBytes; - // - // Close the port - // TODO: Fix this - // -// pPort->pfnClosePort ( pPort, DEBUG_LISTEN | DEBUG_CONNECTION ); - } - } while ( NULL != pPort ); + // + // Get the data type so that it may be linked to the + // correct receive buffer list on the ESL_SOCKET structure + // + bUrgent = pPacket->Op.Tcp4Rx.RxData.UrgentFlag; // - // Remove the service from the service list + // Complete this request // - 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; - } - } - } + EslSocketRxComplete ( pIo, Status, LengthInBytes, bUrgent ); + DBG_EXIT ( ); +} + + +/** + Start a receive operation + + This routine posts a receive buffer to the TCPv4 driver. + See the \ref ReceiveEngine section. + + This support routine is called by EslSocketRxStart. + + @param [in] pPort Address of an ::ESL_PORT structure. + @param [in] pIo Address of an ::ESL_IO_MGMT structure. + + **/ +VOID +EslTcp4RxStart ( + IN ESL_PORT * pPort, + IN ESL_IO_MGMT * pIo + ) +{ + ESL_PACKET * pPacket; + + DBG_ENTER ( ); + + // + // Initialize the buffer for receive + // + pPacket = pIo->pPacket; + pIo->Token.Tcp4Rx.Packet.RxData = &pPacket->Op.Tcp4Rx.RxData; + pPacket->Op.Tcp4Rx.RxData.DataLength = sizeof ( pPacket->Op.Tcp4Rx.Buffer ); + pPacket->Op.Tcp4Rx.RxData.FragmentCount = 1; + pPacket->Op.Tcp4Rx.RxData.FragmentTable[0].FragmentLength = pPacket->Op.Tcp4Rx.RxData.DataLength; + pPacket->Op.Tcp4Rx.RxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Tcp4Rx.Buffer[0]; DBG_EXIT ( ); } @@ -2892,16 +1820,21 @@ EslTcpShutdown4 ( /** Determine if the socket is configured. + This routine uses the flag ESL_SOCKET::bConfigured to determine + if the network layer's configuration routine has been called. + + This routine is called by EslSocketIsConfigured to verify + that the socket has been configured. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. - @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 + EslTcp4SocketIsConfigured ( + IN ESL_SOCKET * pSocket ) { EFI_STATUS Status; @@ -2924,22 +1857,22 @@ EslTcpShutdown4 ( /** 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. + This routine buffers data for the transmit engine in one of 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. - 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. + This routine is called by ::EslSocketTransmit to buffer + data for transmission. When the \ref TransmitEngine has resources, + this routine will start the transmission of the next buffer on + 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] pSocket Address of an ::ESL_SOCKET structure @param [in] Flags Message control flags @@ -2949,27 +1882,34 @@ EslTcpShutdown4 ( @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 -EslTcpTxBuffer4 ( - IN DT_SOCKET * pSocket, +EslTcp4TxBuffer ( + IN ESL_SOCKET * pSocket, IN int Flags, IN size_t BufferLength, IN CONST UINT8 * pBuffer, - OUT size_t * pDataLength + OUT size_t * pDataLength, + IN const struct sockaddr * pAddress, + IN socklen_t AddressLength ) { 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; + BOOLEAN bUrgentQueue; + ESL_PACKET * pPacket; + ESL_IO_MGMT ** ppActive; + ESL_IO_MGMT ** ppFree; + ESL_PORT * pPort; + ESL_PACKET ** ppQueueHead; + ESL_PACKET ** ppQueueTail; + ESL_PACKET * pPreviousPacket; + ESL_TCP4_CONTEXT * pTcp4; size_t * pTxBytes; EFI_TCP4_TRANSMIT_DATA * pTxData; EFI_STATUS Status; @@ -2982,7 +1922,7 @@ EslTcpTxBuffer4 ( // Status = EFI_UNSUPPORTED; pSocket->errno = ENOTCONN; - * pDataLength = 0; + *pDataLength = 0; // // Verify that the socket is connected @@ -2998,18 +1938,21 @@ EslTcpTxBuffer4 ( // pTcp4 = &pPort->Context.Tcp4; bUrgent = (BOOLEAN)( 0 != ( Flags & MSG_OOB )); - if ( bUrgent ) { + bUrgentQueue = bUrgent + && ( !pSocket->bOobInLine ) + && pSocket->pApi->bOobSupported; + if ( bUrgentQueue ) { ppQueueHead = &pSocket->pTxOobPacketListHead; ppQueueTail = &pSocket->pTxOobPacketListTail; - ppPacket = &pTcp4->pTxOobPacket; - pToken = &pTcp4->TxOobToken; + ppActive = &pPort->pTxOobActive; + ppFree = &pPort->pTxOobFree; pTxBytes = &pSocket->TxOobBytes; } else { ppQueueHead = &pSocket->pTxPacketListHead; ppQueueTail = &pSocket->pTxPacketListTail; - ppPacket = &pTcp4->pTxPacket; - pToken = &pTcp4->TxToken; + ppActive = &pPort->pTxActive; + ppFree = &pPort->pTxFree; pTxBytes = &pSocket->TxBytes; } @@ -3018,6 +1961,15 @@ EslTcpTxBuffer4 ( // transmit operation // if ( pSocket->MaxTxBuf > *pTxBytes ) { + if ( pPort->bTxFlowControl ) { + DEBUG (( DEBUG_TX, + "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\r\n0x%08x: pPort, TX flow control released, Max bytes: %d > %d bufferred bytes\r\n", + pPort, + pSocket->MaxTxBuf, + *pTxBytes )); + pPort->bTxFlowControl = FALSE; + } + // // Attempt to allocate the packet // @@ -3025,13 +1977,14 @@ EslTcpTxBuffer4 ( sizeof ( pPacket->Op.Tcp4Tx ) - sizeof ( pPacket->Op.Tcp4Tx.Buffer ) + BufferLength, + 0, DEBUG_TX ); if ( !EFI_ERROR ( Status )) { // // Initialize the transmit operation // pTxData = &pPacket->Op.Tcp4Tx.TxData; - pTxData->Push = TRUE; + pTxData->Push = TRUE || bUrgent; pTxData->Urgent = bUrgent; pTxData->DataLength = (UINT32) BufferLength; pTxData->FragmentCount = 1; @@ -3078,7 +2031,7 @@ EslTcpTxBuffer4 ( DEBUG (( DEBUG_TX, "0x%08x: Packet on %s transmit list\r\n", pPacket, - bUrgent ? L"urgent" : L"normal" )); + bUrgentQueue ? L"urgent" : L"normal" )); // // Account for the buffered data @@ -3089,12 +2042,12 @@ EslTcpTxBuffer4 ( // // Start the transmit engine if it is idle // - if ( NULL == *ppPacket ) { - EslTcpTxStart4 ( pSocket->pPortList, - pToken, - ppQueueHead, - ppQueueTail, - ppPacket ); + if ( NULL != *ppFree ) { + EslSocketTxStart ( pPort, + ppQueueHead, + ppQueueTail, + ppActive, + ppFree ); } } else { @@ -3124,6 +2077,14 @@ EslTcpTxBuffer4 ( } } else { + if ( !pPort->bTxFlowControl ) { + DEBUG (( DEBUG_TX, + "0x%08x: pPort, TX flow control applied, Max bytes %d <= %d bufferred bytes\r\nTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\r\n", + pPort, + pSocket->MaxTxBuf, + *pTxBytes )); + pPort->bTxFlowControl = TRUE; + } // // Not enough buffer space available // @@ -3144,104 +2105,56 @@ EslTcpTxBuffer4 ( /** Process the normal data transmit completion - @param Event The normal transmit completion event + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for normal data. + + This routine is called by the TCPv4 network layer when a + normal data transmit request completes. + + @param [in] Event The normal transmit completion event - @param pPort The DT_PORT structure address + @param [in] pIo The ESL_IO_MGMT structure address **/ VOID -EslTcpTxComplete4 ( +EslTcp4TxComplete ( IN EFI_EVENT Event, - IN DT_PORT * pPort + IN ESL_IO_MGMT * pIo ) { UINT32 LengthInBytes; - DT_PACKET * pCurrentPacket; - DT_PACKET * pNextPacket; - DT_PACKET * pPacket; - DT_SOCKET * pSocket; - DT_TCP4_CONTEXT * pTcp4; + ESL_PACKET * pPacket; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); - + // // Locate the active transmit packet // + pPacket = pIo->pPacket; + pPort = pIo->pPort; pSocket = pPort->pSocket; - pTcp4 = &pPort->Context.Tcp4; - pPacket = pTcp4->pTxPacket; - + // - // Mark this packet as complete + // Get the transmit length and status // - 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 ); + Status = pIo->Token.Tcp4Tx.CompletionToken.Status; // - // Finish the close operation if necessary + // Complete the transmit operation // - if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { - // - // Indicate that the transmit is complete - // - EslTcpPortCloseTxDone4 ( pPort ); - } + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "Normal ", + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); DBG_EXIT ( ); } @@ -3249,23 +2162,27 @@ EslTcpTxComplete4 ( /** Process the urgent data transmit completion - @param Event The urgent transmit completion event + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for urgent data. - @param pPort The DT_PORT structure address + This routine is called by the TCPv4 network layer when a + urgent data transmit request completes. + + @param [in] Event The urgent transmit completion event + + @param [in] pIo The ESL_IO_MGMT structure address **/ VOID -EslTcpTxOobComplete4 ( +EslTcp4TxOobComplete ( IN EFI_EVENT Event, - IN DT_PORT * pPort + IN ESL_IO_MGMT * pIo ) { UINT32 LengthInBytes; - DT_PACKET * pCurrentPacket; - DT_PACKET * pNextPacket; - DT_PACKET * pPacket; - DT_SOCKET * pSocket; - DT_TCP4_CONTEXT * pTcp4; + ESL_PACKET * pPacket; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); @@ -3273,150 +2190,70 @@ EslTcpTxOobComplete4 ( // // Locate the active transmit packet // + pPacket = pIo->pPacket; + pPort = pIo->pPort; pSocket = pPort->pSocket; - pTcp4 = &pPort->Context.Tcp4; - pPacket = pTcp4->pTxOobPacket; // - // Mark this packet as complete + // Get the transmit length and status // - pTcp4->pTxOobPacket = NULL; LengthInBytes = pPacket->Op.Tcp4Tx.TxData.DataLength; pSocket->TxOobBytes -= LengthInBytes; + Status = pIo->Token.Tcp4Tx.CompletionToken.Status; // - // 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 + // Complete the transmit operation // - if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { - // - // Indicate that the transmit is complete - // - EslTcpPortCloseTxDone4 ( pPort ); - } + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "Urgent ", + &pSocket->pTxOobPacketListHead, + &pSocket->pTxOobPacketListTail, + &pPort->pTxOobActive, + &pPort->pTxOobFree ); 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 ( ); -} + Interface between the socket layer and the network specific + code that supports SOCK_STREAM and SOCK_SEQPACKET sockets + over TCPv4. +**/ +CONST ESL_PROTOCOL_API cEslTcp4Api = { + "TCPv4", + IPPROTO_TCP, + OFFSET_OF ( ESL_PORT, Context.Tcp4.ConfigData ), + OFFSET_OF ( ESL_LAYER, pTcp4List ), + OFFSET_OF ( struct sockaddr_in, sin_zero ), + sizeof ( struct sockaddr_in ), + AF_INET, + sizeof (((ESL_PACKET *)0 )->Op.Tcp4Rx ), + OFFSET_OF ( ESL_PACKET, Op.Tcp4Rx.Buffer ) - OFFSET_OF ( ESL_PACKET, Op ), + OFFSET_OF ( ESL_IO_MGMT, Token.Tcp4Rx.Packet.RxData ), + TRUE, + EADDRINUSE, + EslTcp4Accept, + EslTcp4ConnectPoll, + EslTcp4ConnectStart, + EslTcp4SocketIsConfigured, + EslTcp4LocalAddressGet, + EslTcp4LocalAddressSet, + EslTcp4Listen, + NULL, // OptionGet + NULL, // OptionSet + EslTcp4PacketFree, + EslTcp4PortAllocate, + EslTcp4PortClose, + EslTcp4PortCloseOp, + FALSE, + EslTcp4Receive, + EslTcp4RemoteAddressGet, + EslTcp4RemoteAddressSet, + EslTcp4RxComplete, + EslTcp4RxStart, + EslTcp4TxBuffer, + EslTcp4TxComplete, + EslTcp4TxOobComplete +}; diff --git a/StdLib/EfiSocketLib/Udp4.c b/StdLib/EfiSocketLib/Udp4.c index 95fc66517a..e1500d3f9f 100644 --- a/StdLib/EfiSocketLib/Udp4.c +++ b/StdLib/EfiSocketLib/Udp4.c @@ -16,404 +16,152 @@ /** - 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; -} - + Get the local socket address -/** - Initialize the UDP4 service. + This routine returns the IPv4 address and UDP port number associated + with the local socket. - This routine initializes the UDP4 service after its service binding - protocol was located on a controller. + This routine is called by ::EslSocketGetLocalAddress to determine the + network address for the SOCK_DGRAM socket. - @param [in] pService DT_SERVICE structure address + @param [in] pPort Address of an ::ESL_PORT structure. - @retval EFI_SUCCESS The service was properly initialized - @retval other A failure occurred during the service initialization + @param [out] pSockAddr Network address to receive the local system address **/ -EFI_STATUS -EFIAPI -EslUdpInitialize4 ( - IN DT_SERVICE * pService +VOID +EslUdp4LocalAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pSockAddr ) { - DT_LAYER * pLayer; - EFI_STATUS Status; + struct sockaddr_in * pLocalAddress; + ESL_UDP4_CONTEXT * pUdp4; 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 + // Return the local address // - Status = EFI_SUCCESS; + pUdp4 = &pPort->Context.Udp4; + pLocalAddress = (struct sockaddr_in *)pSockAddr; + pLocalAddress->sin_family = AF_INET; + pLocalAddress->sin_port = SwapBytes16 ( pUdp4->ConfigData.StationPort ); + CopyMem ( &pLocalAddress->sin_addr, + &pUdp4->ConfigData.StationAddress.Addr[0], + sizeof ( pLocalAddress->sin_addr )); - // - // Return the initialization status - // - DBG_EXIT_STATUS ( Status ); - return Status; + DBG_EXIT ( ); } /** - Allocate and initialize a DT_PORT structure. + Set the local port address. - @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 + This routine sets the local port address. - @retval EFI_SUCCESS - Socket successfully created + This support routine is called by ::EslSocketPortAllocate. + + @param [in] pPort Address of an ESL_PORT 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] bBindTest TRUE = run bind testing + + @retval EFI_SUCCESS The operation was successful **/ 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 +EslUdp4LocalAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest ) { - UINTN LengthInBytes; EFI_UDP4_CONFIG_DATA * pConfig; - DT_LAYER * pLayer; - DT_PORT * pPort; - DT_UDP4_CONTEXT * pUdp4; + CONST struct sockaddr_in * pIpAddress; + CONST UINT8 * pIpv4Address; 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; - + // Validate the address + // + pIpAddress = (struct sockaddr_in *)pSockAddr; + if ( INADDR_BROADCAST == pIpAddress->sin_addr.s_addr ) { // - // Allocate the receive event + // The local address must not be the broadcast address // - 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 )); - + Status = EFI_INVALID_PARAMETER; + pPort->pSocket->errno = EADDRNOTAVAIL; + } + else { // - // Allocate the transmit event + // Set the local address // - 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 )); + pIpAddress = (struct sockaddr_in *)pSockAddr; + pIpv4Address = (UINT8 *)&pIpAddress->sin_addr.s_addr; + pConfig = &pPort->Context.Udp4.ConfigData; + pConfig->StationAddress.Addr[0] = pIpv4Address[0]; + pConfig->StationAddress.Addr[1] = pIpv4Address[1]; + pConfig->StationAddress.Addr[2] = pIpv4Address[2]; + pConfig->StationAddress.Addr[3] = pIpv4Address[3]; // - // Open the port protocol + // Determine if the default address is used // - 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 )); - + pConfig->UseDefaultAddress = (BOOLEAN)( 0 == pIpAddress->sin_addr.s_addr ); + // - // Set the port address + // Set the subnet mask // - 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; + if ( pConfig->UseDefaultAddress ) { + pConfig->SubnetMask.Addr[0] = 0; + pConfig->SubnetMask.Addr[1] = 0; + pConfig->SubnetMask.Addr[2] = 0; + pConfig->SubnetMask.Addr[3] = 0; } 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 + // Validate the IP address // - 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; + pConfig->StationPort = 0; + Status = bBindTest ? EslSocketBindTest ( pPort, EADDRNOTAVAIL ) + : EFI_SUCCESS; + if ( !EFI_ERROR ( Status )) { + // + // Set the port number + // + pConfig->StationPort = SwapBytes16 ( pIpAddress->sin_port ); - // - // Return the port - // - *ppPort = pPort; - break; + // + // Display the local address + // + DEBUG (( DEBUG_BIND, + "0x%08x: Port, Local UDP4 Address: %d.%d.%d.%d:%d\r\n", + pPort, + pConfig->StationAddress.Addr[0], + pConfig->StationAddress.Addr[1], + pConfig->StationAddress.Addr[2], + pConfig->StationAddress.Addr[3], + pConfig->StationPort )); + } } - // - // Clean up after the error if necessary - // - if (( EFI_ERROR ( Status )) && ( NULL != pPort )) { - // - // Close the port - // - EslUdpPortClose4 ( pPort ); - } // // Return the operation status // @@ -423,292 +171,102 @@ EslUdpPortAllocate4 ( /** - Close a UDP4 port. + Free a receive packet - This routine releases the resources allocated by - ::UdpPortAllocate4(). - - @param [in] pPort Address of the port structure. + This routine performs the network specific operations necessary + to free a receive packet. - @retval EFI_SUCCESS The port is closed - @retval other Port close error + This routine is called by ::EslSocketPortCloseTxDone to free a + receive packet. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + @param [in, out] pRxBytes Address of the count of RX bytes **/ -EFI_STATUS -EslUdpPortClose4 ( - IN DT_PORT * pPort +VOID +EslUdp4PacketFree ( + IN ESL_PACKET * pPacket, + IN OUT size_t * pRxBytes ) { - 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 ); - } - } + EFI_UDP4_RECEIVE_DATA * pRxData; - // - // 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 ); - } - } + DBG_ENTER ( ); // - // Release the port structure + // Account for the receive bytes // - 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 ); - } + pRxData = pPacket->Op.Udp4Rx.pRxData; + *pRxBytes -= pRxData->DataLength; // - // Mark the socket as closed if necessary + // Disconnect the buffer from the packet // - if ( NULL == pSocket->pPortList ) { - pSocket->State = SOCKET_STATE_CLOSED; - DEBUG (( DEBUG_CLOSE | DEBUG_INFO, - "0x%08x: Socket State: SOCKET_STATE_CLOSED\r\n", - pSocket )); - } + pPacket->Op.Udp4Rx.pRxData = NULL; // - // Return the operation status + // Return the buffer to the UDP4 driver // - DBG_EXIT_STATUS ( Status ); - return Status; + gBS->SignalEvent ( pRxData->RecycleSignal ); + DBG_EXIT ( ); } /** - Start the close operation on a UDP4 port, state 1. + Initialize the network specific portions of an ::ESL_PORT structure. - 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 + This routine initializes the network specific portions of an + ::ESL_PORT structure for use by the socket. + + This support routine is called by ::EslSocketPortAllocate + to connect the socket with the underlying network adapter + running the UDPv4 protocol. + + @param [in] pPort Address of an ESL_PORT structure @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. + @retval EFI_SUCCESS - Socket successfully created -**/ + **/ EFI_STATUS -EslUdpPortCloseStart4 ( - IN DT_PORT * pPort, - IN BOOLEAN bCloseNow, +EslUdp4PortAllocate ( + IN ESL_PORT * pPort, IN UINTN DebugFlags ) { - DT_SOCKET * pSocket; + EFI_UDP4_CONFIG_DATA * pConfig; + ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); // - // Verify the socket layer synchronization + // Initialize the port // - VERIFY_TPL ( TPL_SOCKETS ); + pSocket = pPort->pSocket; + pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Udp4Tx.TxData ); + pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Udp4Tx.Event ); + pSocket->TxTokenOffset = OFFSET_OF ( EFI_UDP4_COMPLETION_TOKEN, Packet.TxData ); // - // Mark the port as closing + // Save the cancel, receive and transmit addresses // - 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; + pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.UDPv4->Configure; + pPort->pfnRxCancel = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Cancel; + pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Receive; + pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Transmit; - // - // Determine if transmits are complete - // - Status = EslUdpPortCloseTxDone4 ( pPort ); - } + // + // Set the configuration flags + // + pConfig = &pPort->Context.Udp4.ConfigData; + pConfig->TimeToLive = 255; + pConfig->AcceptAnyPort = FALSE; + pConfig->AcceptBroadcast = FALSE; + pConfig->AcceptPromiscuous = FALSE; + pConfig->AllowDuplicatePort = TRUE; + pConfig->DoNotFragment = TRUE; + Status = EFI_SUCCESS; // // Return the operation status @@ -719,1184 +277,300 @@ EslUdpPortCloseStart4 ( /** - Port close state 3 - - Continue the close operation after the receive is complete. - - @param [in] pPort Address of the port structure. + Receive data from a network connection. - @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. + This routine attempts to return buffered data to the caller. The + data is removed from the urgent queue if the message flag MSG_OOB + is specified, otherwise data is removed from the normal queue. + See the \ref ReceiveEngine section. -**/ -EFI_STATUS -EslUdpPortCloseRxDone4 ( - IN DT_PORT * pPort - ) -{ - PORT_STATE PortState; - DT_SOCKET * pSocket; - DT_UDP4_CONTEXT * pUdp4; - EFI_STATUS Status; + This routine is called by ::EslSocketReceive to handle the network + specific receive operation to support SOCK_DGRAM sockets. - DBG_ENTER ( ); + @param [in] pPort Address of an ::ESL_PORT structure. - // - // 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. + @param [in] pPacket Address of an ::ESL_PACKET 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 -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; - } + @param [in] pbConsumePacket Address of a BOOLEAN indicating if the packet is to be consumed - // - // 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; - } + @param [out] pSkipBytes Address to receive the number of bytes skipped - // - // 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 + @return Returns the address of the next free byte in the buffer. **/ -EFI_STATUS -EslUdpRxCancel4 ( - IN DT_SOCKET * pSocket +UINT8 * +EslUdp4Receive ( + IN ESL_PORT * pPort, + IN ESL_PACKET * pPacket, + IN BOOLEAN * pbConsumePacket, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength, + OUT struct sockaddr * pAddress, + OUT size_t * pSkipBytes ) { - DT_PACKET * pPacket; - DT_PORT * pPort; - DT_UDP4_CONTEXT * pUdp4; - EFI_UDP4_PROTOCOL * pUdp4Protocol; - EFI_STATUS Status; + size_t DataBytes; + struct sockaddr_in * pRemoteAddress; + EFI_UDP4_RECEIVE_DATA * pRxData; DBG_ENTER ( ); + pRxData = pPacket->Op.Udp4Rx.pRxData; // - // Assume failure + // Return the remote system address if requested + // + if ( NULL != pAddress ) { + // + // Build the remote address + // + DEBUG (( DEBUG_RX, + "Getting packet remote 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 )); + pRemoteAddress = (struct sockaddr_in *)pAddress; + CopyMem ( &pRemoteAddress->sin_addr, + &pRxData->UdpSession.SourceAddress.Addr[0], + sizeof ( pRemoteAddress->sin_addr )); + pRemoteAddress->sin_port = SwapBytes16 ( pRxData->UdpSession.SourcePort ); + } + // - Status = EFI_NOT_FOUND; + // Copy the received data + // + pBuffer = EslSocketCopyFragmentedBuffer ( pRxData->FragmentCount, + (EFI_IP4_FRAGMENT_DATA *)&pRxData->FragmentTable[0], + BufferLength, + pBuffer, + &DataBytes ); // - // Locate the port + // Determine if the data is being read // - pPort = pSocket->pPortList; - if ( NULL != pPort ) { + if ( *pbConsumePacket ) { // - // Determine if a receive is pending + // Display for the bytes consumed // - 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; - } - } + DEBUG (( DEBUG_RX, + "0x%08x: Port account for 0x%08x bytes\r\n", + pPort, + DataBytes )); + + // + // Account for any discarded data + // + *pSkipBytes = pRxData->DataLength - DataBytes; } // - // Return the operation status + // Return the data length and the buffer address // - DBG_EXIT_STATUS ( Status ); - return Status; + *pDataLength = DataBytes; + DBG_EXIT_HEX ( pBuffer ); + return pBuffer; } /** - Process the receive completion + Get the remote socket address - 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. + This routine returns the address of the remote connection point + associated with the SOCK_DGRAM socket. - @param Event The receive completion event + This routine is called by ::EslSocketGetPeerAddress to detemine + the UDPv4 address and port number associated with the network adapter. - @param pPort The DT_PORT structure address + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pAddress Network address to receive the remote system address **/ VOID -EslUdpRxComplete4 ( - IN EFI_EVENT Event, - IN DT_PORT * pPort +EslUdp4RemoteAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress ) { - size_t LengthInBytes; - DT_PACKET * pPacket; - DT_PACKET * pPrevious; - EFI_UDP4_RECEIVE_DATA * pRxData; - DT_SOCKET * pSocket; - DT_UDP4_CONTEXT * pUdp4; - EFI_STATUS Status; - + struct sockaddr_in * pRemoteAddress; + ESL_UDP4_CONTEXT * pUdp4; + DBG_ENTER ( ); - + // - // Mark this receive complete + // Return the remote address // 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; + pRemoteAddress = (struct sockaddr_in *)pAddress; + pRemoteAddress->sin_family = AF_INET; + pRemoteAddress->sin_port = SwapBytes16 ( pUdp4->ConfigData.RemotePort ); + CopyMem ( &pRemoteAddress->sin_addr, + &pUdp4->ConfigData.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin_addr )); - // - // Queue this packet - // - pPrevious = pSocket->pRxPacketListTail; - if ( NULL == pPrevious ) { - pSocket->pRxPacketListHead = pPacket; - } - else { - pPrevious->pNext = pPacket; - } - pSocket->pRxPacketListTail = pPacket; + DBG_EXIT ( ); +} - // - // 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 )); +/** + Set the remote address - // - // 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 ); + This routine sets the remote address in the port. - // - // 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 ( ); -} + This routine is called by ::EslSocketConnect to specify the + remote network address. + @param [in] pPort Address of an ::ESL_PORT structure. -/** - Start a receive operation + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. - @param [in] pPort Address of the DT_PORT structure. + @retval EFI_SUCCESS The operation was successful **/ -VOID -EslUdpRxStart4 ( - IN DT_PORT * pPort +EFI_STATUS +EslUdp4RemoteAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength ) { - DT_PACKET * pPacket; - DT_SOCKET * pSocket; - DT_UDP4_CONTEXT * pUdp4; - EFI_UDP4_PROTOCOL * pUdp4Protocol; + CONST struct sockaddr_in * pRemoteAddress; + ESL_UDP4_CONTEXT * pUdp4; EFI_STATUS Status; DBG_ENTER ( ); // - // Determine if a receive is already pending + // Set the remote address // - 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; - } - } - } - } + pRemoteAddress = (struct sockaddr_in *)pSockAddr; + 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 ); + Status = EFI_SUCCESS; - DBG_EXIT ( ); + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; } /** - Shutdown the UDP4 service. + Process the receive completion - This routine undoes the work performed by ::UdpInitialize4. + This routine keeps the UDPv4 driver's buffer and queues it in + in FIFO order to the data queue. The UDP4 driver's buffer will + be returned by either ::EslUdp4Receive or ::EslSocketPortCloseTxDone. + See the \ref ReceiveEngine section. - @param [in] pService DT_SERVICE structure address + This routine is called by the UDPv4 driver when data is + received. + + @param [in] Event The receive completion event + + @param [in] pIo Address of an ::ESL_IO_MGMT structure **/ VOID -EFIAPI -EslUdpShutdown4 ( - IN DT_SERVICE * pService +EslUdp4RxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo ) { - DT_LAYER * pLayer; - DT_PORT * pPort; - DT_SERVICE * pPreviousService; - + size_t LengthInBytes; + ESL_PACKET * pPacket; + EFI_UDP4_RECEIVE_DATA * pRxData; + EFI_STATUS Status; + DBG_ENTER ( ); // - // Verify the socket layer synchronization + // Get the operation status. // - VERIFY_TPL ( TPL_SOCKETS ); - + Status = pIo->Token.Udp4Rx.Status; + // - // Walk the list of ports + // Get the packet length // - 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 ); + pRxData = pIo->Token.Udp4Rx.Packet.RxData; + LengthInBytes = pRxData->DataLength; // - // Remove the service from the service list + // +--------------------+ +-----------------------+ + // | ESL_IO_MGMT | | Data Buffer | + // | | | (Driver owned) | + // | +---------------+ +-----------------------+ + // | | Token | ^ + // | | Rx Event | | + // | | | +-----------------------+ + // | | RxData --> | EFI_UDP4_RECEIVE_DATA | + // +----+---------------+ | (Driver owned) | + // +-----------------------+ + // +--------------------+ ^ + // | ESL_PACKET | . + // | | . + // | +---------------+ . + // | | pRxData --> NULL ....... + // +----+---------------+ // - 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; - } - } - } + // + // Save the data in the packet + // + pPacket = pIo->pPacket; + pPacket->Op.Udp4Rx.pRxData = pRxData; + // + // Complete this request + // + EslSocketRxComplete ( pIo, Status, LengthInBytes, FALSE ); DBG_EXIT ( ); } /** - Determine if the sockedt is configured. + Determine if the socket is configured. + This routine uses the flag ESL_SOCKET::bConfigured to determine + if the network layer's configuration routine has been called. + This routine calls the bind and configuration routines if they + were not already called. After the port is configured, the + \ref ReceiveEngine is started. + + This routine is called by EslSocketIsConfigured to verify + that the socket is configured. + + @param [in] pSocket Address of an ::ESL_SOCKET structure - @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 + EslUdp4SocketIsConfigured ( + IN ESL_SOCKET * pSocket ) { - DT_PORT * pPort; - DT_PORT * pNextPort; - DT_UDP4_CONTEXT * pUdp4; + EFI_UDP4_CONFIG_DATA * pConfigData; + ESL_PORT * pPort; + ESL_PORT * pNextPort; + ESL_UDP4_CONTEXT * pUdp4; EFI_UDP4_PROTOCOL * pUdp4Protocol; EFI_STATUS Status; struct sockaddr_in LocalAddress; @@ -1920,9 +594,10 @@ EslUdpShutdown4 ( LocalAddress.sin_family = AF_INET; LocalAddress.sin_addr.s_addr = 0; LocalAddress.sin_port = 0; - Status = EslUdpBind4 ( pSocket, - (struct sockaddr *)&LocalAddress, - LocalAddress.sin_len ); + Status = EslSocketBind ( &pSocket->SocketProtocol, + (struct sockaddr *)&LocalAddress, + LocalAddress.sin_len, + &pSocket->errno ); } // @@ -1935,9 +610,33 @@ EslUdpShutdown4 ( // pNextPort = pPort->pLinkSocket; pUdp4 = &pPort->Context.Udp4; - pUdp4Protocol = pUdp4->pProtocol; + pUdp4Protocol = pPort->pProtocol.UDPv4; + pConfigData = &pUdp4->ConfigData; + DEBUG (( DEBUG_TX, + "0x%08x: pPort Configuring for %d.%d.%d.%d:%d --> %d.%d.%d.%d:%d\r\n", + pPort, + pConfigData->StationAddress.Addr[0], + pConfigData->StationAddress.Addr[1], + pConfigData->StationAddress.Addr[2], + pConfigData->StationAddress.Addr[3], + pConfigData->StationPort, + pConfigData->RemoteAddress.Addr[0], + pConfigData->RemoteAddress.Addr[1], + pConfigData->RemoteAddress.Addr[2], + pConfigData->RemoteAddress.Addr[3], + pConfigData->RemotePort )); Status = pUdp4Protocol->Configure ( pUdp4Protocol, - &pUdp4->ConfigData ); + pConfigData ); + if ( !EFI_ERROR ( Status )) { + // + // Update the configuration data + // + Status = pUdp4Protocol->GetModeData ( pUdp4Protocol, + pConfigData, + NULL, + NULL, + NULL ); + } if ( EFI_ERROR ( Status )) { DEBUG (( DEBUG_LISTEN, "ERROR - Failed to configure the Udp4 port, Status: %r\r\n", @@ -1970,15 +669,25 @@ EslUdpShutdown4 ( } } else { - DEBUG (( DEBUG_LISTEN, - "0x%08x: Port configured\r\n", - pPort )); - pUdp4->bConfigured = TRUE; + DEBUG (( DEBUG_TX, + "0x%08x: pPort Configured for %d.%d.%d.%d:%d --> %d.%d.%d.%d:%d\r\n", + pPort, + pConfigData->StationAddress.Addr[0], + pConfigData->StationAddress.Addr[1], + pConfigData->StationAddress.Addr[2], + pConfigData->StationAddress.Addr[3], + pConfigData->StationPort, + pConfigData->RemoteAddress.Addr[0], + pConfigData->RemoteAddress.Addr[1], + pConfigData->RemoteAddress.Addr[2], + pConfigData->RemoteAddress.Addr[3], + pConfigData->RemotePort )); + pPort->bConfigured = TRUE; // // Start the first read on the port // - EslUdpRxStart4 ( pPort ); + EslSocketRxStart ( pPort ); // // The socket is connected @@ -2018,10 +727,15 @@ EslUdpShutdown4 ( /** Buffer data for transmission over a network connection. - This routine is called by the socket layer API to buffer + This routine buffers data for the transmit engine in the normal + data queue. When the \ref TransmitEngine has resources, this + routine will start the transmission of the next buffer on the + network connection. + + This routine is called by ::EslSocketTransmit 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 + necessary, this routine starts 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. @@ -2030,7 +744,7 @@ EslUdpShutdown4 ( 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] pSocket Address of an ::ESL_SOCKET structure @param [in] Flags Message control flags @@ -2048,8 +762,8 @@ EslUdpShutdown4 ( **/ EFI_STATUS -EslUdpTxBuffer4 ( - IN DT_SOCKET * pSocket, +EslUdp4TxBuffer ( + IN ESL_SOCKET * pSocket, IN int Flags, IN size_t BufferLength, IN CONST UINT8 * pBuffer, @@ -2058,15 +772,13 @@ EslUdpTxBuffer4 ( IN socklen_t AddressLength ) { - DT_PACKET * pPacket; - DT_PACKET * pPreviousPacket; - DT_PACKET ** ppPacket; - DT_PORT * pPort; + ESL_PACKET * pPacket; + ESL_PACKET * pPreviousPacket; + ESL_PORT * pPort; const struct sockaddr_in * pRemoteAddress; - DT_UDP4_CONTEXT * pUdp4; - EFI_UDP4_COMPLETION_TOKEN * pToken; + ESL_UDP4_CONTEXT * pUdp4; size_t * pTxBytes; - DT_UDP4_TX_DATA * pTxData; + ESL_UDP4_TX_DATA * pTxData; EFI_STATUS Status; EFI_TPL TplPrevious; @@ -2077,7 +789,7 @@ EslUdpTxBuffer4 ( // Status = EFI_UNSUPPORTED; pSocket->errno = ENOTCONN; - * pDataLength = 0; + *pDataLength = 0; // // Verify that the socket is connected @@ -2092,8 +804,6 @@ EslUdpTxBuffer4 ( // Determine the queue head // pUdp4 = &pPort->Context.Udp4; - ppPacket = &pUdp4->pTxPacket; - pToken = &pUdp4->TxToken; pTxBytes = &pSocket->TxBytes; // @@ -2108,6 +818,7 @@ EslUdpTxBuffer4 ( sizeof ( pPacket->Op.Udp4Tx ) - sizeof ( pPacket->Op.Udp4Tx.Buffer ) + BufferLength, + 0, DEBUG_TX ); if ( !EFI_ERROR ( Status )) { // @@ -2120,16 +831,18 @@ EslUdpTxBuffer4 ( pTxData->TxData.FragmentCount = 1; pTxData->TxData.FragmentTable[0].FragmentLength = (UINT32) BufferLength; pTxData->TxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Udp4Tx.Buffer[0]; + pTxData->RetransmitCount = 0; // // Set the remote system address if necessary // + pTxData->TxData.UdpSessionData = NULL; 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.SourceAddress.Addr[0] = pUdp4->ConfigData.StationAddress.Addr[0]; + pTxData->Session.SourceAddress.Addr[1] = pUdp4->ConfigData.StationAddress.Addr[1]; + pTxData->Session.SourceAddress.Addr[2] = pUdp4->ConfigData.StationAddress.Addr[2]; + pTxData->Session.SourceAddress.Addr[3] = pUdp4->ConfigData.StationAddress.Addr[3]; 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 ); @@ -2192,8 +905,12 @@ EslUdpTxBuffer4 ( // // Start the transmit engine if it is idle // - if ( NULL == pUdp4->pTxPacket ) { - EslUdpTxStart4 ( pSocket->pPortList ); + if ( NULL != pPort->pTxFree ) { + EslSocketTxStart ( pPort, + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); } } else { @@ -2243,23 +960,27 @@ EslUdpTxBuffer4 ( /** Process the transmit completion - @param Event The normal transmit completion event + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for data packets. + + This routine is called by the UDPv4 network layer when a data + transmit request completes. - @param pPort The DT_PORT structure address + @param [in] Event The normal transmit completion event + + @param [in] pIo Address of an ::ESL_IO_MGMT structure **/ VOID -EslUdpTxComplete4 ( +EslUdp4TxComplete ( IN EFI_EVENT Event, - IN DT_PORT * pPort + IN ESL_IO_MGMT * pIo ) { UINT32 LengthInBytes; - DT_PACKET * pCurrentPacket; - DT_PACKET * pNextPacket; - DT_PACKET * pPacket; - DT_SOCKET * pSocket; - DT_UDP4_CONTEXT * pUdp4; + ESL_PORT * pPort; + ESL_PACKET * pPacket; + ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); @@ -2267,142 +988,69 @@ EslUdpTxComplete4 ( // // Locate the active transmit packet // + pPacket = pIo->pPacket; + pPort = pIo->pPort; pSocket = pPort->pSocket; - pUdp4 = &pPort->Context.Udp4; - pPacket = pUdp4->pTxPacket; - + // - // Mark this packet as complete + // Get the transmit length and status // - 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 ); - + Status = pIo->Token.Udp4Tx.Status; + // - // Finish the close operation if necessary + // Complete the transmit operation // - if (( PORT_STATE_CLOSE_STARTED <= pPort->State ) - && ( NULL == pSocket->pTxPacketListHead ) - && ( NULL == pUdp4->pTxPacket )) { - // - // Indicate that the transmit is complete - // - EslUdpPortCloseTxDone4 ( pPort ); - } + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "UDP ", + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); 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 ( ); -} - + Interface between the socket layer and the network specific + code that supports SOCK_DGRAM sockets over UDPv4. +**/ +CONST ESL_PROTOCOL_API cEslUdp4Api = { + "UDPv4", + IPPROTO_UDP, + OFFSET_OF ( ESL_PORT, Context.Udp4.ConfigData ), + OFFSET_OF ( ESL_LAYER, pUdp4List ), + OFFSET_OF ( struct sockaddr_in, sin_zero ), + sizeof ( struct sockaddr_in ), + AF_INET, + sizeof (((ESL_PACKET *)0 )->Op.Udp4Rx ), + sizeof (((ESL_PACKET *)0 )->Op.Udp4Rx ), + OFFSET_OF ( ESL_IO_MGMT, Token.Udp4Rx.Packet.RxData ), + FALSE, + EADDRINUSE, + NULL, // Accept + NULL, // ConnectPoll + NULL, // ConnectStart + EslUdp4SocketIsConfigured, + EslUdp4LocalAddressGet, + EslUdp4LocalAddressSet, + NULL, // Listen + NULL, // OptionGet + NULL, // OptionSet + EslUdp4PacketFree, + EslUdp4PortAllocate, + NULL, // PortClose, + NULL, // PortCloseOp + TRUE, + EslUdp4Receive, + EslUdp4RemoteAddressGet, + EslUdp4RemoteAddressSet, + EslUdp4RxComplete, + NULL, // RxStart + EslUdp4TxBuffer, + EslUdp4TxComplete, + NULL // TxOobComplete +}; diff --git a/StdLib/EfiSocketLib/UseEfiSocketLib.c b/StdLib/EfiSocketLib/UseEfiSocketLib.c index a07b2b6e22..1c122af650 100644 --- a/StdLib/EfiSocketLib/UseEfiSocketLib.c +++ b/StdLib/EfiSocketLib/UseEfiSocketLib.c @@ -15,14 +15,30 @@ #include "Socket.h" -CONST EFI_GUID mEslRawServiceGuid = { - 0xc31bf4a5, 0x2c7, 0x49d2, { 0xa5, 0x58, 0xfe, 0x62, 0x6f, 0x7e, 0xd4, 0x77 } +/** + The following GUID values are only used when an application links + against EfiSocketLib. An alternative set of values exists in + SocketDxe\EntryUnload.c which the SocketDxe driver uses to coexist + with socket applications. + + Tag GUID - IPv4 in use by an application using EfiSocketLib +**/ +CONST EFI_GUID mEslIp4ServiceGuid = { + 0x9c756011, 0x5d44, 0x4ee0, { 0xbc, 0xe7, 0xc3, 0x82, 0x18, 0xfe, 0x39, 0x8d } }; + +/** + Tag GUID - TCPv4 in use by an application using EfiSocketLib +**/ CONST EFI_GUID mEslTcp4ServiceGuid = { 0xffc659c2, 0x4ef2, 0x4532, { 0xb8, 0x75, 0xcd, 0x9a, 0xa4, 0x27, 0x4c, 0xde } }; + +/** + Tag GUID - UDPv4 in use by an application using EfiSocketLib +**/ CONST EFI_GUID mEslUdp4ServiceGuid = { 0x44e03a55, 0x8d97, 0x4511, { 0xbf, 0xef, 0xa, 0x8b, 0xc6, 0x2c, 0x25, 0xae } }; @@ -31,10 +47,20 @@ CONST EFI_GUID mEslUdp4ServiceGuid = { /** Connect to the EFI socket library - @param [in] ppSocketProtocol Address to receive the socket protocol address + This routine creates the ::ESL_SOCKET structure and returns + the API (::EFI_SOCKET_PROTOCOL address) to the socket file + system layer in BsdSocketLib. + + This routine is called from the ::socket routine in BsdSocketLib + to create the data structure and initialize the API for a socket. + Note that this implementation is only used by socket applications + that link directly to EslSocketLib. + + @param [in] ppSocketProtocol Address to receive the ::EFI_SOCKET_PROTOCOL + structure address + + @return Value for ::errno, zero (0) indicates success. - @retval 0 Successfully returned the socket protocol - @retval other Value for errno **/ int EslServiceGetProtocol ( @@ -42,7 +68,7 @@ EslServiceGetProtocol ( ) { EFI_HANDLE ChildHandle; - DT_SOCKET * pSocket; + ESL_SOCKET * pSocket; int RetVal; EFI_STATUS Status; @@ -81,6 +107,16 @@ EslServiceGetProtocol ( /** Connect to the network layer + This routine is the constructor for the EfiSocketLib when the + library is linked directly to an application. This routine + walks the ::cEslSocketBinding table to create ::ESL_SERVICE + structures, associated with the network adapters, which this + routine links to the ::ESL_LAYER structure. + + This routine is called from ::EslConstructor as a result of the + constructor redirection in ::mpfnEslConstructor at the end of this + file. + @retval EFI_SUCCESS Successfully connected to the network layer **/ @@ -89,11 +125,12 @@ EslServiceNetworkConnect ( VOID ) { + BOOLEAN bSomethingFound; UINTN HandleCount; - EFI_HANDLE * pHandles; UINTN Index; - CONST DT_SOCKET_BINDING * pSocketBinding; - CONST DT_SOCKET_BINDING * pEnd; + CONST ESL_SOCKET_BINDING * pEnd; + EFI_HANDLE * pHandles; + CONST ESL_SOCKET_BINDING * pSocketBinding; EFI_STATUS Status; DBG_ENTER ( ); @@ -102,13 +139,14 @@ EslServiceNetworkConnect ( // Initialize the socket layer // Status = EFI_SUCCESS; + bSomethingFound = FALSE; EslServiceLoad ( gImageHandle ); // // Connect the network devices // - pSocketBinding = &cEslSocketBinding [0]; - pEnd = &pSocketBinding [ cEslSocketBindingEntries ]; + pSocketBinding = &cEslSocketBinding[0]; + pEnd = &pSocketBinding[ cEslSocketBindingEntries ]; while ( pEnd > pSocketBinding ) { // // Attempt to locate the network adapters @@ -121,24 +159,30 @@ EslServiceNetworkConnect ( &HandleCount, &pHandles ); if ( EFI_ERROR ( Status )) { - break; + DEBUG (( DEBUG_ERROR, + "ERROR with %s layer, Status: %r\r\n", + pSocketBinding->pName, + Status )); } - 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; + else { + 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; + } + bSomethingFound = TRUE; } - } - // - // Done with the handles - // - gBS->FreePool ( pHandles ); + // + // Done with the handles + // + gBS->FreePool ( pHandles ); + } } // @@ -150,6 +194,9 @@ EslServiceNetworkConnect ( // // Return the network connection status // + if ( bSomethingFound ) { + Status = EFI_SUCCESS; + } DBG_EXIT_STATUS ( Status ); return Status; } @@ -158,6 +205,15 @@ EslServiceNetworkConnect ( /** Disconnect from the network layer + Destructor for the EfiSocketLib when the library is linked + directly to an application. This routine walks the + ::cEslSocketBinding table to remove the ::ESL_SERVICE + structures (network connections) from the ::ESL_LAYER structure. + + This routine is called from ::EslDestructor as a result of the + destructor redirection in ::mpfnEslDestructor at the end of this + file. + @retval EFI_SUCCESS Successfully disconnected from the network layer **/ @@ -167,10 +223,10 @@ EslServiceNetworkDisconnect ( ) { UINTN HandleCount; - EFI_HANDLE * pHandles; UINTN Index; - CONST DT_SOCKET_BINDING * pSocketBinding; - CONST DT_SOCKET_BINDING * pEnd; + CONST ESL_SOCKET_BINDING * pEnd; + EFI_HANDLE * pHandles; + CONST ESL_SOCKET_BINDING * pSocketBinding; EFI_STATUS Status; DBG_ENTER ( ); @@ -183,8 +239,8 @@ EslServiceNetworkDisconnect ( // // Disconnect the network devices // - pSocketBinding = &cEslSocketBinding [0]; - pEnd = &pSocketBinding [ cEslSocketBindingEntries ]; + pSocketBinding = &cEslSocketBinding[0]; + pEnd = &pSocketBinding[ cEslSocketBindingEntries ]; while ( pEnd > pSocketBinding ) { // // Attempt to locate the network adapters @@ -205,7 +261,7 @@ EslServiceNetworkDisconnect ( // for ( Index = 0; HandleCount > Index; Index++ ) { Status = EslServiceDisconnect ( gImageHandle, - pHandles [ Index ]); + pHandles[ Index ]); if ( EFI_ERROR ( Status )) { break; } @@ -238,5 +294,19 @@ EslServiceNetworkDisconnect ( } -PFN_ESL_xSTRUCTOR mpfnEslConstructor = EslServiceNetworkConnect; -PFN_ESL_xSTRUCTOR mpfnEslDestructor = EslServiceNetworkDisconnect; +/** + Socket layer's service binding protocol delcaration. +**/ +CONST EFI_SERVICE_BINDING_PROTOCOL mEfiServiceBinding = { + NULL, + NULL +}; + + +/** + The following entries redirect the constructor and destructor + for any socket application that links against the EfiSocketLib. + Note that the SocketDxe driver uses different redirection. +**/ +PFN_ESL_xSTRUCTOR mpfnEslConstructor = EslServiceNetworkConnect; ///< Constructor for EfiSocketLib +PFN_ESL_xSTRUCTOR mpfnEslDestructor = EslServiceNetworkDisconnect; ///< Destructor for EfiSocketLib -- cgit v1.2.3