X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=StdLib%2FEfiSocketLib%2FTcp4.c;h=68477fba6e70122d173a2ffc918564a4a6432446;hp=b489608a5bac5c26687c5c87c2906205c8cdfc9e;hb=c581e5037dca6e1446972aed194f13c9cdd0b01b;hpb=1c34b250f66ba304a4da510404caa827af2ad91e diff --git a/StdLib/EfiSocketLib/Tcp4.c b/StdLib/EfiSocketLib/Tcp4.c index b489608a5b..68477fba6e 100644 --- a/StdLib/EfiSocketLib/Tcp4.c +++ b/StdLib/EfiSocketLib/Tcp4.c @@ -1,28 +1,97 @@ /** @file Implement the TCP4 driver support for the socket layer. - Copyright (c) 2011, Intel Corporation - All rights reserved. This program and the accompanying materials - are licensed and made available under the terms and conditions of the BSD License - which accompanies this distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php + Copyright (c) 2011 - 2015, 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. + + \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,12 +226,19 @@ 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 )); + // + // Start the receive operations + // + pSocket->bConfigured = TRUE; + pSocket->State = SOCKET_STATE_CONNECTED; + EslSocketRxStart ( pPort ); + // // Remove the rest of the ports // @@ -437,20 +248,22 @@ EslTcpConnectComplete4 ( // // The connection failed // - DEBUG (( DEBUG_CONNECT, - "0x%08x: Port connection to %d.%d.%d.%d:%d failed, Status: %r\r\n", - pPort, - pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [0], - pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [1], - pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [2], - pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [3], - pTcp4->ConfigData.AccessPoint.RemotePort, - Status )); + if ( pPort->bConfigured ) { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port connection to %d.%d.%d.%d:%d failed, Status: %r\r\n", + pPort, + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp4->ConfigData.AccessPoint.RemotePort, + Status )); + } // // Close the current port // - Status = EslTcpPortClose4 ( pPort ); + Status = EslSocketPortClose ( pPort ); if ( !EFI_ERROR ( Status )) { DEBUG (( DEBUG_CONNECT, "0x%08x: Port closed\r\n", @@ -466,9 +279,8 @@ 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 +302,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 +330,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. - @param [in] pSocket Address of the socket structure. + 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 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 +347,8 @@ EslTcpConnectComplete4 ( **/ EFI_STATUS -EslTcpConnectPoll4 ( - IN DT_SOCKET * pSocket +EslTcp4ConnectPoll ( + IN ESL_SOCKET * pSocket ) { EFI_STATUS Status; @@ -564,29 +382,47 @@ EslTcpConnectPoll4 ( break; case EFI_ABORTED: - pSocket->errno = ECONNREFUSED; + pSocket->errno = ECONNABORTED; + break; + + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + case EFI_CONNECTION_RESET: + pSocket->errno = ECONNRESET; break; case EFI_INVALID_PARAMETER: - pSocket->errno = EINVAL; + pSocket->errno = EADDRNOTAVAIL; break; - case EFI_NO_MAPPING: + case EFI_HOST_UNREACHABLE: case EFI_NO_RESPONSE: pSocket->errno = EHOSTUNREACH; break; + case EFI_NO_MAPPING: + pSocket->errno = EAFNOSUPPORT; + break; + case EFI_NO_MEDIA: + case EFI_NETWORK_UNREACHABLE: pSocket->errno = ENETDOWN; break; case EFI_OUT_OF_RESOURCES: - pSocket->errno = ENOMEM; + pSocket->errno = ENOBUFS; + break; + + case EFI_PORT_UNREACHABLE: + case EFI_PROTOCOL_UNREACHABLE: + case EFI_CONNECTION_REFUSED: + pSocket->errno = ECONNREFUSED; break; case EFI_SUCCESS: pSocket->errno = 0; - pSocket->bConfigured = TRUE; break; case EFI_TIMEOUT: @@ -594,13 +430,17 @@ EslTcpConnectPoll4 ( break; case EFI_UNSUPPORTED: - pSocket->errno = ENOTSUP; - break; - - case 0x80000069: - pSocket->errno = ECONNRESET; + pSocket->errno = EOPNOTSUPP; break; } + + // + // Display the translation + // + DEBUG (( DEBUG_CONNECT, + "ERROR - errno: %d, Status: %r\r\n", + pSocket->errno, + Status )); } // @@ -612,292 +452,141 @@ 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_SIMPLE_NETWORK_MODE SnpModeData; 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 ); + 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 )); } - if ( NULL != pSocket->pPortList ) { + else { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port configured\r\n", + pPort )); + pPort->bConfigured = TRUE; + // - // Walk the list of ports + // Verify the port connection // - 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 ); - + Status = pTcp4Protocol->GetModeData ( pTcp4Protocol, + NULL, + NULL, + NULL, + NULL, + &SnpModeData ); + if ( !EFI_ERROR ( Status )) { + if ( SnpModeData.MediaPresentSupported + && ( !SnpModeData.MediaPresent )) { + // + // Port is not connected to the network + // + Status = EFI_NO_MEDIA; + } + else { + // + // Attempt the connection to the remote system + // + Status = pTcp4Protocol->Connect ( pTcp4Protocol, + &pTcp4->ConnectToken ); + } + } + if ( EFI_ERROR ( Status )) { // - // Set the next port + // Connection error // - pPort = pPort->pLinkSocket; + DEBUG (( DEBUG_CONNECT, + "ERROR - Port 0x%08x not connected, Status: %r\r\n", + pPort, + Status )); } - - // - // 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 ) { + if ( !EFI_ERROR ( Status )) { // - // Return the local address + // Connection in progress // - 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; + pSocket->errno = EINPROGRESS; + 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 { - pSocket->errno = EINVAL; - Status = EFI_INVALID_PARAMETER; - } - } - else { - pSocket->errno = ENOTCONN; - Status = EFI_NOT_STARTED; - } - - // - // Return the operation status - // - DBG_EXIT_STATUS ( Status ); - return Status; -} - - -/** - Get the remote socket address - - @param [in] pSocket Address of the socket structure. - - @param [out] pAddress Network address to receive the remote system address - - @param [in,out] pAddressLength Length of the remote network address structure - - @retval EFI_SUCCESS - Address available - @retval Other - Failed to get the address - -**/ -EFI_STATUS -EslTcpGetRemoteAddress4 ( - IN DT_SOCKET * pSocket, - OUT struct sockaddr * pAddress, - IN OUT socklen_t * pAddressLength - ) -{ - socklen_t LengthInBytes; - DT_PORT * pPort; - struct sockaddr_in * pRemoteAddress; - DT_TCP4_CONTEXT * pTcp4; - EFI_STATUS Status; - - DBG_ENTER ( ); - - // - // Verify the socket layer synchronization - // - VERIFY_TPL ( TPL_SOCKETS ); + // + // Error return path is through EslTcp4ConnectComplete to + // enable retry on other ports + // + // Status to errno translation gets done in EslTcp4ConnectPoll + // + pTcp4->ConnectToken.CompletionToken.Status = Status; - // - // 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 + // Continue with the next port // - 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; + gBS->CheckEvent ( pTcp4->ConnectToken.CompletionToken.Event ); + gBS->SignalEvent ( pTcp4->ConnectToken.CompletionToken.Event ); } + Status = EFI_NOT_READY; } 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 +598,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 +659,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 +676,7 @@ EslTcpListen4 ( // // Configure the port // - pTcp4Protocol = pTcp4->pProtocol; + pTcp4Protocol = pPort->pProtocol.TCPv4; Status = pTcp4Protocol->Configure ( pTcp4Protocol, &pTcp4->ConfigData ); if ( EFI_ERROR ( Status )) { @@ -1025,7 +714,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 +767,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 ); } // @@ -1088,7 +776,7 @@ EslTcpListen4 ( // pPort = pNextPort; } - + // // Determine if any ports are in the listen state // @@ -1108,6 +796,8 @@ EslTcpListen4 ( // Mark the socket as configured // pSocket->bConfigured = TRUE; + Status = EFI_SUCCESS; + pSocket->errno = 0; // // All done @@ -1132,30 +822,35 @@ 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_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 @@ -1173,7 +868,6 @@ EslTcpListenComplete4 ( // Allocate a socket for this connection // ChildHandle = NULL; - pLayer = &mEslLayer; Status = EslSocketAllocate ( &ChildHandle, DEBUG_CONNECTION, &pNewSocket ); @@ -1181,26 +875,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 +913,6 @@ EslTcpListenComplete4 ( // TcpPortHandle = NULL; pTcp4 = &pNewPort->Context.Tcp4; - pTcp4->bConfigured = TRUE; // // Check for an accept call error @@ -1218,9 +921,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 +980,7 @@ EslTcpListenComplete4 ( // // Start the receive operation // - EslTcpRxStart4 ( pNewPort ); + EslSocketRxStart ( pNewPort ); } else { DEBUG (( DEBUG_ERROR | DEBUG_CONNECTION | DEBUG_INFO, @@ -1297,7 +1001,7 @@ EslTcpListenComplete4 ( // // Close the listening port // - EslTcpPortCloseStart4 ( pPort, TRUE, DEBUG_LISTEN ); + EslSocketPortCloseStart ( pPort, TRUE, DEBUG_LISTEN ); } } @@ -1335,7 +1039,7 @@ EslTcpListenComplete4 ( // Process: // Call close // Release the resources - + } DBG_EXIT ( ); @@ -1343,142 +1047,234 @@ 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] = ( 128 <= pAccessPoint->StationAddress.Addr[0]) ? 0xff : 0; + pAccessPoint->SubnetMask.Addr[2] = ( 192 <= pAccessPoint->StationAddress.Addr[0]) ? 0xff : 0; + pAccessPoint->SubnetMask.Addr[3] = ( 224 <= pAccessPoint->StationAddress.Addr[0]) ? 0xff : 0; } - 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 ); + pPort->pSocket->bAddressSet = TRUE; + + // + // 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 )); + } - // - // Allocate the close event - // - Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, - TPL_SOCKETS, - (EFI_EVENT_NOTIFY)EslTcpPortCloseComplete4, - pPort, - &pTcp4->CloseToken.CompletionToken.Event); - if ( EFI_ERROR ( Status )) { - DEBUG (( DEBUG_ERROR | DebugFlags, - "ERROR - Failed to create the close event, Status: %r\r\n", + // + // 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)EslSocketPortCloseComplete, + pPort, + &pTcp4->CloseToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the close event, Status: %r\r\n", Status )); pSocket->errno = ENOMEM; break; @@ -1492,7 +1288,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 +1303,30 @@ 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->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.TCPv4->Poll; + 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 +1338,35 @@ EslTcpPortAllocate4 ( /** Close a TCP4 port. - This routine releases the resources allocated by - ::TcpPortAllocate4(). - - @param [in] pPort Address of the port structure. + 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 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 ); + DBG_ENTER ( ); // // 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 +1422,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,94 +1431,52 @@ EslTcpPortClose4 ( /** - Process the port close completion - - @param Event The close completion event - - @param pPort The DT_PORT structure address - -**/ -VOID -EslTcpPortCloseComplete4 ( - IN EFI_EVENT Event, - IN DT_PORT * pPort - ) -{ - EFI_STATUS Status; - - DBG_ENTER ( ); - - // - // Update the port state - // - pPort->State = PORT_STATE_CLOSE_DONE; + Perform the network specific close operation on the port. - // - // Release the resources once the receive operation completes - // - Status = EslTcpPortCloseRxDone4 ( pPort ); - DBG_EXIT_STATUS ( Status ); -} + This routine performs a cancel operations on the TCPv4 port to + shutdown the receive operations on the port. + This routine is called by the ::EslSocketPortCloseTxDone + routine after the port completes all of the transmission. -/** - Start the close operation on a TCP4 port, state 1. - - Closing a port goes through the following states: - 1. Port close starting - Mark the port as closing and wait for transmission to complete - 2. Port TX close done - Transmissions complete, close the port and abort the receives - 3. Port RX close done - Receive operations complete, close the port - 4. Port closed - Release the port resources - - @param [in] pPort Address of the port structure. - @param [in] bCloseNow Set TRUE to abort active transfers - @param [in] DebugFlags Flags for debug messages + @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 has started the closing process + @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 -EslTcpPortCloseStart4 ( - IN DT_PORT * pPort, - IN BOOLEAN bCloseNow, - IN UINTN DebugFlags +EslTcp4PortCloseOp ( + IN ESL_PORT * pPort ) { - DT_SOCKET * pSocket; + ESL_TCP4_CONTEXT * pTcp4; + EFI_TCP4_PROTOCOL * pTcp4Protocol; EFI_STATUS Status; DBG_ENTER ( ); // - // Verify the socket layer synchronization - // - VERIFY_TPL ( TPL_SOCKETS ); - - // - // Mark the port as closing + // Close the configured port // - 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", + 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 )); - pPort->bCloseNow = bCloseNow; - pPort->DebugFlags = DebugFlags; - - // - // Determine if transmits are complete - // - Status = EslTcpPortCloseTxDone4 ( pPort ); + } + else { + DEBUG (( DEBUG_ERROR | pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "ERROR - Close failed on port 0x%08x, Status: %r\r\n", + pPort, + Status )); } // @@ -2000,890 +1488,343 @@ EslTcpPortCloseStart4 ( /** - Port close state 3 + Receive data from a network connection. - Continue the close operation after the receive is complete. + 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] pPort Address of the port structure. + This routine is called by ::EslSocketReceive to handle the network + specific receive operation to support SOCK_STREAM and SOCK_SEQPACKET + sockets. - @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] pPort Address of an ::ESL_PORT structure. -**/ -EFI_STATUS -EslTcpPortCloseRxDone4 ( - IN DT_PORT * pPort - ) -{ - PORT_STATE PortState; - DT_TCP4_CONTEXT * pTcp4; - EFI_STATUS Status; + @param [in] pPacket Address of an ::ESL_PACKET structure. - DBG_ENTER ( ); + @param [in] pbConsumePacket Address of a BOOLEAN indicating if the packet is to be consumed - // - // Verify the socket layer synchronization - // - VERIFY_TPL ( TPL_SOCKETS ); - - // - // Verify that the port is closing - // - Status = EFI_ALREADY_STARTED; - PortState = pPort->State; - if (( PORT_STATE_CLOSE_TX_DONE == PortState ) - || ( PORT_STATE_CLOSE_DONE == PortState )) { - // - // Determine if the receive operation is pending - // - Status = EFI_NOT_READY; - pTcp4 = &pPort->Context.Tcp4; - if ( NULL == pTcp4->pReceivePending ) { - // - // The receive operation is complete - // Update the port state - // - pPort->State = PORT_STATE_CLOSE_RX_DONE; - DEBUG (( DEBUG_CLOSE | DEBUG_INFO, - "0x%08x: Port Close State: PORT_STATE_CLOSE_RX_DONE\r\n", - pPort )); - - // - // Determine if the close operation has completed - // - if ( PORT_STATE_CLOSE_DONE == PortState ) { - // - // The close operation has completed - // Release the port resources - // - Status = EslTcpPortClose4 ( pPort ); - } - else - { - DEBUG (( DEBUG_CLOSE | DEBUG_INFO, - "0x%08x: Port Close: Close operation still pending!\r\n", - pPort )); - } - } - else { - DEBUG (( DEBUG_CLOSE | DEBUG_INFO, - "0x%08x: Port Close: Receive still pending!\r\n", - pPort )); - } - } - - // - // Return the operation status - // - DBG_EXIT_STATUS ( Status ); - return Status; -} - - -/** - Port close state 2 - - Continue the close operation after the transmission is complete. - - @param [in] pPort Address of the port structure. - - @retval EFI_SUCCESS The port is closed, not normally returned - @retval EFI_NOT_READY The port is still closing - @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, - most likely the routine was called already. - -**/ -EFI_STATUS -EslTcpPortCloseTxDone4 ( - IN DT_PORT * pPort - ) -{ - DT_SOCKET * pSocket; - DT_TCP4_CONTEXT * pTcp4; - EFI_TCP4_PROTOCOL * pTcp4Protocol; - EFI_STATUS Status; - - DBG_ENTER ( ); - - // - // Verify the socket layer synchronization - // - VERIFY_TPL ( TPL_SOCKETS ); - - // - // All transmissions are complete or must be stopped - // Mark the port as TX complete - // - Status = EFI_ALREADY_STARTED; - if ( PORT_STATE_CLOSE_STARTED == pPort->State ) { - // - // Verify that the transmissions are complete - // - pSocket = pPort->pSocket; - if ( pPort->bCloseNow - || ( EFI_SUCCESS != pSocket->TxError ) - || (( 0 == pSocket->TxOobBytes ) - && ( 0 == pSocket->TxBytes ))) { - // - // Start the close operation on the port - // - pTcp4 = &pPort->Context.Tcp4; - pTcp4->CloseToken.AbortOnClose = FALSE; - pTcp4Protocol = pTcp4->pProtocol; - if ( !pTcp4->bConfigured ) { - // - // Skip the close operation since the port is not - // configured - // - // Update the port state - // - pPort->State = PORT_STATE_CLOSE_DONE; - DEBUG (( DEBUG_CLOSE | DEBUG_INFO, - "0x%08x: Port Close State: PORT_STATE_CLOSE_DONE\r\n", - pPort )); - Status = EFI_SUCCESS; - } - else { - // - // Close the configured port - // - Status = pTcp4Protocol->Close ( pTcp4Protocol, - &pTcp4->CloseToken ); - if ( !EFI_ERROR ( Status )) { - DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, - "0x%08x: Port close started\r\n", - pPort )); - - // - // Update the port state - // - pPort->State = PORT_STATE_CLOSE_TX_DONE; - DEBUG (( DEBUG_CLOSE | DEBUG_INFO, - "0x%08x: Port Close State: PORT_STATE_CLOSE_TX_DONE\r\n", - pPort )); - } - else { - DEBUG (( DEBUG_ERROR | pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, - "ERROR - Close failed on port 0x%08x, Status: %r\r\n", - pPort, - Status )); - ASSERT ( EFI_SUCCESS == Status ); - } - } - - // - // Determine if the receive operation is pending - // - if ( !EFI_ERROR ( Status )) { - Status = EslTcpPortCloseRxDone4 ( pPort ); - } - } - else { - // - // Transmissions are still active, exit - // - DEBUG (( DEBUG_CLOSE | DEBUG_INFO, - "0x%08x: Port Close: Transmits are still pending!\r\n", - pPort )); - Status = EFI_NOT_READY; - pSocket->errno = EAGAIN; - } - } - - // - // Return the operation status - // - DBG_EXIT_STATUS ( Status ); - return Status; -} - - -/** - Receive data from a network connection. - - - @param [in] pSocket Address of a DT_SOCKET structure - - @param [in] Flags Message control flags - @param [in] BufferLength Length of the the buffer - + @param [in] pBuffer Address of a buffer to receive the data. - + @param [in] pDataLength Number of received data bytes in the buffer. @param [out] pAddress Network address to receive the remote system address - @param [in,out] pAddressLength Length of the remote network address structure + @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; + ESL_TCP4_CONTEXT * pTcp4; DBG_ENTER ( ); // - // Assume failure + // Return the remote system address if requested // - Status = EFI_UNSUPPORTED; - pSocket->errno = ENOTCONN; - - // - // Verify that the socket is connected - // - if (( SOCKET_STATE_CONNECTED == pSocket->State ) - || ( PORT_STATE_RX_ERROR == pSocket->State )) { + if ( NULL != pAddress ) { // - // Locate the port + // Build the remote address // - 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; - } - } - } + pTcp4 = &pPort->Context.Tcp4; + 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; -} - - -/** - 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; + DataLength = pPacket->ValidBytes; + if ( BufferLength < DataLength ) { + DataLength = BufferLength; + } - DBG_ENTER ( ); + // + // 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 ); // - // Assume failure + // Set the next buffer address // - Status = EFI_NOT_FOUND; + pBuffer += DataLength; // - // Locate the port + // Determine if the data is being read // - pPort = pSocket->pPortList; - if ( NULL != pPort ) { + if ( *pbConsumePacket ) { // - // Determine if a receive is pending + // Account for the bytes consumed // - pTcp4 = &pPort->Context.Tcp4; - pPacket = pTcp4->pReceivePending; - if ( NULL != pPacket ) { + pPacket->pBuffer += DataLength; + pPacket->ValidBytes -= DataLength; + DEBUG (( DEBUG_RX, + "0x%08x: Port account for 0x%08x bytes\r\n", + pPort, + DataLength )); + + // + // Determine if the entire packet was consumed + // + if (( 0 == pPacket->ValidBytes ) + || ( SOCK_STREAM != pPort->pSocket->Type )) { // - // Attempt to cancel the receive operation + // All done with this packet + // Account for any discarded data // - pTcp4Protocol = pTcp4->pProtocol; - Status = pTcp4Protocol->Cancel ( pTcp4Protocol, - &pTcp4->RxToken.CompletionToken ); - if ( EFI_NOT_FOUND == Status ) { - // - // The receive is complete - // - Status = EFI_SUCCESS; - } + *pSkipBytes = pPacket->ValidBytes; + } + else + { + // + // More data to consume later + // + *pbConsumePacket = FALSE; } } // - // Return the operation status + // Return the data length and the buffer address // - DBG_EXIT_STATUS ( Status ); - return Status; + *pDataLength = DataLength; + DBG_EXIT_HEX ( pBuffer ); + return pBuffer; } /** - Process the receive completion + Get the remote socket address. - Buffer the data that was just received. + 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 Event The receive completion event + @param [in] pPort Address of an ::ESL_PORT structure. - @param pPort The DT_PORT structure address + @param [out] pAddress Network address to receive the remote system address **/ VOID -EslTcpRxComplete4 ( - IN EFI_EVENT Event, - IN DT_PORT * pPort +EslTcp4RemoteAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress ) -{ - BOOLEAN bUrgent; - size_t LengthInBytes; - DT_PACKET * pPacket; - DT_PACKET * pPrevious; - DT_SOCKET * pSocket; - DT_TCP4_CONTEXT * pTcp4; - EFI_STATUS Status; - - DBG_ENTER ( ); - - // - // Mark this receive complete - // - pTcp4 = &pPort->Context.Tcp4; - pPacket = pTcp4->pReceivePending; - pTcp4->pReceivePending = NULL; - - // - // Determine if this receive was successful - // - pSocket = pPort->pSocket; - Status = pTcp4->RxToken.CompletionToken.Status; - if (( !EFI_ERROR ( Status )) && ( !pSocket->bRxDisable )) { - // - // Set the buffer size and address - // - pPacket->Op.Tcp4Rx.pBuffer = pPacket->Op.Tcp4Rx.RxData.FragmentTable[0].FragmentBuffer; - LengthInBytes = pPacket->Op.Tcp4Rx.RxData.DataLength; - pPacket->Op.Tcp4Rx.ValidBytes = LengthInBytes; - pPacket->pNext = NULL; - - // - // Queue this packet - // - bUrgent = pPacket->Op.Tcp4Rx.RxData.UrgentFlag; - if ( bUrgent ) { - // - // Add packet to the urgent list - // - pPrevious = pSocket->pRxOobPacketListTail; - if ( NULL == pPrevious ) { - pSocket->pRxOobPacketListHead = pPacket; - } - else { - pPrevious->pNext = pPacket; - } - pSocket->pRxOobPacketListTail = pPacket; - - // - // Account for the urgent data - // - pSocket->RxOobBytes += LengthInBytes; - } - else { - // - // Add packet to the normal list - // - pPrevious = pSocket->pRxPacketListTail; - if ( NULL == pPrevious ) { - pSocket->pRxPacketListHead = pPacket; - } - else { - pPrevious->pNext = pPacket; - } - pSocket->pRxPacketListTail = pPacket; - - // - // Account for the normal data - // - pSocket->RxBytes += LengthInBytes; - } - - // - // Log the received data - // - DEBUG (( DEBUG_RX | DEBUG_INFO, - "0x%08x: Packet queued on port 0x%08x with 0x%08x bytes of %s data\r\n", - pPacket, - pPort, - LengthInBytes, - bUrgent ? L"urgent" : L"normal" )); - - // - // Attempt to restart this receive operation - // - if ( pSocket->MaxRxBuf > pSocket->RxBytes ) { - EslTcpRxStart4 ( pPort ); - } - else { - DEBUG (( DEBUG_RX, - "0x%08x: Port RX suspended, 0x%08x bytes queued\r\n", - pPort, - pSocket->RxBytes )); - } - } - else - { - DEBUG (( DEBUG_RX | DEBUG_INFO, - "ERROR - Receiving packet 0x%08x, on port 0x%08x, Status:%r\r\n", - pPacket, - pPort, - Status )); +{ + struct sockaddr_in * pRemoteAddress; + ESL_TCP4_CONTEXT * pTcp4; - // - // Receive error, free the packet save the error - // - EslSocketPacketFree ( pPacket, DEBUG_RX ); - if ( !EFI_ERROR ( pSocket->RxError )) { - pSocket->RxError = Status; - } + DBG_ENTER ( ); - // - // Update the port state - // - if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { - EslTcpPortCloseRxDone4 ( pPort ); - } - else { - if ( EFI_ERROR ( Status )) { - pPort->State = PORT_STATE_RX_ERROR; - } - } - } + // + // 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. + + Buffer the data that was just received. - This routine undoes the work performed by ::TcpInitialize4. + @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 +1833,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,52 +1870,58 @@ 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 - + @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 -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; size_t * pTxBytes; EFI_TCP4_TRANSMIT_DATA * pTxData; EFI_STATUS Status; @@ -2982,7 +1934,7 @@ EslTcpTxBuffer4 ( // Status = EFI_UNSUPPORTED; pSocket->errno = ENOTCONN; - * pDataLength = 0; + *pDataLength = 0; // // Verify that the socket is connected @@ -2996,20 +1948,22 @@ EslTcpTxBuffer4 ( // // Determine the queue head // - 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 +1972,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 +1988,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 +2042,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 +2053,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 +2088,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 +2116,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. - @param pPort The DT_PORT structure address + 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 [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 +2173,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 +2201,232 @@ 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. + Verify the adapter's IP address + This support routine is called by EslSocketBindTest. - @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 + @param [in] pPort Address of an ::ESL_PORT structure. + @param [in] pConfigData Address of the configuration data + + @retval EFI_SUCCESS - The IP address is valid + @retval EFI_NOT_STARTED - The IP address is invalid **/ -VOID -EslTcpTxStart4 ( - IN DT_PORT * pPort, - IN EFI_TCP4_IO_TOKEN * pToken, - IN DT_PACKET ** ppQueueHead, - IN DT_PACKET ** ppQueueTail, - IN DT_PACKET ** ppPacket +EFI_STATUS +EslTcp4VerifyLocalIpAddress ( + IN ESL_PORT * pPort, + IN EFI_TCP4_CONFIG_DATA * pConfigData ) { - DT_PACKET * pNextPacket; - DT_PACKET * pPacket; - DT_SOCKET * pSocket; - EFI_TCP4_PROTOCOL * pTcp4Protocol; + UINTN DataSize; + EFI_TCP4_ACCESS_POINT * pAccess; + EFI_IP4_CONFIG2_INTERFACE_INFO * pIfInfo; + EFI_IP4_CONFIG2_PROTOCOL * pIpConfig2Protocol; + ESL_SERVICE * pService; EFI_STATUS Status; DBG_ENTER ( ); // - // Assume success + // Use break instead of goto // - Status = EFI_SUCCESS; + pIfInfo = NULL; + for ( ; ; ) { + // + // Determine if the IP address is specified + // + pAccess = &pConfigData->AccessPoint; + DEBUG (( DEBUG_BIND, + "UseDefaultAddress: %s\r\n", + pAccess->UseDefaultAddress ? L"TRUE" : L"FALSE" )); + DEBUG (( DEBUG_BIND, + "Requested IP address: %d.%d.%d.%d\r\n", + pAccess->StationAddress.Addr [ 0 ], + pAccess->StationAddress.Addr [ 1 ], + pAccess->StationAddress.Addr [ 2 ], + pAccess->StationAddress.Addr [ 3 ])); + if ( pAccess->UseDefaultAddress + || (( 0 == pAccess->StationAddress.Addr [ 0 ]) + && ( 0 == pAccess->StationAddress.Addr [ 1 ]) + && ( 0 == pAccess->StationAddress.Addr [ 2 ]) + && ( 0 == pAccess->StationAddress.Addr [ 3 ]))) + { + Status = EFI_SUCCESS; + break; + } + + // + // Open the configuration protocol + // + pService = pPort->pService; + Status = gBS->OpenProtocol ( + pService->Controller, + &gEfiIp4Config2ProtocolGuid, + (VOID **)&pIpConfig2Protocol, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR, + "ERROR - IP Configuration Protocol not available, Status: %r\r\n", + Status )); + break; + } - // - // Get the packet from the queue head - // - pPacket = *ppQueueHead; - if ( NULL != pPacket ) { // - // Remove the packet from the queue + // Get the interface information size. // - pNextPacket = pPacket->pNext; - *ppQueueHead = pNextPacket; - if ( NULL == pNextPacket ) { - *ppQueueTail = NULL; + DataSize = 0; + Status = pIpConfig2Protocol->GetData ( + pIpConfig2Protocol, + Ip4Config2DataTypeInterfaceInfo, + &DataSize, + NULL + ); + if ( EFI_BUFFER_TOO_SMALL != Status ) { + DEBUG (( DEBUG_ERROR, + "ERROR - Failed to get the interface information size, Status: %r\r\n", + Status )); + break; } // - // Set the packet as active + // Allocate the interface information buffer // - *ppPacket = pPacket; + pIfInfo = AllocatePool ( DataSize ); + if ( NULL == pIfInfo ) { + DEBUG (( DEBUG_ERROR, + "ERROR - Not enough memory to allocate the interface information buffer!\r\n" )); + Status = EFI_OUT_OF_RESOURCES; + break; + } // - // Start the transmit operation + // Get the interface info. // - pTcp4Protocol = pPort->Context.Tcp4.pProtocol; - pToken->Packet.TxData = &pPacket->Op.Tcp4Tx.TxData; - Status = pTcp4Protocol->Transmit ( pTcp4Protocol, pToken ); + Status = pIpConfig2Protocol->GetData ( + pIpConfig2Protocol, + Ip4Config2DataTypeInterfaceInfo, + &DataSize, + pIfInfo + ); if ( EFI_ERROR ( Status )) { - pSocket = pPort->pSocket; - if ( EFI_SUCCESS == pSocket->TxError ) { - pSocket->TxError = Status; - } + DEBUG (( DEBUG_ERROR, + "ERROR - Failed to return the interface info, Status: %r\r\n", + Status )); + break; + } + + // + // Display the current configuration + // + DEBUG (( DEBUG_BIND, + "Actual adapter IP address: %d.%d.%d.%d\r\n", + pIfInfo->StationAddress.Addr [ 0 ], + pIfInfo->StationAddress.Addr [ 1 ], + pIfInfo->StationAddress.Addr [ 2 ], + pIfInfo->StationAddress.Addr [ 3 ])); + + // + // Assume the port is not configured + // + Status = EFI_SUCCESS; + if (( pAccess->StationAddress.Addr [ 0 ] == pIfInfo->StationAddress.Addr [ 0 ]) + && ( pAccess->StationAddress.Addr [ 1 ] == pIfInfo->StationAddress.Addr [ 1 ]) + && ( pAccess->StationAddress.Addr [ 2 ] == pIfInfo->StationAddress.Addr [ 2 ]) + && ( pAccess->StationAddress.Addr [ 3 ] == pIfInfo->StationAddress.Addr [ 3 ])) { + break; } + + // + // The IP address did not match + // + Status = EFI_NOT_STARTED; + break; } - DBG_EXIT ( ); + // + // Free the buffer if necessary + // + if ( NULL != pIfInfo ) { + FreePool ( pIfInfo ); + } + + // + // Return the IP address status + // + DBG_EXIT_STATUS ( Status ); + return Status; } + + +/** + 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, + (PFN_API_VERIFY_LOCAL_IP_ADDRESS)EslTcp4VerifyLocalIpAddress +};