/** @file Implement the socket support for the socket layer. Socket States: * Bound - pSocket->PortList is not NULL * Listen - AcceptWait event is not NULL Copyright (c) 2011, Intel Corporation All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \section DataStructures Data Structures

                +-------------+   +-------------+   +-------------+   
  Service Lists | ::ESL_SERVICE |-->| ESL_SERVICE |-->| ESL_SERVICE |--> NULL (pNext)
                +-------------+   +-------------+   +-------------+   
                  ^                       | (pPortList)    |
    pUdp4List ^   | pTcp4List             |                |
              |   |                       |                |
          ^   |   |                       |                |
 pIp4List |   |   |                       |                |
        +---------------+                 |                |
        |   ::ESL_LAYER   |   ::mEslLayer     |                |
        +---------------+                 |                |
                  | (pSocketList)         |                |
    Socket List   V                       V                V
                +-------------+   +-------------+   +-------------+   
                | ::ESL_SOCKET  |-->|   ::ESL_PORT  |-->|   ESL_PORT  |--> NULL (pLinkSocket)
                +-------------+   +-------------+   +-------------+   
                  |                       |                |
                  |                       |                V
                  V                       V               NULL
                +-------------+   +-------------+         
                | ESL_SOCKET  |-->|   ESL_PORT  |--> NULL
                +-------------+   +-------------+
                  |    | | | |            |
                  V    | | | |            V
                 NULL  | | | |           NULL
               (pNext) | | | |     (pLinkService)
                       | | | |                                     pRxPacketListHead
                       | | | `-----------------------------------------------.
                       | | |                     pRxOobPacketListHead        |
                       | | `--------------------------------.                |
                       | |      pTxPacketListHead           |                |
                       | `---------------.                  |                |
  pTxOobPacketListHead |                 |                  |                |
                       V                 V                  V                V
                  +------------+    +------------+    +------------+    +------------+
                  | ::ESL_PACKET |    | ESL_PACKET |    | ESL_PACKET |    | ESL_PACKET |
                  +------------+    +------------+    +------------+    +------------+
                         |                 |                |                |
                         V                 V                V                V
                  +------------+    +------------+    +------------+    +------------+
                  | ESL_PACKET |    | ESL_PACKET |    | ESL_PACKET |    | ESL_PACKET |
                  +------------+    +------------+    +------------+    +------------+
                         |                 |                |                |
                         V                 V                V                V
                        NULL              NULL             NULL             NULL
                       (pNext)

  
::mEslLayer is the one and only ::ESL_LAYER structure. It connects directly or indirectly to the other data structures. The ESL_LAYER structure has a unique service list for each of the network protocol interfaces. ::ESL_SERVICE manages the network interfaces for a given transport type (IP4, TCP4, UDP4, etc.) ::ESL_SOCKET manages the activity for a single socket instance. As such, it contains the ::EFI_SOCKET_PROTOCOL structure which the BSD socket library uses as the object reference and the API into the EFI socket library. ::ESL_PORT manages the connection with a single instance of the lower layer network. This structure is the socket equivalent of an IP connection or a TCP or UDP port. ::ESL_PACKET buffers data for transmit and receive. There are four queues connected to the ::ESL_SOCKET that manage the data: The selection of the transmit queue is controlled by the MSG_OOB flag on the transmit request as well as the socket option SO_OOBINLINE. The receive queue is selected by the URGENT data flag for TCP and the setting of the socket option SO_OOBINLINE. Data structure synchronization is done by raising TPL to TPL_SOCKET. Modifying critical elements within the data structures must be done at this TPL. TPL is then restored to the previous level. Note that the code verifies that all callbacks are entering at TPL_SOCKETS for proper data structure synchronization. \section PortCloseStateMachine Port Close State Machine The port close state machine walks the port through the necessary states to stop activity on the port and get it into a state where the resources may be released. The state machine consists of the following arcs and states:

      +--------------------------+
      |          Open            |
      +--------------------------+
                   |
                   |  ::EslSocketPortCloseStart
                   V
      +--------------------------+
      | PORT_STATE_CLOSE_STARTED |
      +--------------------------+
                   |
                   |  ::EslSocketPortCloseTxDone
                   V
      +--------------------------+
      | PORT_STATE_CLOSE_TX_DONE |
      +--------------------------+
                   |
                   |  ::EslSocketPortCloseComplete
                   V
      +--------------------------+
      |  PORT_STATE_CLOSE_DONE   |
      +--------------------------+
                   |
                   |  ::EslSocketPortCloseRxDone
                   V
      +--------------------------+
      | PORT_STATE_CLOSE_RX_DONE |
      +--------------------------+
                   |
                   |  ::EslSocketPortClose
                   V
      +--------------------------+
      |          Closed          |
      +--------------------------+

  
\section ReceiveEngine Receive Engine The receive path accepts data from the network and queues (buffers) it for the application. Flow control is applied once a maximum amount of buffering is reached and is released when the buffer usage drops below that limit. Eventually the application requests data from the socket which removes entries from the queue and returns the data. The receive engine is the state machine which reads data from the network and fills the queue with received packets. The receive engine uses two data structures to manage the network receive opeations and the buffers. At a high level, the ::ESL_IO_MGMT structures are managing the tokens and events for the interface to the UEFI network stack. The ::ESL_PACKET structures are managing the receive data buffers. The receive engine connects these two structures in the network specific receive completion routines.

      +------------------+
      |     ::ESL_PORT     |
      |                  |
      +------------------+
      |    ::ESL_IO_MGMT   |
      +------------------+
      |    ESL_IO_MGMT   |
      +------------------+
      .                  .
      .    ESL_IO_MGMT   .
      .                  .
      +------------------+

The ::ESL_IO_MGMT structures are allocated as part of the ::ESL_PORT structure in ::EslSocketPortAllocate. The ESL_IO_MGMT structures are separated and placed on the free list by calling ::EslSocketIoInit. The ESL_IO_MGMT structure contains the network layer specific receive completion token and event. The receive engine is eventually shutdown by ::EslSocketPortCloseTxDone and the resources in these structures are released in ::EslSocketPortClose by a call to ::EslSocketIoFree.

         pPort->pRxActive
                |
                V
          +-------------+   +-------------+   +-------------+   
  Active  | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL
          +-------------+   +-------------+   +-------------+   

          +-------------+   +-------------+   +-------------+   
  Free    | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL
          +-------------+   +-------------+   +-------------+   
                ^
                |
          pPort->pRxFree
The receive engine is started by calling ::EslSocketRxStart. Flow control pauses the receive engine by stopping the calls to EslSocketRxStart when the amount of receive data waiting for the application meets or exceeds MAX_RX_DATA. After the application reads enough data that the amount of buffering drops below this limit, the calls to EslSockeRxStart continue which releases the flow control. Receive flow control is applied when the port is created, since no receive operation are pending to the low layer network driver. The flow control gets released when the low layer network port is configured or the first receive operation is posted. Flow control remains in the released state until the maximum buffer space is consumed. During this time, ::EslSocketRxComplete calls ::EslSocketRxStart. Flow control is applied in EslSocketRxComplete by skipping the call to EslSocketRxStart. Flow control is eventually released in ::EslSocketReceive when the buffer space drops below the maximum amount causing EslSocketReceive to call EslSocketRxStart.

                    +------------+   +------------+   
    High     .----->| ESL_PACKET |-->| ESL_PACKET |--> NULL (pNext)
  Priority   |      +------------+   +------------+
             |
             | pRxOobPacketListHead
       +------------+
       | ::ESL_SOCKET |
       +------------+
             | pRxPacketListHead
    Low      |
  Priority   |      +------------+   +------------+   +------------+   
             `----->| ::ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL
                    +------------+   +------------+   +------------+   

::EslSocketRxStart connects an ::ESL_PACKET structure to the ::ESL_IO_MGMT structure and then calls the network layer to start the receive operation. Upon receive completion, ::EslSocketRxComplete breaks the connection between these structrues and places the ESL_IO_MGMT structure onto the ESL_PORT::pRxFree list to make token and event available for another receive operation. EslSocketRxComplete then queues the ESL_PACKET structure (data packet) to either the ESL_SOCKET::pRxOobPacketListTail or ESL_SOCKET::pRxPacketListTail depending on whether urgent or normal data was received. Finally ::EslSocketRxComplete attempts to start another receive operation.

  Setup for IP4 and UDP4

      +--------------------+
      | ESL_IO_MGMT        |
      |                    |
      |    +---------------+
      |    | Token         |
      |    |        RxData --> NULL
      +----+---------------+
         |
         V
      +--------------------+
      | ESL_PACKET         |
      |                    |
      |    +---------------+
      |    |       pRxData --> NULL
      +----+---------------+

  Completion for IP4 and UDP4

      +--------------------+   +----------------------+
      | ESL_IO_MGMT        |   |      Data Buffer     |
      |                    |   |     (Driver owned)   |
      |    +---------------+   +----------------------+
      |    | Token         |               ^
      |    |      Rx Event |               |
      |    |               |   +----------------------+
      |    |        RxData --> | EFI_IP4_RECEIVE_DATA |
      +----+---------------+   |    (Driver owned)    |
         |                     +----------------------+
         V                                 ^
      +--------------------+               .
      | ESL_PACKET         |               .
      |                    |               .
      |    +---------------+               .
      |    |       pRxData --> NULL  .......
      +----+---------------+


  Setup and completion for TCP4

      +--------------------+   +--------------------------+
      | ESL_IO_MGMT        |-->| ESL_PACKET               |
      |                    |   |                          |
      |    +---------------+   +----------------------+   |
      |    | Token         |   | EFI_IP4_RECEIVE_DATA |   |
      |    |        RxData --> |                      |   |
      |    |               |   +----------------------+---+
      |    |        Event  |   |       Data Buffer        |
      +----+---------------+   |                          |
                               |                          |
                               +--------------------------+

To minimize the number of buffer copies, the data is not copied until the application makes a receive call. At this point socket performs a single copy in the receive path to move the data from the buffer filled by the network layer into the application's buffer. The IP4 and UDP4 drivers go one step further to reduce buffer copies. They allow the socket layer to hold on to the actual receive buffer until the application has performed a receive operation or closes the socket. Both of theses operations return the buffer to the lower layer network driver by calling ESL_PROTOCOL_API::pfnPacketFree. When a socket application wants to receive data it indirectly calls ::EslSocketReceive to remove data from one of the receive data queues. This routine removes the next available packet from ESL_SOCKET::pRxOobPacketListHead or ESL_SOCKET::pRxPacketListHead and copies the data from the packet into the application's buffer. For SOCK_STREAM sockets, if the packet contains more data then the ESL_PACKET structures remains at the head of the receive queue for the next application receive operation. For SOCK_DGRAM, SOCK_RAW and SOCK_SEQ_PACKET sockets, the ::ESL_PACKET structure is removed from the head of the receive queue and any remaining data is discarded as the packet is placed on the free queue. During socket layer shutdown, ::EslSocketShutdown calls ::EslSocketRxCancel to cancel any pending receive operations. EslSocketRxCancel calls the network specific cancel routine using ESL_PORT::pfnRxCancel. \section TransmitEngine Transmit Engine Application calls to ::EslSocketTransmit cause data to be copied into a buffer. The buffer exists as an extension to an ESL_PACKET structure and the structure is placed at the end of the transmit queue.

     *ppQueueHead: pSocket->pRxPacketListHead or pSocket->pRxOobPacketListHead
          |
          V
        +------------+   +------------+   +------------+   
  Data  | ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL
        +------------+   +------------+   +------------+   
                                                     ^
                                                     |
     *ppQueueTail: pSocket->pRxPacketListTail or pSocket->pRxOobPacketListTail

There are actually two transmit queues the normal or low priority queue which is the default and the urgent or high priority queue which is addressed by specifying the MSG_OOB flag during the transmit request. Associated with each queue is a transmit engine which is responsible for sending the data in that queue. The transmit engine is the state machine which removes entries from the head of the transmit queue and causes the data to be sent over the network.

      +--------------------+   +--------------------+
      | ESL_IO_MGMT        |   | ESL_PACKET         |
      |                    |   |                    |
      |    +---------------+   +----------------+   |
      |    | Token         |   | Buffer Length  |   |
      |    |        TxData --> | Buffer Address |   |
      |    |               |   +----------------+---+
      |    |        Event  |   | Data Buffer        |
      +----+---------------+   |                    |
                               +--------------------+
At a high level, the transmit engine uses a couple of data structures to manage the data flow. The ::ESL_IO_MGMT structures manage the tokens and events for the interface to the UEFI network stack. The ::ESL_PACKET structures manage the data buffers that get sent. The transmit engine connects these two structures prior to transmission and disconnects them upon completion.

         pPort->pTxActive or pTxOobActive
                |
                V
          +-------------+   +-------------+   +-------------+   
  Active  | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL
          +-------------+   +-------------+   +-------------+   

          +-------------+   +-------------+   +-------------+   
  Free    | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL
          +-------------+   +-------------+   +-------------+   
                ^
                |
          pPort->pTxFree or pTxOobFree

The transmit engine manages multiple transmit operations using the active and free lists shown above. ::EslSocketPortAllocate allocates the ::ESL_IO_MGMT structures as an extension to the ::ESL_PORT structure. This routine places the ESL_IO_MGMT structures on the free list by calling ::EslSocketIoInit. During their lifetime, the ESL_IO_MGMT structures will move from the free list to the active list and back again. The active list contains the packets that are actively being processed by the UEFI network stack. Eventually the ESL_IO_MGMT structures will be removed from the free list and be deallocated by the EslSocketPortClose routine. The network specific code calls the ::EslSocketTxStart routine to hand a packet to the network stack. EslSocketTxStart connects the transmit packet (::ESL_PACKET) to an ::ESL_IO_MGMT structure and then queues the result to one of the active lists: ESL_PORT::pTxActive or ESL_PORT::pTxOobActive. The routine then hands the packet to the network stack. Upon completion, the network specific TxComplete routine calls ::EslSocketTxComplete to disconnect the transmit packet from the ESL_IO_MGMT structure and frees the ::ESL_PACKET structure by calling ::EslSocketPacketFree. The routine places the ::ESL_IO_MGMT structure into the free list either ESL_PORT::pTxFree or ESL_PORT::pTxOobFree. EslSocketTxComplete then starts the next transmit operation while the socket is active or calls the ::EslSocketPortCloseTxDone routine when the socket is shutting down. **/ #include "Socket.h" /** Socket driver connection points List the network stack connection points for the socket driver. **/ CONST ESL_SOCKET_BINDING cEslSocketBinding[] = { { L"Ip4", &gEfiIp4ServiceBindingProtocolGuid, &gEfiIp4ProtocolGuid, &mEslIp4ServiceGuid, OFFSET_OF ( ESL_LAYER, pIp4List ), 4, // RX buffers 4, // TX buffers 0 }, // TX Oob buffers { L"Tcp4", &gEfiTcp4ServiceBindingProtocolGuid, &gEfiTcp4ProtocolGuid, &mEslTcp4ServiceGuid, OFFSET_OF ( ESL_LAYER, pTcp4List ), 4, // RX buffers 4, // TX buffers 4 }, // TX Oob buffers { L"Tcp6", &gEfiTcp6ServiceBindingProtocolGuid, &gEfiTcp6ProtocolGuid, &mEslTcp6ServiceGuid, OFFSET_OF ( ESL_LAYER, pTcp6List ), 4, // RX buffers 4, // TX buffers 4 }, // TX Oob buffers { L"Udp4", &gEfiUdp4ServiceBindingProtocolGuid, &gEfiUdp4ProtocolGuid, &mEslUdp4ServiceGuid, OFFSET_OF ( ESL_LAYER, pUdp4List ), 4, // RX buffers 4, // TX buffers 0 }, // TX Oob buffers { L"Udp6", &gEfiUdp6ServiceBindingProtocolGuid, &gEfiUdp6ProtocolGuid, &mEslUdp6ServiceGuid, OFFSET_OF ( ESL_LAYER, pUdp6List ), 4, // RX buffers 4, // TX buffers 0 } // TX Oob buffers }; CONST UINTN cEslSocketBindingEntries = DIM ( cEslSocketBinding ); /** APIs to support the various socket types for the v4 network stack. **/ CONST ESL_PROTOCOL_API * cEslAfInetApi[] = { NULL, // 0 &cEslTcp4Api, // SOCK_STREAM &cEslUdp4Api, // SOCK_DGRAM &cEslIp4Api, // SOCK_RAW NULL, // SOCK_RDM &cEslTcp4Api // SOCK_SEQPACKET }; /** Number of entries in the v4 API array ::cEslAfInetApi. **/ CONST int cEslAfInetApiSize = DIM ( cEslAfInetApi ); /** APIs to support the various socket types for the v6 network stack. **/ CONST ESL_PROTOCOL_API * cEslAfInet6Api[] = { NULL, // 0 &cEslTcp6Api, // SOCK_STREAM &cEslUdp6Api, // SOCK_DGRAM NULL, // SOCK_RAW NULL, // SOCK_RDM &cEslTcp6Api // SOCK_SEQPACKET }; /** Number of entries in the v6 API array ::cEslAfInet6Api. **/ CONST int cEslAfInet6ApiSize = DIM ( cEslAfInet6Api ); /** Global management structure for the socket layer. **/ ESL_LAYER mEslLayer; /** Initialize an endpoint for network communication. This routine initializes the communication endpoint. The ::socket routine calls this routine indirectly to create the communication endpoint. @param [in] pSocketProtocol Address of the socket protocol structure. @param [in] domain Select the family of protocols for the client or server application. See the ::socket documentation for values. @param [in] type Specifies how to make the network connection. See the ::socket documentation for values. @param [in] protocol Specifies the lower layer protocol to use. See the ::socket documentation for values. @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Socket successfully created @retval EFI_INVALID_PARAMETER - Invalid domain value, errno = EAFNOSUPPORT @retval EFI_INVALID_PARAMETER - Invalid type value, errno = EINVAL @retval EFI_INVALID_PARAMETER - Invalid protocol value, errno = EINVAL **/ EFI_STATUS EslSocket ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN int domain, IN int type, IN int protocol, IN int * pErrno ) { CONST ESL_PROTOCOL_API * pApi; CONST ESL_PROTOCOL_API ** ppApiArray; CONST ESL_PROTOCOL_API ** ppApiArrayEnd; int ApiArraySize; ESL_SOCKET * pSocket; EFI_STATUS Status; int errno; DBG_ENTER ( ); // // Locate the socket // pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // // Set the default domain if necessary // if ( AF_UNSPEC == domain ) { domain = AF_INET; } // // Assume success // errno = 0; Status = EFI_SUCCESS; // // Use break instead of goto // for ( ; ; ) { // // Validate the domain value // if (( AF_INET != domain ) && ( AF_INET6 != domain ) && ( AF_LOCAL != domain )) { DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, "ERROR - Invalid domain value\r\n" )); Status = EFI_INVALID_PARAMETER; errno = EAFNOSUPPORT; break; } // // Determine the protocol APIs // ppApiArray = NULL; ApiArraySize = 0; if (( AF_INET == domain ) || ( AF_LOCAL == domain )) { ppApiArray = &cEslAfInetApi[0]; ApiArraySize = cEslAfInetApiSize; } else { ppApiArray = &cEslAfInet6Api[0]; ApiArraySize = cEslAfInet6ApiSize; } // // Set the default type if necessary // if ( 0 == type ) { type = SOCK_STREAM; } // // Validate the type value // if (( type >= ApiArraySize ) || ( NULL == ppApiArray ) || ( NULL == ppApiArray[ type ])) { DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, "ERROR - Invalid type value\r\n" )); // // The socket type is not supported // Status = EFI_INVALID_PARAMETER; errno = EPROTOTYPE; break; } // // Set the default protocol if necessary // pApi = ppApiArray[ type ]; if ( 0 == protocol ) { protocol = pApi->DefaultProtocol; } // // Validate the protocol value // if (( pApi->DefaultProtocol != protocol ) && ( SOCK_RAW != type )) { Status = EFI_INVALID_PARAMETER; // // Assume that the driver supports this protocol // ppApiArray = &cEslAfInetApi[0]; ppApiArrayEnd = &ppApiArray [ cEslAfInetApiSize ]; while ( ppApiArrayEnd > ppApiArray ) { pApi = *ppApiArray; if ( protocol == pApi->DefaultProtocol ) { break; } ppApiArray += 1; } if ( ppApiArrayEnd <= ppApiArray ) { // // Verify against the IPv6 table // ppApiArray = &cEslAfInet6Api[0]; ppApiArrayEnd = &ppApiArray [ cEslAfInet6ApiSize ]; while ( ppApiArrayEnd > ppApiArray ) { pApi = *ppApiArray; if ( protocol == pApi->DefaultProtocol ) { break; } ppApiArray += 1; } } if ( ppApiArrayEnd <= ppApiArray ) { DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, "ERROR - The protocol is not supported!\r\n" )); errno = EPROTONOSUPPORT; break; } // // The driver does not support this protocol // DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, "ERROR - The protocol does not support this socket type!\r\n" )); errno = EPROTONOSUPPORT; errno = EPROTOTYPE; break; } // // Save the socket attributes // pSocket->pApi = pApi; pSocket->Domain = domain; pSocket->Type = type; pSocket->Protocol = protocol; // // Done // break; } // // Return the operation status // if ( NULL != pErrno ) { *pErrno = errno; } DBG_EXIT_STATUS ( Status ); return Status; } /** Accept a network connection. This routine calls the network specific layer to remove the next connection from the FIFO. The ::accept calls this routine to poll for a network connection to the socket. When a connection is available this routine returns the ::EFI_SOCKET_PROTOCOL structure address associated with the new socket and the remote network address if requested. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] pSockAddr Address of a buffer to receive the remote network address. @param [in, out] pSockAddrLength Length in bytes of the address buffer. On output specifies the length of the remote network address. @param [out] ppSocketProtocol Address of a buffer to receive the ::EFI_SOCKET_PROTOCOL instance associated with the new socket. @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS New connection successfully created @retval EFI_NOT_READY No connection is available **/ EFI_STATUS EslSocketAccept ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN struct sockaddr * pSockAddr, IN OUT socklen_t * pSockAddrLength, IN EFI_SOCKET_PROTOCOL ** ppSocketProtocol, IN int * pErrno ) { ESL_SOCKET * pNewSocket; ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; // // Validate the socket // pSocket = NULL; pNewSocket = NULL; if ( NULL != pSocketProtocol ) { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // // Verify the API // if ( NULL == pSocket->pApi->pfnAccept ) { Status = EFI_UNSUPPORTED; pSocket->errno = ENOTSUP; } else { // // Validate the sockaddr // if (( NULL != pSockAddr ) && ( NULL == pSockAddrLength )) { DEBUG (( DEBUG_ACCEPT, "ERROR - pSockAddr is NULL!\r\n" )); Status = EFI_INVALID_PARAMETER; pSocket->errno = EFAULT; } else { // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Verify that the socket is in the listen state // if ( SOCKET_STATE_LISTENING != pSocket->State ) { DEBUG (( DEBUG_ACCEPT, "ERROR - Socket is not listening!\r\n" )); if ( NULL == pSocket->pApi->pfnAccept ) { // // Socket does not support listen // pSocket->errno = EOPNOTSUPP; Status = EFI_UNSUPPORTED; } else { // // Socket supports listen, but not in listen state // pSocket->errno = EINVAL; Status = EFI_NOT_STARTED; } } else { // // Determine if a socket is available // if ( 0 == pSocket->FifoDepth ) { // // No connections available // Determine if any ports are available // if ( NULL == pSocket->pPortList ) { // // No ports available // Status = EFI_DEVICE_ERROR; pSocket->errno = EINVAL; // // Update the socket state // pSocket->State = SOCKET_STATE_NO_PORTS; } else { // // Ports are available // No connection requests at this time // Status = EFI_NOT_READY; pSocket->errno = EAGAIN; } } else { // // Attempt to accept the connection and // get the remote network address // pNewSocket = pSocket->pFifoHead; ASSERT ( NULL != pNewSocket ); Status = pSocket->pApi->pfnAccept ( pNewSocket, pSockAddr, pSockAddrLength ); if ( !EFI_ERROR ( Status )) { // // Remove the new socket from the list // pSocket->pFifoHead = pNewSocket->pNextConnection; if ( NULL == pSocket->pFifoHead ) { pSocket->pFifoTail = NULL; } // // Account for this socket // pSocket->FifoDepth -= 1; // // Update the new socket's state // pNewSocket->State = SOCKET_STATE_CONNECTED; pNewSocket->bConfigured = TRUE; DEBUG (( DEBUG_ACCEPT, "0x%08x: Socket connected\r\n", pNewSocket )); } } } // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); } } } // // Return the new socket // if (( NULL != ppSocketProtocol ) && ( NULL != pNewSocket )) { *ppSocketProtocol = &pNewSocket->SocketProtocol; } // // Return the operation status // if ( NULL != pErrno ) { if ( NULL != pSocket ) { *pErrno = pSocket->errno; } else { Status = EFI_INVALID_PARAMETER; *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); return Status; } /** Allocate and initialize a ESL_SOCKET structure. This support function allocates an ::ESL_SOCKET structure and installs a protocol on ChildHandle. If pChildHandle is a pointer to NULL, then a new handle is created and returned in pChildHandle. If pChildHandle is not a pointer to NULL, then the protocol installs on the existing pChildHandle. @param [in, out] pChildHandle Pointer to the handle of the child to create. If it is NULL, then a new handle is created. If it is a pointer to an existing UEFI handle, then the protocol is added to the existing UEFI handle. @param [in] DebugFlags Flags for debug messages @param [in, out] ppSocket The buffer to receive an ::ESL_SOCKET structure address. @retval EFI_SUCCESS The protocol was added to ChildHandle. @retval EFI_INVALID_PARAMETER ChildHandle is NULL. @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create the child @retval other The child handle was not created **/ EFI_STATUS EFIAPI EslSocketAllocate ( IN OUT EFI_HANDLE * pChildHandle, IN UINTN DebugFlags, IN OUT ESL_SOCKET ** ppSocket ) { UINTN LengthInBytes; ESL_LAYER * pLayer; ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Create a socket structure // LengthInBytes = sizeof ( *pSocket ); pSocket = (ESL_SOCKET *) AllocateZeroPool ( LengthInBytes ); if ( NULL != pSocket ) { DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, "0x%08x: Allocate pSocket, %d bytes\r\n", pSocket, LengthInBytes )); // // Initialize the socket protocol // pSocket->Signature = SOCKET_SIGNATURE; pSocket->SocketProtocol.pfnAccept = EslSocketAccept; pSocket->SocketProtocol.pfnBind = EslSocketBind; pSocket->SocketProtocol.pfnClosePoll = EslSocketClosePoll; pSocket->SocketProtocol.pfnCloseStart = EslSocketCloseStart; pSocket->SocketProtocol.pfnConnect = EslSocketConnect; pSocket->SocketProtocol.pfnGetLocal = EslSocketGetLocalAddress; pSocket->SocketProtocol.pfnGetPeer = EslSocketGetPeerAddress; pSocket->SocketProtocol.pfnListen = EslSocketListen; pSocket->SocketProtocol.pfnOptionGet = EslSocketOptionGet; pSocket->SocketProtocol.pfnOptionSet = EslSocketOptionSet; pSocket->SocketProtocol.pfnPoll = EslSocketPoll; pSocket->SocketProtocol.pfnReceive = EslSocketReceive; pSocket->SocketProtocol.pfnShutdown = EslSocketShutdown; pSocket->SocketProtocol.pfnSocket = EslSocket; pSocket->SocketProtocol.pfnTransmit = EslSocketTransmit; pSocket->MaxRxBuf = MAX_RX_DATA; pSocket->MaxTxBuf = MAX_TX_DATA; // // Install the socket protocol on the specified handle // Status = gBS->InstallMultipleProtocolInterfaces ( pChildHandle, &gEfiSocketProtocolGuid, &pSocket->SocketProtocol, NULL ); if ( !EFI_ERROR ( Status )) { DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, "Installed: gEfiSocketProtocolGuid on 0x%08x\r\n", *pChildHandle )); pSocket->SocketProtocol.SocketHandle = *pChildHandle; // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Add this socket to the list // pLayer = &mEslLayer; pSocket->pNext = pLayer->pSocketList; pLayer->pSocketList = pSocket; // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); // // Return the socket structure address // *ppSocket = pSocket; } else { DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL | DEBUG_INIT, "ERROR - Failed to install gEfiSocketProtocolGuid on 0x%08x, Status: %r\r\n", *pChildHandle, Status )); } // // Release the socket if necessary // if ( EFI_ERROR ( Status )) { gBS->FreePool ( pSocket ); DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, "0x%08x: Free pSocket, %d bytes\r\n", pSocket, sizeof ( *pSocket ))); pSocket = NULL; } } else { Status = EFI_OUT_OF_RESOURCES; } // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Bind a name to a socket. This routine calls the network specific layer to save the network address of the local connection point. The ::bind routine calls this routine to connect a name (network address and port) to a socket on the local machine. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] pSockAddr Address of a sockaddr structure that contains the connection point on the local machine. An IPv4 address 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] SockAddrLength Specifies the length in bytes of the sockaddr structure. @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Socket successfully created **/ EFI_STATUS EslSocketBind ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN CONST struct sockaddr * pSockAddr, IN socklen_t SockAddrLength, OUT int * pErrno ) { EFI_HANDLE ChildHandle; UINT8 * pBuffer; ESL_PORT * pPort; ESL_SERVICE ** ppServiceListHead; ESL_SOCKET * pSocket; ESL_SERVICE * pService; EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding; EFI_STATUS Status; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; // // Validate the socket // pSocket = NULL; if ( NULL != pSocketProtocol ) { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // // Validate the structure pointer // pSocket->errno = 0; if ( NULL == pSockAddr ) { DEBUG (( DEBUG_BIND, "ERROR - pSockAddr is NULL!\r\n" )); Status = EFI_INVALID_PARAMETER; pSocket->errno = EFAULT; } // // Validate the local address length // else if ( SockAddrLength < pSocket->pApi->MinimumAddressLength ) { DEBUG (( DEBUG_BIND, "ERROR - Invalid bind name length: %d\r\n", SockAddrLength )); Status = EFI_INVALID_PARAMETER; pSocket->errno = EINVAL; } // // Validate the shutdown state // else if ( pSocket->bRxDisable || pSocket->bTxDisable ) { DEBUG (( DEBUG_BIND, "ERROR - Shutdown has been called on socket 0x%08x\r\n", pSocket )); pSocket->errno = EINVAL; Status = EFI_INVALID_PARAMETER; } // // Verify the socket state // else if ( SOCKET_STATE_NOT_CONFIGURED != pSocket->State ) { DEBUG (( DEBUG_BIND, "ERROR - The socket 0x%08x is already configured!\r\n", pSocket )); pSocket->errno = EINVAL; Status = EFI_ALREADY_STARTED; } else { // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Assume no ports are available // pSocket->errno = EADDRNOTAVAIL; Status = EFI_INVALID_PARAMETER; // // Walk the list of services // pBuffer = (UINT8 *)&mEslLayer; pBuffer = &pBuffer[ pSocket->pApi->ServiceListOffset ]; ppServiceListHead = (ESL_SERVICE **)pBuffer; pService = *ppServiceListHead; while ( NULL != pService ) { // // Create the port // pServiceBinding = pService->pServiceBinding; ChildHandle = NULL; Status = pServiceBinding->CreateChild ( pServiceBinding, &ChildHandle ); if ( !EFI_ERROR ( Status )) { DEBUG (( DEBUG_BIND | DEBUG_POOL, "0x%08x: %s port handle created\r\n", ChildHandle, pService->pSocketBinding->pName )); // // Open the port // Status = EslSocketPortAllocate ( pSocket, pService, ChildHandle, pSockAddr, TRUE, DEBUG_BIND, &pPort ); } else { DEBUG (( DEBUG_BIND | DEBUG_POOL, "ERROR - Failed to open %s port handle, Status: %r\r\n", pService->pSocketBinding->pName, Status )); } // // Set the next service // pService = pService->pNext; } // // Verify that at least one network connection was found // if ( NULL != pSocket->pPortList ) { Status = EFI_SUCCESS; } else { if ( EADDRNOTAVAIL == pSocket->errno ) { DEBUG (( DEBUG_BIND | DEBUG_POOL | DEBUG_INIT, "ERROR - Socket address is not available!\r\n" )); } if ( EADDRINUSE == pSocket->errno ) { DEBUG (( DEBUG_BIND | DEBUG_POOL | DEBUG_INIT, "ERROR - Socket address is in use!\r\n" )); } Status = EFI_INVALID_PARAMETER; } // // Mark this socket as bound if successful // if ( !EFI_ERROR ( Status )) { pSocket->State = SOCKET_STATE_BOUND; pSocket->errno = 0; } // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); } } // // Return the operation status // if ( NULL != pErrno ) { if ( NULL != pSocket ) { *pErrno = pSocket->errno; } else { Status = EFI_INVALID_PARAMETER; *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); return Status; } /** Test the bind configuration. @param [in] pPort Address of the ::ESL_PORT structure. @param [in] ErrnoValue errno value if test fails @retval EFI_SUCCESS The connection was successfully established. @retval Others The connection attempt failed. **/ EFI_STATUS EslSocketBindTest ( IN ESL_PORT * pPort, IN int ErrnoValue ) { UINT8 * pBuffer; VOID * pConfigData; EFI_STATUS Status; DBG_ENTER ( ); // // Locate the configuration data // pBuffer = (UINT8 *)pPort; pBuffer = &pBuffer [ pPort->pSocket->pApi->ConfigDataOffset ]; pConfigData = (VOID *)pBuffer; // // Attempt to use this configuration // Status = pPort->pfnConfigure ( pPort->pProtocol.v, pConfigData ); if ( EFI_ERROR ( Status )) { DEBUG (( DEBUG_WARN | DEBUG_BIND, "WARNING - Port 0x%08x failed configuration, Status: %r\r\n", pPort, Status )); pPort->pSocket->errno = ErrnoValue; } else { // // Reset the port // Status = pPort->pfnConfigure ( pPort->pProtocol.v, NULL ); if ( EFI_ERROR ( Status )) { DEBUG (( DEBUG_ERROR | DEBUG_BIND, "ERROR - Port 0x%08x failed configuration reset, Status: %r\r\n", pPort, Status )); ASSERT ( EFI_SUCCESS == Status ); } } // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Determine if the socket is closed This routine checks the state of the socket to determine if the network specific layer has completed the close operation. The ::close routine polls this routine to determine when the close operation is complete. The close operation needs to reverse the operations of the ::EslSocketAllocate routine. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS Socket successfully closed @retval EFI_NOT_READY Close still in progress @retval EFI_ALREADY Close operation already in progress @retval Other Failed to close the socket **/ EFI_STATUS EslSocketClosePoll ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN int * pErrno ) { int errno; ESL_LAYER * pLayer; ESL_SOCKET * pNextSocket; ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Assume success // errno = 0; Status = EFI_SUCCESS; // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Locate the socket // pLayer = &mEslLayer; pNextSocket = pLayer->pSocketList; pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); while ( NULL != pNextSocket ) { if ( pNextSocket == pSocket ) { // // Determine if the socket is in the closing state // if ( SOCKET_STATE_CLOSED == pSocket->State ) { // // Walk the list of ports // if ( NULL == pSocket->pPortList ) { // // All the ports are closed // Close the WaitAccept event if necessary // if ( NULL != pSocket->WaitAccept ) { Status = gBS->CloseEvent ( pSocket->WaitAccept ); if ( !EFI_ERROR ( Status )) { DEBUG (( DEBUG_SOCKET | DEBUG_CLOSE | DEBUG_POOL, "0x%08x: Closed WaitAccept event\r\n", pSocket->WaitAccept )); // // Return the transmit status // Status = pSocket->TxError; if ( EFI_ERROR ( Status )) { pSocket->errno = EIO; } } else { DEBUG (( DEBUG_ERROR | DEBUG_SOCKET | DEBUG_CLOSE | DEBUG_POOL, "ERROR - Failed to close the WaitAccept event, Status: %r\r\n", Status )); ASSERT ( EFI_SUCCESS == Status ); } } } else { // // At least one port is still open // Status = EFI_NOT_READY; errno = EAGAIN; } } else { // // SocketCloseStart was not called // Status = EFI_NOT_STARTED; errno = EPERM; } break; } // // Set the next socket // pNextSocket = pNextSocket->pNext; } // // Handle the error case where the socket was already closed // if ( NULL == pSocket ) { // // Socket not found // Status = EFI_NOT_FOUND; errno = ENOTSOCK; } // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); // // Return the operation status // if ( NULL != pErrno ) { *pErrno = errno; } DBG_EXIT_STATUS ( Status ); return Status; } /** Start the close operation on the socket This routine calls the network specific layer to initiate the close state machine. This routine then calls the network specific layer to determine if the close state machine has gone to completion. The result from this poll is returned to the caller. The ::close routine calls this routine to start the close operation which reverses the operations of the ::EslSocketAllocate routine. The close routine then polls the ::EslSocketClosePoll routine to determine when the socket is closed. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] bCloseNow Boolean to control close behavior @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS Socket successfully closed @retval EFI_NOT_READY Close still in progress @retval EFI_ALREADY Close operation already in progress @retval Other Failed to close the socket **/ EFI_STATUS EslSocketCloseStart ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN BOOLEAN bCloseNow, IN int * pErrno ) { int errno; ESL_PORT * pNextPort; ESL_PORT * pPort; ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; errno = 0; // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Determine if the socket is already closed // pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); if ( SOCKET_STATE_CLOSED > pSocket->State ) { // // Update the socket state // pSocket->State = SOCKET_STATE_CLOSED; // // Walk the list of ports // pPort = pSocket->pPortList; while ( NULL != pPort ) { // // Start closing the ports // pNextPort = pPort->pLinkSocket; Status = EslSocketPortCloseStart ( pPort, bCloseNow, DEBUG_CLOSE | DEBUG_LISTEN | DEBUG_CONNECTION ); if (( EFI_SUCCESS != Status ) && ( EFI_NOT_READY != Status )) { errno = EIO; break; } // // Set the next port // pPort = pNextPort; } // // Attempt to finish closing the socket // if ( NULL == pPort ) { Status = EslSocketClosePoll ( pSocketProtocol, &errno ); } } else { Status = EFI_NOT_READY; errno = EAGAIN; } // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); // // Return the operation status // if ( NULL != pErrno ) { *pErrno = errno; } DBG_EXIT_STATUS ( Status ); return Status; } /** Connect to a remote system via the network. This routine calls the network specific layer to establish the remote system address and establish the connection to the remote system. The ::connect routine calls this routine to establish a connection with the specified remote system. This routine is designed to be polled by the connect routine for completion of the network connection. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] pSockAddr Network address of the remote system. @param [in] SockAddrLength Length in bytes of the network address. @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS The connection was successfully established. @retval EFI_NOT_READY The connection is in progress, call this routine again. @retval Others The connection attempt failed. **/ EFI_STATUS EslSocketConnect ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN const struct sockaddr * pSockAddr, IN socklen_t SockAddrLength, IN int * pErrno ) { struct sockaddr_in6 LocalAddress; ESL_PORT * pPort; ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; DEBUG (( DEBUG_CONNECT, "Entering SocketConnect\r\n" )); // // Assume success // Status = EFI_SUCCESS; // // Validate the socket // pSocket = NULL; if ( NULL != pSocketProtocol ) { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // // Validate the name length // if ( SockAddrLength < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data ))) { DEBUG (( DEBUG_CONNECT, "ERROR - Invalid bind name length: %d\r\n", SockAddrLength )); Status = EFI_INVALID_PARAMETER; pSocket->errno = EINVAL; } else { // // Assume success // pSocket->errno = 0; // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Validate the socket state // switch ( pSocket->State ) { default: // // Wrong socket state // pSocket->errno = EIO; Status = EFI_DEVICE_ERROR; break; case SOCKET_STATE_NOT_CONFIGURED: case SOCKET_STATE_BOUND: // // Validate the address length // if ( SockAddrLength >= pSocket->pApi->MinimumAddressLength ) { // // Verify the API // if ( NULL == pSocket->pApi->pfnRemoteAddrSet ) { // // Already connected // pSocket->errno = ENOTSUP; Status = EFI_UNSUPPORTED; } else { // // Determine if BIND was already called // if ( NULL == pSocket->pPortList ) { // // Allow any local port // ZeroMem ( &LocalAddress, sizeof ( LocalAddress )); LocalAddress.sin6_len = (uint8_t)pSocket->pApi->MinimumAddressLength; LocalAddress.sin6_family = pSocket->pApi->AddressFamily; Status = EslSocketBind ( &pSocket->SocketProtocol, (struct sockaddr *)&LocalAddress, LocalAddress.sin6_len, &pSocket->errno ); } if ( NULL != pSocket->pPortList ) { // // Walk the list of ports // pPort = pSocket->pPortList; while ( NULL != pPort ) { // // Set the remote address // Status = pSocket->pApi->pfnRemoteAddrSet ( pPort, pSockAddr, SockAddrLength ); if ( EFI_ERROR ( Status )) { break; } // // Set the next port // pPort = pPort->pLinkSocket; } // // Verify the API // if (( !EFI_ERROR ( Status )) && ( NULL != pSocket->pApi->pfnConnectStart )) { // // Initiate the connection with the remote system // Status = pSocket->pApi->pfnConnectStart ( pSocket ); // // Set the next state if connecting // if ( EFI_NOT_READY == Status ) { pSocket->State = SOCKET_STATE_CONNECTING; } } } } } else { DEBUG (( DEBUG_CONNECT, "ERROR - Invalid address length: %d\r\n", SockAddrLength )); Status = EFI_INVALID_PARAMETER; pSocket->errno = EINVAL; } break; case SOCKET_STATE_CONNECTING: // // Poll the network adapter // EslSocketRxPoll ( pSocket ); // // Poll for connection completion // if ( NULL == pSocket->pApi->pfnConnectPoll ) { // // Already connected // pSocket->errno = EISCONN; Status = EFI_ALREADY_STARTED; } else { Status = pSocket->pApi->pfnConnectPoll ( pSocket ); // // Set the next state if connected // if ( EFI_NOT_READY != Status ) { if ( !EFI_ERROR ( Status )) { pSocket->State = SOCKET_STATE_CONNECTED; // // Start the receive operations // EslSocketRxStart ( pSocket->pPortList ); } else { pSocket->State = SOCKET_STATE_BOUND; } } } break; case SOCKET_STATE_CONNECTED: // // Already connected // pSocket->errno = EISCONN; Status = EFI_ALREADY_STARTED; break; } // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); } } // // Return the operation status // if ( NULL != pErrno ) { if ( NULL != pSocket ) { *pErrno = pSocket->errno; } else { // // Bad socket protocol // DEBUG (( DEBUG_ERROR | DEBUG_CONNECT, "ERROR - pSocketProtocol invalid!\r\n" )); Status = EFI_INVALID_PARAMETER; *pErrno = ENOTSOCK; } } // // Return the operation status // DEBUG (( DEBUG_CONNECT, "Exiting SocketConnect, Status: %r\r\n", Status )); return Status; } /** Copy a fragmented buffer into a destination buffer. This support routine copies a fragmented buffer to the caller specified buffer. This routine is called by ::EslIp4Receive and ::EslUdp4Receive. @param [in] FragmentCount Number of fragments in the table @param [in] pFragmentTable Address of an EFI_IP4_FRAGMENT_DATA structure @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. @return Returns the address of the next free byte in the buffer. **/ UINT8 * EslSocketCopyFragmentedBuffer ( IN UINT32 FragmentCount, IN EFI_IP4_FRAGMENT_DATA * pFragmentTable, IN size_t BufferLength, IN UINT8 * pBuffer, OUT size_t * pDataLength ) { size_t BytesToCopy; UINT32 Fragment; UINT8 * pBufferEnd; UINT8 * pData; DBG_ENTER ( ); // // Validate the IP and UDP structures are identical // ASSERT ( OFFSET_OF ( EFI_IP4_FRAGMENT_DATA, FragmentLength ) == OFFSET_OF ( EFI_UDP4_FRAGMENT_DATA, FragmentLength )); ASSERT ( OFFSET_OF ( EFI_IP4_FRAGMENT_DATA, FragmentBuffer ) == OFFSET_OF ( EFI_UDP4_FRAGMENT_DATA, FragmentBuffer )); // // Copy the received data // Fragment = 0; pBufferEnd = &pBuffer [ BufferLength ]; while (( pBufferEnd > pBuffer ) && ( FragmentCount > Fragment )) { // // Determine the amount of received data // pData = pFragmentTable[Fragment].FragmentBuffer; BytesToCopy = pFragmentTable[Fragment].FragmentLength; if (((size_t)( pBufferEnd - pBuffer )) < BytesToCopy ) { BytesToCopy = pBufferEnd - pBuffer; } // // Move the data into the buffer // DEBUG (( DEBUG_RX, "0x%08x --> 0x%08x: Copy data 0x%08x bytes\r\n", pData, pBuffer, BytesToCopy )); CopyMem ( pBuffer, pData, BytesToCopy ); pBuffer += BytesToCopy; Fragment += 1; } // // Return the data length and the buffer address // *pDataLength = BufferLength - ( pBufferEnd - pBuffer ); DBG_EXIT_HEX ( pBuffer ); return pBuffer; } /** Free the socket. This routine frees the socket structure and handle resources. The ::close routine calls EslServiceFreeProtocol which then calls this routine to free the socket context structure and close the handle. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS The socket resources were returned successfully. **/ EFI_STATUS EslSocketFree ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN int * pErrno ) { EFI_HANDLE ChildHandle; int errno; ESL_LAYER * pLayer; ESL_SOCKET * pSocket; ESL_SOCKET * pSocketPrevious; EFI_STATUS Status; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Assume failure // errno = EIO; pSocket = NULL; Status = EFI_INVALID_PARAMETER; // // Validate the socket // pLayer = &mEslLayer; if ( NULL != pSocketProtocol ) { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Walk the socket list // pSocketPrevious = pLayer->pSocketList; if ( NULL != pSocketPrevious ) { if ( pSocket == pSocketPrevious ) { // // Remove the socket from the head of the list // pLayer->pSocketList = pSocket->pNext; } else { // // Find the socket in the middle of the list // while (( NULL != pSocketPrevious ) && ( pSocket != pSocketPrevious->pNext )) { // // Set the next socket // pSocketPrevious = pSocketPrevious->pNext; } if ( NULL != pSocketPrevious ) { // // Remove the socket from the middle of the list // pSocketPrevious = pSocket->pNext; } } } else { DEBUG (( DEBUG_ERROR | DEBUG_POOL, "ERROR - Socket list is empty!\r\n" )); } // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); // // Determine if the socket was found // if ( NULL != pSocketPrevious ) { pSocket->pNext = NULL; // // Remove the socket protocol // ChildHandle = pSocket->SocketProtocol.SocketHandle; Status = gBS->UninstallMultipleProtocolInterfaces ( ChildHandle, &gEfiSocketProtocolGuid, &pSocket->SocketProtocol, NULL ); if ( !EFI_ERROR ( Status )) { DEBUG (( DEBUG_POOL | DEBUG_INFO, "Removed: gEfiSocketProtocolGuid from 0x%08x\r\n", ChildHandle )); // // Free the socket structure // Status = gBS->FreePool ( pSocket ); if ( !EFI_ERROR ( Status )) { DEBUG (( DEBUG_POOL, "0x%08x: Free pSocket, %d bytes\r\n", pSocket, sizeof ( *pSocket ))); errno = 0; } else { DEBUG (( DEBUG_ERROR | DEBUG_POOL, "ERROR - Failed to free pSocket 0x%08x, Status: %r\r\n", pSocket, Status )); } } else { DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INFO, "ERROR - Failed to remove gEfiSocketProtocolGuid from 0x%08x, Status: %r\r\n", ChildHandle, Status )); } } else { DEBUG (( DEBUG_ERROR | DEBUG_INFO, "ERROR - The socket was not in the socket list!\r\n" )); Status = EFI_NOT_FOUND; } } else { DEBUG (( DEBUG_ERROR, "ERROR - Invalid parameter pSocketProtocol is NULL\r\n" )); } // // Return the errno value if possible // if ( NULL != pErrno ) { *pErrno = errno; } // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Get the local address. This routine calls the network specific layer to get the network address of the local host connection point. The ::getsockname routine calls this routine to obtain the network address associated with the local host connection point. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [out] pAddress Network address to receive the local system address @param [in,out] pAddressLength Length of the local network address structure @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Local address successfully returned **/ EFI_STATUS EslSocketGetLocalAddress ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, OUT struct sockaddr * pAddress, IN OUT socklen_t * pAddressLength, IN int * pErrno ) { socklen_t LengthInBytes; ESL_PORT * pPort; ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; // // Validate the socket // pSocket = NULL; if ( NULL != pSocketProtocol ) { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // // Verify the socket state // EslSocketIsConfigured ( pSocket ); if ( pSocket->bAddressSet ) { // // Verify the address buffer and length address // if (( NULL != pAddress ) && ( NULL != pAddressLength )) { // // Verify the API // if ( NULL == pSocket->pApi->pfnLocalAddrGet ) { Status = EFI_UNSUPPORTED; pSocket->errno = ENOTSUP; } else { // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Verify that there is just a single connection // pPort = pSocket->pPortList; if ( NULL != pPort ) { // // Verify the address length // LengthInBytes = pSocket->pApi->AddressLength; if (( LengthInBytes <= *pAddressLength ) && ( 255 >= LengthInBytes )) { // // Return the local address and address length // ZeroMem ( pAddress, LengthInBytes ); pAddress->sa_len = (uint8_t)LengthInBytes; *pAddressLength = pAddress->sa_len; pSocket->pApi->pfnLocalAddrGet ( pPort, pAddress ); pSocket->errno = 0; Status = EFI_SUCCESS; } else { pSocket->errno = EINVAL; Status = EFI_INVALID_PARAMETER; } } else { pSocket->errno = ENOTCONN; Status = EFI_NOT_STARTED; } // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); } } else { pSocket->errno = EINVAL; Status = EFI_INVALID_PARAMETER; } } else { // // Address not set // Status = EFI_NOT_STARTED; pSocket->errno = EADDRNOTAVAIL; } } // // Return the operation status // if ( NULL != pErrno ) { if ( NULL != pSocket ) { *pErrno = pSocket->errno; } else { Status = EFI_INVALID_PARAMETER; *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); return Status; } /** Get the peer address. This routine calls the network specific layer to get the remote system connection point. The ::getpeername routine calls this routine to obtain the network address of the remote connection point. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [out] pAddress Network address to receive the remote system address @param [in,out] pAddressLength Length of the remote network address structure @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Remote address successfully returned **/ EFI_STATUS EslSocketGetPeerAddress ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, OUT struct sockaddr * pAddress, IN OUT socklen_t * pAddressLength, IN int * pErrno ) { socklen_t LengthInBytes; ESL_PORT * pPort; ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; // // Validate the socket // pSocket = NULL; if ( NULL != pSocketProtocol ) { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // // Verify the socket state // Status = EslSocketIsConfigured ( pSocket ); if ( !EFI_ERROR ( Status )) { // // Verify the API // if ( NULL == pSocket->pApi->pfnRemoteAddrGet ) { Status = EFI_UNSUPPORTED; pSocket->errno = ENOTSUP; } else { // // Verify the address buffer and length address // if (( NULL != pAddress ) && ( NULL != pAddressLength )) { // // Verify the socket state // if ( SOCKET_STATE_CONNECTED == pSocket->State ) { // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Verify that there is just a single connection // pPort = pSocket->pPortList; if (( NULL != pPort ) && ( NULL == pPort->pLinkSocket )) { // // Verify the address length // LengthInBytes = pSocket->pApi->AddressLength; if ( LengthInBytes <= *pAddressLength ) { // // Return the local address // ZeroMem ( pAddress, LengthInBytes ); pAddress->sa_len = (uint8_t)LengthInBytes; *pAddressLength = pAddress->sa_len; pSocket->pApi->pfnRemoteAddrGet ( pPort, pAddress ); pSocket->errno = 0; Status = EFI_SUCCESS; } else { pSocket->errno = EINVAL; Status = EFI_INVALID_PARAMETER; } } else { pSocket->errno = ENOTCONN; Status = EFI_NOT_STARTED; } // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); } else { pSocket->errno = ENOTCONN; Status = EFI_NOT_STARTED; } } else { pSocket->errno = EINVAL; Status = EFI_INVALID_PARAMETER; } } } } // // Return the operation status // if ( NULL != pErrno ) { if ( NULL != pSocket ) { *pErrno = pSocket->errno; } else { Status = EFI_INVALID_PARAMETER; *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); return Status; } /** Free the ESL_IO_MGMT event and structure This support routine walks the free list to close the event in the ESL_IO_MGMT structure and remove the structure from the free list. See the \ref TransmitEngine section. @param [in] pPort Address of an ::ESL_PORT structure @param [in] ppFreeQueue Address of the free queue head @param [in] DebugFlags Flags for debug messages @param [in] pEventName Zero terminated string containing the event name @retval EFI_SUCCESS - The structures were properly initialized **/ EFI_STATUS EslSocketIoFree ( IN ESL_PORT * pPort, IN ESL_IO_MGMT ** ppFreeQueue, IN UINTN DebugFlags, IN CHAR8 * pEventName ) { UINT8 * pBuffer; EFI_EVENT * pEvent; ESL_IO_MGMT * pIo; ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; // // Walk the list of IO structures // pSocket = pPort->pSocket; while ( *ppFreeQueue ) { // // Free the event for this structure // pIo = *ppFreeQueue; pBuffer = (UINT8 *)pIo; pBuffer = &pBuffer[ pSocket->TxTokenEventOffset ]; pEvent = (EFI_EVENT *)pBuffer; Status = gBS->CloseEvent ( *pEvent ); if ( EFI_ERROR ( Status )) { DEBUG (( DEBUG_ERROR | DebugFlags, "ERROR - Failed to close the %a event, Status: %r\r\n", pEventName, Status )); pSocket->errno = ENOMEM; break; } DEBUG (( DebugFlags, "0x%08x: Closed %a event 0x%08x\r\n", pIo, pEventName, *pEvent )); // // Remove this structure from the queue // *ppFreeQueue = pIo->pNext; } // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Initialize the ESL_IO_MGMT structures This support routine initializes the ESL_IO_MGMT structure and places them on to a free list. This routine is called by ::EslSocketPortAllocate routines to prepare the transmit engines. See the \ref TransmitEngine section. @param [in] pPort Address of an ::ESL_PORT structure @param [in, out] ppIo Address containing the first structure address. Upon return this buffer contains the next structure address. @param [in] TokenCount Number of structures to initialize @param [in] ppFreeQueue Address of the free queue head @param [in] DebugFlags Flags for debug messages @param [in] pEventName Zero terminated string containing the event name @param [in] pfnCompletion Completion routine address @retval EFI_SUCCESS - The structures were properly initialized **/ EFI_STATUS EslSocketIoInit ( IN ESL_PORT * pPort, IN ESL_IO_MGMT ** ppIo, IN UINTN TokenCount, IN ESL_IO_MGMT ** ppFreeQueue, IN UINTN DebugFlags, IN CHAR8 * pEventName, IN PFN_API_IO_COMPLETE pfnCompletion ) { ESL_IO_MGMT * pEnd; EFI_EVENT * pEvent; ESL_IO_MGMT * pIo; ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; // // Walk the list of IO structures // pSocket = pPort->pSocket; pIo = *ppIo; pEnd = &pIo [ TokenCount ]; while ( pEnd > pIo ) { // // Initialize the IO structure // pIo->pPort = pPort; pIo->pPacket = NULL; // // Allocate the event for this structure // pEvent = (EFI_EVENT *)&(((UINT8 *)pIo)[ pSocket->TxTokenEventOffset ]); Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_SOCKETS, (EFI_EVENT_NOTIFY)pfnCompletion, pIo, pEvent ); if ( EFI_ERROR ( Status )) { DEBUG (( DEBUG_ERROR | DebugFlags, "ERROR - Failed to create the %a event, Status: %r\r\n", pEventName, Status )); pSocket->errno = ENOMEM; break; } DEBUG (( DebugFlags, "0x%08x: Created %a event 0x%08x\r\n", pIo, pEventName, *pEvent )); // // Add this structure to the queue // pIo->pNext = *ppFreeQueue; *ppFreeQueue = pIo; // // Set the next structure // pIo += 1; } // // Save the next structure // *ppIo = pIo; // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Determine if the socket is configured This support routine is called to determine if the socket if the configuration call was made to the network layer. The following routines call this routine to verify that they may be successful in their operations: @param [in] pSocket Address of an ::ESL_SOCKET structure @retval EFI_SUCCESS - The socket is configured **/ EFI_STATUS EslSocketIsConfigured ( IN ESL_SOCKET * pSocket ) { EFI_STATUS Status; EFI_TPL TplPrevious; // // Assume success // Status = EFI_SUCCESS; // // Verify the socket state // if ( !pSocket->bConfigured ) { DBG_ENTER ( ); // // Verify the API // if ( NULL == pSocket->pApi->pfnIsConfigured ) { Status = EFI_UNSUPPORTED; pSocket->errno = ENOTSUP; } else { // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Determine if the socket is configured // Status = pSocket->pApi->pfnIsConfigured ( pSocket ); // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); // // Set errno if a failure occurs // if ( EFI_ERROR ( Status )) { pSocket->errno = EADDRNOTAVAIL; } } DBG_EXIT_STATUS ( Status ); } // // Return the configuration status // return Status; } /** Establish the known port to listen for network connections. This routine calls into the network protocol layer to establish a handler that is called upon connection completion. The handler is responsible for inserting the connection into the FIFO. The ::listen routine indirectly calls this routine to place the socket into a state that enables connection attempts. Connections are placed in a FIFO that is serviced by the application. The application calls the ::accept (::EslSocketAccept) routine to remove the next connection from the FIFO and get the associated socket and address. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] Backlog Backlog specifies the maximum FIFO depth for the connections waiting for the application to call accept. Connection attempts received while the queue is full are refused. @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Socket successfully created @retval Other - Failed to enable the socket for listen **/ EFI_STATUS EslSocketListen ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN INT32 Backlog, OUT int * pErrno ) { ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_STATUS TempStatus; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; // // Validate the socket // pSocket = NULL; if ( NULL != pSocketProtocol ) { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // // Verify the API // if ( NULL == pSocket->pApi->pfnListen ) { Status = EFI_UNSUPPORTED; pSocket->errno = ENOTSUP; } else { // // Assume success // pSocket->Status = EFI_SUCCESS; pSocket->errno = 0; // // Verify that the bind operation was successful // if ( SOCKET_STATE_BOUND == pSocket->State ) { // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Create the event for SocketAccept completion // Status = gBS->CreateEvent ( 0, TPL_SOCKETS, NULL, NULL, &pSocket->WaitAccept ); if ( !EFI_ERROR ( Status )) { DEBUG (( DEBUG_POOL, "0x%08x: Created WaitAccept event\r\n", pSocket->WaitAccept )); // // Set the maximum FIFO depth // if ( 0 >= Backlog ) { Backlog = MAX_PENDING_CONNECTIONS; } else { if ( SOMAXCONN < Backlog ) { Backlog = SOMAXCONN; } else { pSocket->MaxFifoDepth = Backlog; } } // // Initiate the connection attempt listen // Status = pSocket->pApi->pfnListen ( pSocket ); // // Place the socket in the listen state if successful // if ( !EFI_ERROR ( Status )) { pSocket->State = SOCKET_STATE_LISTENING; pSocket->bListenCalled = TRUE; } else { // // Not waiting for SocketAccept to complete // TempStatus = gBS->CloseEvent ( pSocket->WaitAccept ); if ( !EFI_ERROR ( TempStatus )) { DEBUG (( DEBUG_POOL, "0x%08x: Closed WaitAccept event\r\n", pSocket->WaitAccept )); pSocket->WaitAccept = NULL; } else { DEBUG (( DEBUG_ERROR | DEBUG_POOL, "ERROR - Failed to close WaitAccept event, Status: %r\r\n", TempStatus )); ASSERT ( EFI_SUCCESS == TempStatus ); } } } else { DEBUG (( DEBUG_ERROR | DEBUG_LISTEN, "ERROR - Failed to create the WaitAccept event, Status: %r\r\n", Status )); pSocket->errno = ENOMEM; } // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); } else { DEBUG (( DEBUG_ERROR | DEBUG_LISTEN, "ERROR - Bind operation must be performed first!\r\n" )); pSocket->errno = ( SOCKET_STATE_NOT_CONFIGURED == pSocket->State ) ? EDESTADDRREQ : EINVAL; Status = EFI_NO_MAPPING; } } } // // Return the operation status // if ( NULL != pErrno ) { if ( NULL != pSocket ) { *pErrno = pSocket->errno; } else { Status = EFI_INVALID_PARAMETER; *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); return Status; } /** Get the socket options This routine handles the socket level options and passes the others to the network specific layer. The ::getsockopt routine calls this routine to retrieve the socket options one at a time by name. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] level Option protocol level @param [in] OptionName Name of the option @param [out] pOptionValue Buffer to receive the option value @param [in,out] pOptionLength Length of the buffer in bytes, upon return length of the option value in bytes @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Socket data successfully received **/ EFI_STATUS EslSocketOptionGet ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN int level, IN int OptionName, OUT void * __restrict pOptionValue, IN OUT socklen_t * __restrict pOptionLength, IN int * pErrno ) { int errno; socklen_t LengthInBytes; socklen_t MaxBytes; CONST UINT8 * pOptionData; ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); // // Assume failure // errno = EINVAL; Status = EFI_INVALID_PARAMETER; // // Validate the socket // pSocket = NULL; if ( NULL == pSocketProtocol ) { DEBUG (( DEBUG_OPTION, "ERROR - pSocketProtocol is NULL!\r\n" )); } else if ( NULL == pOptionValue ) { DEBUG (( DEBUG_OPTION, "ERROR - No option buffer specified\r\n" )); } else if ( NULL == pOptionLength ) { DEBUG (( DEBUG_OPTION, "ERROR - Option length not specified!\r\n" )); } else { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); LengthInBytes = 0; MaxBytes = *pOptionLength; pOptionData = NULL; switch ( level ) { default: // // See if the protocol will handle the option // if ( NULL != pSocket->pApi->pfnOptionGet ) { if ( pSocket->pApi->DefaultProtocol == level ) { Status = pSocket->pApi->pfnOptionGet ( pSocket, OptionName, (CONST void ** __restrict)&pOptionData, &LengthInBytes ); errno = pSocket->errno; break; } else { // // Protocol not supported // DEBUG (( DEBUG_OPTION, "ERROR - The socket does not support this protocol!\r\n" )); } } else { // // Protocol level not supported // DEBUG (( DEBUG_OPTION, "ERROR - %a does not support any options!\r\n", pSocket->pApi->pName )); } errno = ENOPROTOOPT; Status = EFI_INVALID_PARAMETER; break; case SOL_SOCKET: switch ( OptionName ) { default: // // Socket option not supported // DEBUG (( DEBUG_INFO | DEBUG_OPTION, "ERROR - Invalid socket option!\r\n" )); errno = EINVAL; Status = EFI_INVALID_PARAMETER; break; case SO_ACCEPTCONN: // // Return the listen flag // pOptionData = (CONST UINT8 *)&pSocket->bListenCalled; LengthInBytes = sizeof ( pSocket->bListenCalled ); break; case SO_DEBUG: // // Return the debug flags // pOptionData = (CONST UINT8 *)&pSocket->bOobInLine; LengthInBytes = sizeof ( pSocket->bOobInLine ); break; case SO_OOBINLINE: // // Return the out-of-band inline flag // pOptionData = (CONST UINT8 *)&pSocket->bOobInLine; LengthInBytes = sizeof ( pSocket->bOobInLine ); break; case SO_RCVTIMEO: // // Return the receive timeout // pOptionData = (CONST UINT8 *)&pSocket->RxTimeout; LengthInBytes = sizeof ( pSocket->RxTimeout ); break; case SO_RCVBUF: // // Return the maximum receive buffer size // pOptionData = (CONST UINT8 *)&pSocket->MaxRxBuf; LengthInBytes = sizeof ( pSocket->MaxRxBuf ); break; case SO_REUSEADDR: // // Return the address reuse flag // pOptionData = (UINT8 *)&pSocket->bReUseAddr; LengthInBytes = sizeof ( pSocket->bReUseAddr ); break; case SO_SNDBUF: // // Return the maximum transmit buffer size // pOptionData = (CONST UINT8 *)&pSocket->MaxTxBuf; LengthInBytes = sizeof ( pSocket->MaxTxBuf ); break; case SO_TYPE: // // Return the socket type // pOptionData = (CONST UINT8 *)&pSocket->Type; LengthInBytes = sizeof ( pSocket->Type ); break; } break; } // // Return the option length // *pOptionLength = LengthInBytes; // // Determine if the option is present // if ( 0 != LengthInBytes ) { // // Silently truncate the value length // if ( LengthInBytes > MaxBytes ) { DEBUG (( DEBUG_OPTION, "INFO - Truncating option from %d to %d bytes\r\n", LengthInBytes, MaxBytes )); LengthInBytes = MaxBytes; } // // Return the value // CopyMem ( pOptionValue, pOptionData, LengthInBytes ); // // Zero fill any remaining space // if ( LengthInBytes < MaxBytes ) { ZeroMem ( &((UINT8 *)pOptionValue)[LengthInBytes], MaxBytes - LengthInBytes ); } errno = 0; Status = EFI_SUCCESS; } } // // Return the operation status // if ( NULL != pErrno ) { *pErrno = errno; } DBG_EXIT_STATUS ( Status ); return Status; } /** Set the socket options This routine handles the socket level options and passes the others to the network specific layer. The ::setsockopt routine calls this routine to adjust the socket options one at a time by name. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] level Option protocol level @param [in] OptionName Name of the option @param [in] pOptionValue Buffer containing the option value @param [in] OptionLength Length of the buffer in bytes @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Option successfully set **/ EFI_STATUS EslSocketOptionSet ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN int level, IN int OptionName, IN CONST void * pOptionValue, IN socklen_t OptionLength, IN int * pErrno ) { BOOLEAN bTrueFalse; int errno; socklen_t LengthInBytes; UINT8 * pOptionData; ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); // // Assume failure // errno = EINVAL; Status = EFI_INVALID_PARAMETER; // // Validate the socket // pSocket = NULL; if ( NULL == pSocketProtocol ) { DEBUG (( DEBUG_OPTION, "ERROR - pSocketProtocol is NULL!\r\n" )); } else if ( NULL == pOptionValue ) { DEBUG (( DEBUG_OPTION, "ERROR - No option buffer specified\r\n" )); } else { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); if ( pSocket->bRxDisable || pSocket->bTxDisable ) { DEBUG (( DEBUG_OPTION, "ERROR - Socket has been shutdown!\r\n" )); } else { LengthInBytes = 0; pOptionData = NULL; switch ( level ) { default: // // See if the protocol will handle the option // if ( NULL != pSocket->pApi->pfnOptionSet ) { if ( pSocket->pApi->DefaultProtocol == level ) { Status = pSocket->pApi->pfnOptionSet ( pSocket, OptionName, pOptionValue, OptionLength ); errno = pSocket->errno; break; } else { // // Protocol not supported // DEBUG (( DEBUG_OPTION, "ERROR - The socket does not support this protocol!\r\n" )); } } else { // // Protocol level not supported // DEBUG (( DEBUG_OPTION, "ERROR - %a does not support any options!\r\n", pSocket->pApi->pName )); } errno = ENOPROTOOPT; Status = EFI_INVALID_PARAMETER; break; case SOL_SOCKET: switch ( OptionName ) { default: // // Option not supported // DEBUG (( DEBUG_OPTION, "ERROR - Sockets does not support this option!\r\n" )); errno = EINVAL; Status = EFI_INVALID_PARAMETER; break; case SO_DEBUG: // // Set the debug flags // pOptionData = (UINT8 *)&pSocket->bOobInLine; LengthInBytes = sizeof ( pSocket->bOobInLine ); break; case SO_OOBINLINE: pOptionData = (UINT8 *)&pSocket->bOobInLine; LengthInBytes = sizeof ( pSocket->bOobInLine ); // // Validate the option length // if ( sizeof ( UINT32 ) == OptionLength ) { // // Restrict the input to TRUE or FALSE // bTrueFalse = TRUE; if ( 0 == *(UINT32 *)pOptionValue ) { bTrueFalse = FALSE; } pOptionValue = &bTrueFalse; } else { // // Force an invalid option length error // OptionLength = LengthInBytes - 1; } break; case SO_RCVTIMEO: // // Return the receive timeout // pOptionData = (UINT8 *)&pSocket->RxTimeout; LengthInBytes = sizeof ( pSocket->RxTimeout ); break; case SO_RCVBUF: // // Return the maximum receive buffer size // pOptionData = (UINT8 *)&pSocket->MaxRxBuf; LengthInBytes = sizeof ( pSocket->MaxRxBuf ); break; case SO_REUSEADDR: // // Return the address reuse flag // pOptionData = (UINT8 *)&pSocket->bReUseAddr; LengthInBytes = sizeof ( pSocket->bReUseAddr ); break; case SO_SNDBUF: // // Send buffer size // // // Return the maximum transmit buffer size // pOptionData = (UINT8 *)&pSocket->MaxTxBuf; LengthInBytes = sizeof ( pSocket->MaxTxBuf ); break; } break; } // // Determine if an option was found // if ( 0 != LengthInBytes ) { // // Validate the option length // if ( LengthInBytes <= OptionLength ) { // // Set the option value // CopyMem ( pOptionData, pOptionValue, LengthInBytes ); errno = 0; Status = EFI_SUCCESS; } else { DEBUG (( DEBUG_OPTION, "ERROR - Buffer to small, %d bytes < %d bytes!\r\n", OptionLength, LengthInBytes )); } } } } // // Return the operation status // if ( NULL != pErrno ) { *pErrno = errno; } DBG_EXIT_STATUS ( Status ); return Status; } /** Allocate a packet for a receive or transmit operation This support routine is called by ::EslSocketRxStart and the network specific TxBuffer routines to get buffer space for the next operation. @param [in] ppPacket Address to receive the ::ESL_PACKET structure @param [in] LengthInBytes Length of the packet structure @param [in] ZeroBytes Length of packet to zero @param [in] DebugFlags Flags for debug messages @retval EFI_SUCCESS - The packet was allocated successfully **/ EFI_STATUS EslSocketPacketAllocate ( IN ESL_PACKET ** ppPacket, IN size_t LengthInBytes, IN size_t ZeroBytes, IN UINTN DebugFlags ) { ESL_PACKET * pPacket; EFI_STATUS Status; DBG_ENTER ( ); // // Allocate a packet structure // LengthInBytes += sizeof ( *pPacket ) - sizeof ( pPacket->Op ); Status = gBS->AllocatePool ( EfiRuntimeServicesData, LengthInBytes, (VOID **)&pPacket ); if ( !EFI_ERROR ( Status )) { DEBUG (( DebugFlags | DEBUG_POOL, "0x%08x: Allocate pPacket, %d bytes\r\n", pPacket, LengthInBytes )); if ( 0 != ZeroBytes ) { ZeroMem ( &pPacket->Op, ZeroBytes ); } pPacket->PacketSize = LengthInBytes; } else { DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INFO, "ERROR - Packet allocation failed for %d bytes, Status: %r\r\n", LengthInBytes, Status )); pPacket = NULL; } // // Return the packet // *ppPacket = pPacket; // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Free a packet used for receive or transmit operation This support routine is called by the network specific Close and TxComplete routines and during error cases in RxComplete and TxBuffer. Note that the network layers typically place receive packets on the ESL_SOCKET::pRxFree list for reuse. @param [in] pPacket Address of an ::ESL_PACKET structure @param [in] DebugFlags Flags for debug messages @retval EFI_SUCCESS - The packet was allocated successfully **/ EFI_STATUS EslSocketPacketFree ( IN ESL_PACKET * pPacket, IN UINTN DebugFlags ) { UINTN LengthInBytes; EFI_STATUS Status; DBG_ENTER ( ); // // Free a packet structure // LengthInBytes = pPacket->PacketSize; Status = gBS->FreePool ( pPacket ); if ( !EFI_ERROR ( Status )) { DEBUG (( DebugFlags | DEBUG_POOL, "0x%08x: Free pPacket, %d bytes\r\n", pPacket, LengthInBytes )); } else { DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INFO, "ERROR - Failed to free packet 0x%08x, Status: %r\r\n", pPacket, Status )); } // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Poll a socket for pending activity. This routine builds a detected event mask which is returned to the caller in the buffer provided. The ::poll routine calls this routine to determine if the socket needs to be serviced as a result of connection, error, receive or transmit activity. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] Events Events of interest for this socket @param [in] pEvents Address to receive the detected events @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Socket successfully polled @retval EFI_INVALID_PARAMETER - When pEvents is NULL **/ EFI_STATUS EslSocketPoll ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN short Events, IN short * pEvents, IN int * pErrno ) { short DetectedEvents; ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; short ValidEvents; DEBUG (( DEBUG_POLL, "Entering SocketPoll\r\n" )); // // Assume success // Status = EFI_SUCCESS; DetectedEvents = 0; pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); pSocket->errno = 0; // // Verify the socket state // Status = EslSocketIsConfigured ( pSocket ); if ( !EFI_ERROR ( Status )) { // // Check for invalid events // ValidEvents = POLLIN | POLLPRI | POLLOUT | POLLWRNORM | POLLERR | POLLHUP | POLLNVAL | POLLRDNORM | POLLRDBAND | POLLWRBAND ; if ( 0 != ( Events & ( ~ValidEvents ))) { DetectedEvents |= POLLNVAL; DEBUG (( DEBUG_INFO | DEBUG_POLL, "ERROR - Invalid event mask, Valid Events: 0x%04x, Invalid Events: 0x%04x\r\n", Events & ValidEvents, Events & ( ~ValidEvents ))); } else { // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Increase the network performance by extending the // polling (idle) loop down into the LAN driver // EslSocketRxPoll ( pSocket ); // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); // // Check for pending connections // if ( 0 != pSocket->FifoDepth ) { // // A connection is waiting for an accept call // See posix connect documentation at // http://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.htm // DetectedEvents |= POLLIN | POLLRDNORM; } if ( pSocket->bConnected ) { // // A connection is present // See posix connect documentation at // http://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.htm // DetectedEvents |= POLLOUT | POLLWRNORM; } // // The following bits are set based upon the POSIX poll documentation at // http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html // // // Check for urgent receive data // if ( 0 < pSocket->RxOobBytes ) { DetectedEvents |= POLLRDBAND | POLLPRI | POLLIN; } // // Check for normal receive data // if (( 0 < pSocket->RxBytes ) || ( EFI_SUCCESS != pSocket->RxError )) { DetectedEvents |= POLLRDNORM | POLLIN; } // // Handle the receive errors // if (( EFI_SUCCESS != pSocket->RxError ) && ( 0 == ( DetectedEvents & POLLIN ))) { DetectedEvents |= POLLERR | POLLIN | POLLRDNORM | POLLRDBAND; } // // Check for urgent transmit data buffer space // if (( MAX_TX_DATA > pSocket->TxOobBytes ) || ( EFI_SUCCESS != pSocket->TxError )) { DetectedEvents |= POLLWRBAND; } // // Check for normal transmit data buffer space // if (( MAX_TX_DATA > pSocket->TxBytes ) || ( EFI_SUCCESS != pSocket->TxError )) { DetectedEvents |= POLLWRNORM; } // // Handle the transmit error // if ( EFI_ERROR ( pSocket->TxError )) { DetectedEvents |= POLLERR; } } } // // Return the detected events // *pEvents = DetectedEvents & ( Events | POLLERR | POLLHUP | POLLNVAL ); // // Return the operation status // DEBUG (( DEBUG_POLL, "Exiting SocketPoll, Status: %r\r\n", Status )); return Status; } /** Allocate and initialize a ESL_PORT structure. This routine initializes an ::ESL_PORT structure for use by the socket. This routine calls a routine via ESL_PROTOCOL_API::pfnPortAllocate to initialize the network specific resources. The resources are released later by the \ref PortCloseStateMachine. This support routine is called by: to connect the socket with the underlying network adapter to the socket. @param [in] pSocket Address of an ::ESL_SOCKET structure. @param [in] pService Address of an ::ESL_SERVICE structure. @param [in] ChildHandle Network protocol child handle @param [in] pSockAddr Address of a sockaddr structure that contains the connection point on the local machine. An IPv4 address of INADDR_ANY specifies that the connection is made to all of the network stacks on the platform. Specifying a specific IPv4 address restricts the connection to the network stack supporting that address. Specifying zero for the port causes the network layer to assign a port number from the dynamic range. Specifying a specific port number causes the network layer to use that port. @param [in] bBindTest TRUE if EslSocketBindTest should be called @param [in] DebugFlags Flags for debug messages @param [out] ppPort Buffer to receive new ::ESL_PORT structure address @retval EFI_SUCCESS - Socket successfully created **/ EFI_STATUS EslSocketPortAllocate ( IN ESL_SOCKET * pSocket, IN ESL_SERVICE * pService, IN EFI_HANDLE ChildHandle, IN CONST struct sockaddr * pSockAddr, IN BOOLEAN bBindTest, IN UINTN DebugFlags, OUT ESL_PORT ** ppPort ) { UINTN LengthInBytes; UINT8 * pBuffer; ESL_IO_MGMT * pIo; ESL_LAYER * pLayer; ESL_PORT * pPort; EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding; CONST ESL_SOCKET_BINDING * pSocketBinding; EFI_STATUS Status; EFI_STATUS TempStatus; DBG_ENTER ( ); // // Verify the socket layer synchronization // VERIFY_TPL ( TPL_SOCKETS ); // // Use for/break instead of goto pSocketBinding = pService->pSocketBinding; for ( ; ; ) { // // Allocate a port structure // pLayer = &mEslLayer; LengthInBytes = sizeof ( *pPort ) + ESL_STRUCTURE_ALIGNMENT_BYTES + (( pSocketBinding->RxIo + pSocketBinding->TxIoNormal + pSocketBinding->TxIoUrgent ) * sizeof ( ESL_IO_MGMT )); pPort = (ESL_PORT *) AllocateZeroPool ( LengthInBytes ); if ( NULL == pPort ) { Status = EFI_OUT_OF_RESOURCES; pSocket->errno = ENOMEM; break; } DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, "0x%08x: Allocate pPort, %d bytes\r\n", pPort, LengthInBytes )); // // Initialize the port // pPort->DebugFlags = DebugFlags; pPort->Handle = ChildHandle; pPort->pService = pService; pPort->pServiceBinding = pService->pServiceBinding; pPort->pSocket = pSocket; pPort->pSocketBinding = pService->pSocketBinding; pPort->Signature = PORT_SIGNATURE; // // Open the port protocol // Status = gBS->OpenProtocol ( pPort->Handle, pSocketBinding->pNetworkProtocolGuid, &pPort->pProtocol.v, pLayer->ImageHandle, NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL ); if ( EFI_ERROR ( Status )) { DEBUG (( DEBUG_ERROR | DebugFlags, "ERROR - Failed to open network protocol GUID on controller 0x%08x\r\n", pPort->Handle )); pSocket->errno = EEXIST; break; } DEBUG (( DebugFlags, "0x%08x: Network protocol GUID opened on controller 0x%08x\r\n", pPort->pProtocol.v, pPort->Handle )); // // Initialize the port specific resources // Status = pSocket->pApi->pfnPortAllocate ( pPort, DebugFlags ); if ( EFI_ERROR ( Status )) { break; } // // Set the local address // Status = pSocket->pApi->pfnLocalAddrSet ( pPort, pSockAddr, bBindTest ); if ( EFI_ERROR ( Status )) { break; } // // Test the address/port configuration // if ( bBindTest ) { Status = EslSocketBindTest ( pPort, pSocket->pApi->BindTestErrno ); if ( EFI_ERROR ( Status )) { break; } } // // Initialize the receive structures // pBuffer = (UINT8 *)&pPort[ 1 ]; pBuffer = &pBuffer[ ESL_STRUCTURE_ALIGNMENT_BYTES ]; pBuffer = (UINT8 *)( ESL_STRUCTURE_ALIGNMENT_MASK & (UINTN)pBuffer ); pIo = (ESL_IO_MGMT *)pBuffer; if (( 0 != pSocketBinding->RxIo ) && ( NULL != pSocket->pApi->pfnRxComplete )) { Status = EslSocketIoInit ( pPort, &pIo, pSocketBinding->RxIo, &pPort->pRxFree, DebugFlags | DEBUG_POOL, "receive", pSocket->pApi->pfnRxComplete ); if ( EFI_ERROR ( Status )) { break; } } // // Initialize the urgent transmit structures // if (( 0 != pSocketBinding->TxIoUrgent ) && ( NULL != pSocket->pApi->pfnTxOobComplete )) { Status = EslSocketIoInit ( pPort, &pIo, pSocketBinding->TxIoUrgent, &pPort->pTxOobFree, DebugFlags | DEBUG_POOL, "urgent transmit", pSocket->pApi->pfnTxOobComplete ); if ( EFI_ERROR ( Status )) { break; } } // // Initialize the normal transmit structures // if (( 0 != pSocketBinding->TxIoNormal ) && ( NULL != pSocket->pApi->pfnTxComplete )) { Status = EslSocketIoInit ( pPort, &pIo, pSocketBinding->TxIoNormal, &pPort->pTxFree, DebugFlags | DEBUG_POOL, "normal transmit", pSocket->pApi->pfnTxComplete ); if ( EFI_ERROR ( Status )) { break; } } // // Add this port to the socket // pPort->pLinkSocket = pSocket->pPortList; pSocket->pPortList = pPort; DEBUG (( DebugFlags, "0x%08x: Socket adding port: 0x%08x\r\n", pSocket, pPort )); // // Add this port to the service // pPort->pLinkService = pService->pPortList; pService->pPortList = pPort; // // Return the port // *ppPort = pPort; break; } // // Clean up after the error if necessary // if ( EFI_ERROR ( Status )) { if ( NULL != pPort ) { // // Close the port // EslSocketPortClose ( pPort ); } else { // // Close the port if necessary // pServiceBinding = pService->pServiceBinding; TempStatus = pServiceBinding->DestroyChild ( pServiceBinding, ChildHandle ); if ( !EFI_ERROR ( TempStatus )) { DEBUG (( DEBUG_BIND | DEBUG_POOL, "0x%08x: %s port handle destroyed\r\n", ChildHandle, pSocketBinding->pName )); } else { DEBUG (( DEBUG_ERROR | DEBUG_BIND | DEBUG_POOL, "ERROR - Failed to destroy the %s port handle 0x%08x, Status: %r\r\n", pSocketBinding->pName, ChildHandle, TempStatus )); ASSERT ( EFI_SUCCESS == TempStatus ); } } } // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Close a port. This routine releases the resources allocated by ::EslSocketPortAllocate. This routine calls ESL_PROTOCOL_API::pfnPortClose to release the network specific resources. This routine is called by: See the \ref PortCloseStateMachine section. @param [in] pPort Address of an ::ESL_PORT structure. @retval EFI_SUCCESS The port is closed @retval other Port close error **/ EFI_STATUS EslSocketPortClose ( IN ESL_PORT * pPort ) { UINTN DebugFlags; ESL_LAYER * pLayer; ESL_PACKET * pPacket; ESL_PORT * pPreviousPort; ESL_SERVICE * pService; EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding; CONST ESL_SOCKET_BINDING * pSocketBinding; ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); // // Verify the socket layer synchronization // VERIFY_TPL ( TPL_SOCKETS ); // // Locate the port in the socket list // Status = EFI_SUCCESS; pLayer = &mEslLayer; DebugFlags = pPort->DebugFlags; pSocket = pPort->pSocket; pPreviousPort = pSocket->pPortList; if ( pPreviousPort == pPort ) { // // Remove this port from the head of the socket list // pSocket->pPortList = pPort->pLinkSocket; } else { // // Locate the port in the middle of the socket list // while (( NULL != pPreviousPort ) && ( pPreviousPort->pLinkSocket != pPort )) { pPreviousPort = pPreviousPort->pLinkSocket; } if ( NULL != pPreviousPort ) { // // Remove the port from the middle of the socket list // pPreviousPort->pLinkSocket = pPort->pLinkSocket; } } // // Locate the port in the service list // Note that the port may not be in the service list // if the service has been shutdown. // pService = pPort->pService; if ( NULL != pService ) { pPreviousPort = pService->pPortList; if ( pPreviousPort == pPort ) { // // Remove this port from the head of the service list // pService->pPortList = pPort->pLinkService; } else { // // Locate the port in the middle of the service list // while (( NULL != pPreviousPort ) && ( pPreviousPort->pLinkService != pPort )) { pPreviousPort = pPreviousPort->pLinkService; } if ( NULL != pPreviousPort ) { // // Remove the port from the middle of the service list // pPreviousPort->pLinkService = pPort->pLinkService; } } } // // Empty the urgent receive queue // while ( NULL != pSocket->pRxOobPacketListHead ) { pPacket = pSocket->pRxOobPacketListHead; pSocket->pRxOobPacketListHead = pPacket->pNext; pSocket->pApi->pfnPacketFree ( pPacket, &pSocket->RxOobBytes ); EslSocketPacketFree ( pPacket, DEBUG_RX ); } pSocket->pRxOobPacketListTail = NULL; ASSERT ( 0 == pSocket->RxOobBytes ); // // Empty the receive queue // while ( NULL != pSocket->pRxPacketListHead ) { pPacket = pSocket->pRxPacketListHead; pSocket->pRxPacketListHead = pPacket->pNext; pSocket->pApi->pfnPacketFree ( pPacket, &pSocket->RxBytes ); EslSocketPacketFree ( pPacket, DEBUG_RX ); } pSocket->pRxPacketListTail = NULL; ASSERT ( 0 == pSocket->RxBytes ); // // Empty the receive free queue // while ( NULL != pSocket->pRxFree ) { pPacket = pSocket->pRxFree; pSocket->pRxFree = pPacket->pNext; EslSocketPacketFree ( pPacket, DEBUG_RX ); } // // Release the network specific resources // if ( NULL != pSocket->pApi->pfnPortClose ) { Status = pSocket->pApi->pfnPortClose ( pPort ); } // // Done with the normal transmit events // Status = EslSocketIoFree ( pPort, &pPort->pTxFree, DebugFlags | DEBUG_POOL, "normal transmit" ); // // Done with the urgent transmit events // Status = EslSocketIoFree ( pPort, &pPort->pTxOobFree, DebugFlags | DEBUG_POOL, "urgent transmit" ); // // Done with the receive events // Status = EslSocketIoFree ( pPort, &pPort->pRxFree, DebugFlags | DEBUG_POOL, "receive" ); // // Done with the lower layer network protocol // pSocketBinding = pPort->pSocketBinding; if ( NULL != pPort->pProtocol.v ) { Status = gBS->CloseProtocol ( pPort->Handle, pSocketBinding->pNetworkProtocolGuid, pLayer->ImageHandle, NULL ); if ( !EFI_ERROR ( Status )) { DEBUG (( DebugFlags, "0x%08x: Network protocol GUID closed on controller 0x%08x\r\n", pPort->pProtocol.v, pPort->Handle )); } else { DEBUG (( DEBUG_ERROR | DebugFlags, "ERROR - Failed to close network protocol GUID on controller 0x%08x, Status: %r\r\n", pPort->Handle, Status )); ASSERT ( EFI_SUCCESS == Status ); } } // // Done with the network port // pServiceBinding = pPort->pServiceBinding; if ( NULL != pPort->Handle ) { Status = pServiceBinding->DestroyChild ( pServiceBinding, pPort->Handle ); if ( !EFI_ERROR ( Status )) { DEBUG (( DebugFlags | DEBUG_POOL, "0x%08x: %s port handle destroyed\r\n", pPort->Handle, pSocketBinding->pName )); } else { DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL, "ERROR - Failed to destroy the %s port handle, Status: %r\r\n", pSocketBinding->pName, Status )); ASSERT ( EFI_SUCCESS == Status ); } } // // Release the port structure // Status = gBS->FreePool ( pPort ); if ( !EFI_ERROR ( Status )) { DEBUG (( DebugFlags | DEBUG_POOL, "0x%08x: Free pPort, %d bytes\r\n", pPort, sizeof ( *pPort ))); } else { DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL, "ERROR - Failed to free pPort: 0x%08x, Status: %r\r\n", pPort, Status )); ASSERT ( EFI_SUCCESS == Status ); } // // Mark the socket as closed if necessary // if ( NULL == pSocket->pPortList ) { pSocket->State = SOCKET_STATE_CLOSED; DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Socket State: SOCKET_STATE_CLOSED\r\n", pSocket )); } // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Port close state 3 This routine attempts to complete the port close operation. This routine is called by the TCP layer upon completion of the close operation and by ::EslSocketPortCloseTxDone. See the \ref PortCloseStateMachine section. @param [in] Event The close completion event @param [in] pPort Address of an ::ESL_PORT structure. **/ VOID EslSocketPortCloseComplete ( IN EFI_EVENT Event, IN ESL_PORT * pPort ) { ESL_IO_MGMT * pIo; EFI_STATUS Status; DBG_ENTER ( ); VERIFY_AT_TPL ( TPL_SOCKETS ); // // Update the port state // pPort->State = PORT_STATE_CLOSE_DONE; DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Port Close State: PORT_STATE_CLOSE_DONE\r\n", pPort )); // // Shutdown the receive operation on the port // if ( NULL != pPort->pfnRxCancel ) { pIo = pPort->pRxActive; while ( NULL != pIo ) { EslSocketRxCancel ( pPort, pIo ); pIo = pIo->pNext; } } // // Determine if the receive operation is pending // Status = EslSocketPortCloseRxDone ( pPort ); DBG_EXIT_STATUS ( Status ); } /** Port close state 4 This routine determines the state of the receive operations and continues the close operation after the pending receive operations are cancelled. This routine is called by to determine the state of the receive operations. See the \ref PortCloseStateMachine section. @param [in] pPort Address of an ::ESL_PORT structure. @retval EFI_SUCCESS The port is closed @retval EFI_NOT_READY The port is still closing @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, most likely the routine was called already. **/ EFI_STATUS EslSocketPortCloseRxDone ( IN ESL_PORT * pPort ) { EFI_STATUS Status; DBG_ENTER ( ); // // Verify the socket layer synchronization // VERIFY_TPL ( TPL_SOCKETS ); // // Verify that the port is closing // Status = EFI_ALREADY_STARTED; if ( PORT_STATE_CLOSE_DONE == pPort->State ) { // // Determine if the receive operation is pending // Status = EFI_NOT_READY; if ( NULL == pPort->pRxActive ) { // // The receive operation is complete // Update the port state // pPort->State = PORT_STATE_CLOSE_RX_DONE; DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Port Close State: PORT_STATE_CLOSE_RX_DONE\r\n", pPort )); // // Complete the port close operation // Status = EslSocketPortClose ( pPort ); } else { DEBUG_CODE_BEGIN (); { ESL_IO_MGMT * pIo; // // Display the outstanding receive operations // DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Port Close: Receive still pending!\r\n", pPort )); pIo = pPort->pRxActive; while ( NULL != pIo ) { DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Packet pending on network adapter\r\n", pIo->pPacket )); pIo = pIo->pNext; } } DEBUG_CODE_END ( ); } } // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Start the close operation on a port, state 1. This routine marks the port as closed and initiates the \ref PortCloseStateMachine. The first step is to allow the \ref TransmitEngine to run down. This routine is called by ::EslSocketCloseStart to initiate the socket network specific close operation on the socket. @param [in] pPort Address of an ::ESL_PORT structure. @param [in] bCloseNow Set TRUE to abort active transfers @param [in] DebugFlags Flags for debug messages @retval EFI_SUCCESS The port is closed, not normally returned @retval EFI_NOT_READY The port has started the closing process @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, most likely the routine was called already. **/ EFI_STATUS EslSocketPortCloseStart ( IN ESL_PORT * pPort, IN BOOLEAN bCloseNow, IN UINTN DebugFlags ) { ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); // // Verify the socket layer synchronization // VERIFY_TPL ( TPL_SOCKETS ); // // Mark the port as closing // Status = EFI_ALREADY_STARTED; pSocket = pPort->pSocket; pSocket->errno = EALREADY; if ( PORT_STATE_CLOSE_STARTED > pPort->State ) { // // Update the port state // pPort->State = PORT_STATE_CLOSE_STARTED; DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Port Close State: PORT_STATE_CLOSE_STARTED\r\n", pPort )); pPort->bCloseNow = bCloseNow; pPort->DebugFlags = DebugFlags; // // Determine if transmits are complete // Status = EslSocketPortCloseTxDone ( pPort ); } // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Port close state 2 This routine determines the state of the transmit engine and continue the close operation after the transmission is complete. The next step is to stop the \ref ReceiveEngine. See the \ref PortCloseStateMachine section. This routine is called by ::EslSocketPortCloseStart to determine if the transmission is complete. @param [in] pPort Address of an ::ESL_PORT structure. @retval EFI_SUCCESS The port is closed, not normally returned @retval EFI_NOT_READY The port is still closing @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, most likely the routine was called already. **/ EFI_STATUS EslSocketPortCloseTxDone ( IN ESL_PORT * pPort ) { ESL_IO_MGMT * pIo; ESL_SOCKET * pSocket; 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 ) || (( NULL == pPort->pTxActive ) && ( NULL == pPort->pTxOobActive ))) { // // 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 )); // // Close the port // Skip the close operation if the port is not configured // Status = EFI_SUCCESS; pSocket = pPort->pSocket; if (( pPort->bConfigured ) && ( NULL != pSocket->pApi->pfnPortCloseOp )) { // // Start the close operation // Status = pSocket->pApi->pfnPortCloseOp ( pPort ); DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Port Close: Close operation still pending!\r\n", pPort )); ASSERT ( EFI_SUCCESS == Status ); } else { // // The receive operation is complete // Update the port state // EslSocketPortCloseComplete ( NULL, pPort ); } } else { // // Transmissions are still active, exit // Status = EFI_NOT_READY; pSocket->errno = EAGAIN; DEBUG_CODE_BEGIN ( ); { ESL_PACKET * pPacket; DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Port Close: Transmits are still pending!\r\n", pPort )); // // Display the pending urgent transmit packets // pPacket = pSocket->pTxOobPacketListHead; while ( NULL != pPacket ) { DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Packet pending on urgent TX list, %d bytes\r\n", pPacket, pPacket->PacketSize )); pPacket = pPacket->pNext; } pIo = pPort->pTxOobActive; while ( NULL != pIo ) { pPacket = pIo->pPacket; DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Packet active %d bytes, pIo: 0x%08x\r\n", pPacket, pPacket->PacketSize, pIo )); pIo = pIo->pNext; } // // Display the pending normal transmit packets // pPacket = pSocket->pTxPacketListHead; while ( NULL != pPacket ) { DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Packet pending on normal TX list, %d bytes\r\n", pPacket, pPacket->PacketSize )); pPacket = pPacket->pNext; } pIo = pPort->pTxActive; while ( NULL != pIo ) { pPacket = pIo->pPacket; DEBUG (( DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Packet active %d bytes, pIo: 0x%08x\r\n", pPacket, pPacket->PacketSize, pIo )); pIo = pIo->pNext; } } DEBUG_CODE_END (); } } // // Return the operation status // DBG_EXIT_STATUS ( Status ); return Status; } /** Receive data from a network connection. This routine calls the network specific routine to remove the next portion of data from the receive queue and return it to the caller. The ::recvfrom routine calls this routine to determine if any data is received from the remote system. Note that the other routines ::recv and ::read are layered on top of ::recvfrom. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] Flags Message control flags @param [in] BufferLength Length of the the buffer @param [in] pBuffer Address of a buffer to receive the data. @param [in] pDataLength Number of received data bytes in the buffer. @param [out] pAddress Network address to receive the remote system address @param [in,out] pAddressLength Length of the remote network address structure @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Socket data successfully received **/ EFI_STATUS EslSocketReceive ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN INT32 Flags, IN size_t BufferLength, IN UINT8 * pBuffer, OUT size_t * pDataLength, OUT struct sockaddr * pAddress, IN OUT socklen_t * pAddressLength, IN int * pErrno ) { union { struct sockaddr_in v4; struct sockaddr_in6 v6; } Addr; socklen_t AddressLength; BOOLEAN bConsumePacket; BOOLEAN bUrgentQueue; size_t DataLength; ESL_PACKET * pNextPacket; ESL_PACKET * pPacket; ESL_PORT * pPort; ESL_PACKET ** ppQueueHead; ESL_PACKET ** ppQueueTail; struct sockaddr * pRemoteAddress; size_t * pRxDataBytes; ESL_SOCKET * pSocket; size_t SkipBytes; EFI_STATUS Status; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; // // Validate the socket // pSocket = NULL; if ( NULL != pSocketProtocol ) { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // // Validate the return address parameters // if (( NULL == pAddress ) || ( NULL != pAddressLength )) { // // Return the transmit error if necessary // if ( EFI_SUCCESS != pSocket->TxError ) { pSocket->errno = EIO; Status = pSocket->TxError; pSocket->TxError = EFI_SUCCESS; } else { // // Verify the socket state // Status = EslSocketIsConfigured ( pSocket ); if ( !EFI_ERROR ( Status )) { // // Validate the buffer length // if (( NULL == pDataLength ) || ( NULL == pBuffer )) { if ( NULL == pDataLength ) { DEBUG (( DEBUG_RX, "ERROR - pDataLength is NULL!\r\n" )); } else { DEBUG (( DEBUG_RX, "ERROR - pBuffer is NULL!\r\n" )); } Status = EFI_INVALID_PARAMETER; pSocket->errno = EFAULT; } else { // // Verify the API // if ( NULL == pSocket->pApi->pfnReceive ) { Status = EFI_UNSUPPORTED; pSocket->errno = ENOTSUP; } else { // // Zero the receive address if being returned // pRemoteAddress = NULL; if ( NULL != pAddress ) { pRemoteAddress = (struct sockaddr *)&Addr; ZeroMem ( pRemoteAddress, sizeof ( Addr )); pRemoteAddress->sa_family = pSocket->pApi->AddressFamily; pRemoteAddress->sa_len = (UINT8)pSocket->pApi->AddressLength; } // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Assume failure // Status = EFI_UNSUPPORTED; pSocket->errno = ENOTCONN; // // Verify that the socket is connected // if ( SOCKET_STATE_CONNECTED == pSocket->State ) { // // Poll the network to increase performance // EslSocketRxPoll ( pSocket ); // // Locate the port // pPort = pSocket->pPortList; if ( NULL != pPort ) { // // Determine the queue head // bUrgentQueue = (BOOLEAN)( 0 != ( Flags & MSG_OOB )); if ( bUrgentQueue ) { ppQueueHead = &pSocket->pRxOobPacketListHead; ppQueueTail = &pSocket->pRxOobPacketListTail; pRxDataBytes = &pSocket->RxOobBytes; } else { ppQueueHead = &pSocket->pRxPacketListHead; ppQueueTail = &pSocket->pRxPacketListTail; pRxDataBytes = &pSocket->RxBytes; } // // Determine if there is any data on the queue // *pDataLength = 0; pPacket = *ppQueueHead; if ( NULL != pPacket ) { // // Copy the received data // do { // // Attempt to receive a packet // SkipBytes = 0; bConsumePacket = (BOOLEAN)( 0 == ( Flags & MSG_PEEK )); pBuffer = pSocket->pApi->pfnReceive ( pPort, pPacket, &bConsumePacket, BufferLength, pBuffer, &DataLength, (struct sockaddr *)&Addr, &SkipBytes ); *pDataLength += DataLength; BufferLength -= DataLength; // // Determine if the data is being read // pNextPacket = pPacket->pNext; if ( bConsumePacket ) { // // All done with this packet // Account for any discarded data // pSocket->pApi->pfnPacketFree ( pPacket, pRxDataBytes ); if ( 0 != SkipBytes ) { DEBUG (( DEBUG_RX, "0x%08x: Port, packet read, skipping over 0x%08x bytes\r\n", pPort, SkipBytes )); } // // Remove this packet from the queue // *ppQueueHead = pPacket->pNext; if ( NULL == *ppQueueHead ) { *ppQueueTail = NULL; } // // Move the packet to the free queue // pPacket->pNext = pSocket->pRxFree; pSocket->pRxFree = pPacket; DEBUG (( DEBUG_RX, "0x%08x: Port freeing packet 0x%08x\r\n", pPort, pPacket )); // // Restart the receive operation if necessary // if (( NULL != pPort->pRxFree ) && ( MAX_RX_DATA > pSocket->RxBytes )) { EslSocketRxStart ( pPort ); } } // // Get the next packet // pPacket = pNextPacket; } while (( SOCK_STREAM == pSocket->Type ) && ( NULL != pPacket ) && ( 0 < BufferLength )); // // Successful operation // Status = EFI_SUCCESS; pSocket->errno = 0; } else { // // The queue is empty // Determine if it is time to return the receive error // if ( EFI_ERROR ( pSocket->RxError ) && ( NULL == pSocket->pRxPacketListHead ) && ( NULL == pSocket->pRxOobPacketListHead )) { Status = pSocket->RxError; pSocket->RxError = EFI_SUCCESS; switch ( Status ) { default: pSocket->errno = EIO; break; case EFI_CONNECTION_FIN: // // Continue to return zero bytes received when the // peer has successfully closed the connection // pSocket->RxError = EFI_CONNECTION_FIN; *pDataLength = 0; pSocket->errno = 0; Status = EFI_SUCCESS; break; case EFI_CONNECTION_REFUSED: pSocket->errno = ECONNREFUSED; break; case EFI_CONNECTION_RESET: pSocket->errno = ECONNRESET; break; case EFI_HOST_UNREACHABLE: pSocket->errno = EHOSTUNREACH; break; case EFI_NETWORK_UNREACHABLE: pSocket->errno = ENETUNREACH; break; case EFI_PORT_UNREACHABLE: pSocket->errno = EPROTONOSUPPORT; break; case EFI_PROTOCOL_UNREACHABLE: pSocket->errno = ENOPROTOOPT; break; } } else { Status = EFI_NOT_READY; pSocket->errno = EAGAIN; } } } } // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); if (( !EFI_ERROR ( Status )) && ( NULL != pAddress )) { // // Return the remote address if requested, truncate if necessary // AddressLength = pRemoteAddress->sa_len; if ( AddressLength > *pAddressLength ) { AddressLength = *pAddressLength; } DEBUG (( DEBUG_RX, "Returning the remote address, 0x%016x bytes --> 0x%16x\r\n", *pAddressLength, pAddress )); ZeroMem ( pAddress, *pAddressLength ); CopyMem ( pAddress, &Addr, AddressLength ); // // Update the address length // *pAddressLength = pRemoteAddress->sa_len; } } } } } } else { // // Bad return address pointer and length // Status = EFI_INVALID_PARAMETER; pSocket->errno = EINVAL; } } // // Return the operation status // if ( NULL != pErrno ) { if ( NULL != pSocket ) { *pErrno = pSocket->errno; } else { Status = EFI_INVALID_PARAMETER; *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); return Status; } /** Cancel the receive operations This routine cancels a pending receive operation. See the \ref ReceiveEngine section. This routine is called by ::EslSocketShutdown when the socket layer is being shutdown. @param [in] pPort Address of an ::ESL_PORT structure @param [in] pIo Address of an ::ESL_IO_MGMT structure **/ VOID EslSocketRxCancel ( IN ESL_PORT * pPort, IN ESL_IO_MGMT * pIo ) { EFI_STATUS Status; DBG_ENTER ( ); // // Cancel the outstanding receive // Status = pPort->pfnRxCancel ( pPort->pProtocol.v, &pIo->Token ); if ( !EFI_ERROR ( Status )) { DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Packet receive aborted on port: 0x%08x\r\n", pIo->pPacket, pPort )); } else { DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, "0x%08x: Packet receive pending on Port 0x%08x, Status: %r\r\n", pIo->pPacket, pPort, Status )); } DBG_EXIT ( ); } /** Process the receive completion This routine queues the data in FIFO order in either the urgent or normal data queues depending upon the type of data received. See the \ref ReceiveEngine section. This routine is called when some data is received by: @param [in] pIo Address of an ::ESL_IO_MGMT structure @param [in] Status Receive status @param [in] LengthInBytes Length of the receive data @param [in] bUrgent TRUE if urgent data is received and FALSE for normal data. **/ VOID EslSocketRxComplete ( IN ESL_IO_MGMT * pIo, IN EFI_STATUS Status, IN UINTN LengthInBytes, IN BOOLEAN bUrgent ) { BOOLEAN bUrgentQueue; ESL_IO_MGMT * pIoNext; ESL_PACKET * pPacket; ESL_PORT * pPort; ESL_PACKET * pPrevious; ESL_PACKET ** ppQueueHead; ESL_PACKET ** ppQueueTail; size_t * pRxBytes; ESL_SOCKET * pSocket; DBG_ENTER ( ); VERIFY_AT_TPL ( TPL_SOCKETS ); // // Locate the active receive packet // pPacket = pIo->pPacket; pPort = pIo->pPort; pSocket = pPort->pSocket; // // pPort->pRxActive // | // V // +-------------+ +-------------+ +-------------+ // Active | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL // +-------------+ +-------------+ +-------------+ // // +-------------+ +-------------+ +-------------+ // Free | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL // +-------------+ +-------------+ +-------------+ // ^ // | // pPort->pRxFree // // // Remove the IO structure from the active list // The following code searches for the entry in the list and does not // assume that the receive operations complete in the order they were // issued to the UEFI network layer. // pIoNext = pPort->pRxActive; while (( NULL != pIoNext ) && ( pIoNext != pIo ) && ( pIoNext->pNext != pIo )) { pIoNext = pIoNext->pNext; } ASSERT ( NULL != pIoNext ); if ( pIoNext == pIo ) { pPort->pRxActive = pIo->pNext; // Beginning of list } else { pIoNext->pNext = pIo->pNext; // Middle of list } // // Free the IO structure // pIo->pNext = pPort->pRxFree; pPort->pRxFree = pIo; // // pRxOobPacketListHead pRxOobPacketListTail // | | // V V // +------------+ +------------+ +------------+ // Urgent Data | ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL // +------------+ +------------+ +------------+ // // +------------+ +------------+ +------------+ // Normal Data | ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL // +------------+ +------------+ +------------+ // ^ ^ // | | // pRxPacketListHead pRxPacketListTail // // // Determine the queue to use // bUrgentQueue = (BOOLEAN)( bUrgent && pSocket->pApi->bOobSupported && ( !pSocket->bOobInLine )); if ( bUrgentQueue ) { ppQueueHead = &pSocket->pRxOobPacketListHead; ppQueueTail = &pSocket->pRxOobPacketListTail; pRxBytes = &pSocket->RxOobBytes; } else { ppQueueHead = &pSocket->pRxPacketListHead; ppQueueTail = &pSocket->pRxPacketListTail; pRxBytes = &pSocket->RxBytes; } // // Determine if this receive was successful // if (( !EFI_ERROR ( Status )) && ( PORT_STATE_CLOSE_STARTED > pPort->State ) && ( !pSocket->bRxDisable )) { // // Account for the received data // *pRxBytes += LengthInBytes; // // Log the received data // DEBUG (( DEBUG_RX | DEBUG_INFO, "0x%08x: Packet queued on %s queue of port 0x%08x with 0x%08x bytes of %s data\r\n", pPacket, bUrgentQueue ? L"urgent" : L"normal", pPort, LengthInBytes, bUrgent ? L"urgent" : L"normal" )); // // Add the packet to the list tail. // pPacket->pNext = NULL; pPrevious = *ppQueueTail; if ( NULL == pPrevious ) { *ppQueueHead = pPacket; } else { pPrevious->pNext = pPacket; } *ppQueueTail = pPacket; // // Attempt to restart this receive operation // if ( pSocket->MaxRxBuf > pSocket->RxBytes ) { EslSocketRxStart ( pPort ); } else { DEBUG (( DEBUG_RX, "0x%08x: Port RX suspended, 0x%08x bytes queued\r\n", pPort, pSocket->RxBytes )); } } else { if ( EFI_ERROR ( Status )) { DEBUG (( DEBUG_RX | DEBUG_INFO, "ERROR - Receive error on port 0x%08x, packet 0x%08x, Status:%r\r\n", pPort, pPacket, Status )); } // // Account for the receive bytes and release the driver's buffer // if ( !EFI_ERROR ( Status )) { *pRxBytes += LengthInBytes; pSocket->pApi->pfnPacketFree ( pPacket, pRxBytes ); } // // Receive error, free the packet save the error // EslSocketPacketFree ( pPacket, DEBUG_RX ); if ( !EFI_ERROR ( pSocket->RxError )) { pSocket->RxError = Status; } // // Update the port state // if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { if ( PORT_STATE_CLOSE_DONE == pPort->State ) { EslSocketPortCloseRxDone ( pPort ); } } else { if ( EFI_ERROR ( Status )) { DEBUG (( DEBUG_RX | DEBUG_INFO, "0x%08x: Port state: PORT_STATE_RX_ERROR, Status: %r\r\n", pPort, Status )); pPort->State = PORT_STATE_RX_ERROR; } } } DBG_EXIT ( ); } /** Poll a socket for pending receive activity. This routine is called at elivated TPL and extends the idle loop which polls a socket down into the LAN driver layer to determine if there is any receive activity. The ::EslSocketPoll, ::EslSocketReceive and ::EslSocketTransmit routines call this routine when there is nothing to do. @param [in] pSocket Address of an ::EFI_SOCKET structure. **/ VOID EslSocketRxPoll ( IN ESL_SOCKET * pSocket ) { ESL_PORT * pPort; DEBUG (( DEBUG_POLL, "Entering EslSocketRxPoll\r\n" )); // // Increase the network performance by extending the // polling (idle) loop down into the LAN driver // pPort = pSocket->pPortList; while ( NULL != pPort ) { // // Poll the LAN adapter // pPort->pfnRxPoll ( pPort->pProtocol.v ); // // Locate the next LAN adapter // pPort = pPort->pLinkSocket; } DEBUG (( DEBUG_POLL, "Exiting EslSocketRxPoll\r\n" )); } /** Start a receive operation This routine posts a receive buffer to the network adapter. See the \ref ReceiveEngine section. This support routine is called by: @param [in] pPort Address of an ::ESL_PORT structure. **/ VOID EslSocketRxStart ( IN ESL_PORT * pPort ) { UINT8 * pBuffer; ESL_IO_MGMT * pIo; ESL_PACKET * pPacket; ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); // // Determine if a receive is already pending // Status = EFI_SUCCESS; pPacket = NULL; pSocket = pPort->pSocket; if ( !EFI_ERROR ( pPort->pSocket->RxError )) { if (( NULL != pPort->pRxFree ) && ( !pSocket->bRxDisable ) && ( PORT_STATE_CLOSE_STARTED > pPort->State )) { // // Start all of the pending receive operations // while ( NULL != pPort->pRxFree ) { // // Determine if there are any free packets // pPacket = pSocket->pRxFree; if ( NULL != pPacket ) { // // Remove this packet from the free list // pSocket->pRxFree = pPacket->pNext; DEBUG (( DEBUG_RX, "0x%08x: Port removed packet 0x%08x from free list\r\n", pPort, pPacket )); } else { // // Allocate a packet structure // Status = EslSocketPacketAllocate ( &pPacket, pSocket->pApi->RxPacketBytes, pSocket->pApi->RxZeroBytes, DEBUG_RX ); if ( EFI_ERROR ( Status )) { pPacket = NULL; DEBUG (( DEBUG_ERROR | DEBUG_RX, "0x%08x: Port failed to allocate RX packet, Status: %r\r\n", pPort, Status )); break; } } // // Connect the IO and packet structures // pIo = pPort->pRxFree; pIo->pPacket = pPacket; // // Eliminate the need for IP4 and UDP4 specific routines by // clearing the RX data pointer here. // // No driver buffer for this packet // // +--------------------+ // | ESL_IO_MGMT | // | | // | +---------------+ // | | Token | // | | RxData --> NULL // +----+---------------+ // pBuffer = (UINT8 *)pIo; pBuffer = &pBuffer[ pSocket->pApi->RxBufferOffset ]; *(VOID **)pBuffer = NULL; // // Network specific receive packet initialization // if ( NULL != pSocket->pApi->pfnRxStart ) { pSocket->pApi->pfnRxStart ( pPort, pIo ); } // // Start the receive on the packet // Status = pPort->pfnRxStart ( pPort->pProtocol.v, &pIo->Token ); if ( !EFI_ERROR ( Status )) { DEBUG (( DEBUG_RX | DEBUG_INFO, "0x%08x: Packet receive pending on port 0x%08x\r\n", pPacket, pPort )); // // Allocate the receive control structure // pPort->pRxFree = pIo->pNext; // // Mark this receive as pending // pIo->pNext = pPort->pRxActive; pPort->pRxActive = pIo; } else { DEBUG (( DEBUG_RX | DEBUG_INFO, "ERROR - Failed to post a receive on port 0x%08x, Status: %r\r\n", pPort, Status )); if ( !EFI_ERROR ( pSocket->RxError )) { // // Save the error status // pSocket->RxError = Status; } // // Free the packet // pIo->pPacket = NULL; pPacket->pNext = pSocket->pRxFree; pSocket->pRxFree = pPacket; break; } } } else { if ( NULL == pPort->pRxFree ) { DEBUG (( DEBUG_RX | DEBUG_INFO, "0x%08x: Port, no available ESL_IO_MGMT structures\r\n", pPort)); } if ( pSocket->bRxDisable ) { DEBUG (( DEBUG_RX | DEBUG_INFO, "0x%08x: Port, receive disabled!\r\n", pPort )); } if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { DEBUG (( DEBUG_RX | DEBUG_INFO, "0x%08x: Port, is closing!\r\n", pPort )); } } } else { DEBUG (( DEBUG_ERROR | DEBUG_RX, "ERROR - Previous receive error, Status: %r\r\n", pPort->pSocket->RxError )); } DBG_EXIT ( ); } /** Shutdown the socket receive and transmit operations This routine sets a flag to stop future transmissions and calls the network specific layer to cancel the pending receive operation. The ::shutdown routine calls this routine to stop receive and transmit operations on the socket. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] How Which operations to stop @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Socket operations successfully shutdown **/ EFI_STATUS EslSocketShutdown ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN int How, IN int * pErrno ) { ESL_IO_MGMT * pIo; ESL_PORT * pPort; ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; // // Validate the socket // pSocket = NULL; if ( NULL != pSocketProtocol ) { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // // Verify that the socket is connected // if ( pSocket->bConnected ) { // // Validate the How value // if (( SHUT_RD <= How ) && ( SHUT_RDWR >= How )) { // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Disable the receiver if requested // if (( SHUT_RD == How ) || ( SHUT_RDWR == How )) { pSocket->bRxDisable = TRUE; } // // Disable the transmitter if requested // if (( SHUT_WR == How ) || ( SHUT_RDWR == How )) { pSocket->bTxDisable = TRUE; } // // Cancel the pending receive operations // if ( pSocket->bRxDisable ) { // // Walk the list of ports // pPort = pSocket->pPortList; while ( NULL != pPort ) { // // Walk the list of active receive operations // pIo = pPort->pRxActive; while ( NULL != pIo ) { EslSocketRxCancel ( pPort, pIo ); } // // Set the next port // pPort = pPort->pLinkSocket; } } // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); } else { // // Invalid How value // pSocket->errno = EINVAL; Status = EFI_INVALID_PARAMETER; } } else { // // The socket is not connected // pSocket->errno = ENOTCONN; Status = EFI_NOT_STARTED; } } // // Return the operation status // if ( NULL != pErrno ) { if ( NULL != pSocket ) { *pErrno = pSocket->errno; } else { Status = EFI_INVALID_PARAMETER; *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); return Status; } /** Send data using a network connection. This routine calls the network specific layer to queue the data for transmission. Eventually the buffer will reach the head of the queue and will get transmitted over the network by the \ref TransmitEngine. For datagram sockets (SOCK_DGRAM and SOCK_RAW) there is no guarantee that the data reaches the application running on the remote system. The ::sendto routine calls this routine to send data to the remote system. Note that ::send and ::write are layered on top of ::sendto. @param [in] pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure. @param [in] Flags Message control flags @param [in] BufferLength Length of the the buffer @param [in] pBuffer Address of a buffer containing the data to send @param [in] pDataLength Address to receive the number of data bytes sent @param [in] pAddress Network address of the remote system address @param [in] AddressLength Length of the remote network address structure @param [out] pErrno Address to receive the errno value upon completion. @retval EFI_SUCCESS - Socket data successfully queued for transmit **/ EFI_STATUS EslSocketTransmit ( IN EFI_SOCKET_PROTOCOL * pSocketProtocol, IN int Flags, IN size_t BufferLength, IN CONST UINT8 * pBuffer, OUT size_t * pDataLength, IN const struct sockaddr * pAddress, IN socklen_t AddressLength, IN int * pErrno ) { ESL_SOCKET * pSocket; EFI_STATUS Status; EFI_TPL TplPrevious; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; // // Validate the socket // pSocket = NULL; if ( NULL != pSocketProtocol ) { pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol ); // // Return the transmit error if necessary // if ( EFI_SUCCESS != pSocket->TxError ) { pSocket->errno = EIO; Status = pSocket->TxError; pSocket->TxError = EFI_SUCCESS; } else { // // Verify the socket state // Status = EslSocketIsConfigured ( pSocket ); if ( !EFI_ERROR ( Status )) { // // Verify that transmit is still allowed // if ( !pSocket->bTxDisable ) { // // Validate the buffer length // if (( NULL == pDataLength ) && ( 0 > pDataLength ) && ( NULL == pBuffer )) { if ( NULL == pDataLength ) { DEBUG (( DEBUG_RX, "ERROR - pDataLength is NULL!\r\n" )); } else if ( NULL == pBuffer ) { DEBUG (( DEBUG_RX, "ERROR - pBuffer is NULL!\r\n" )); } else { DEBUG (( DEBUG_RX, "ERROR - Data length < 0!\r\n" )); } Status = EFI_INVALID_PARAMETER; pSocket->errno = EFAULT; } else { // // Validate the remote network address // if (( NULL != pAddress ) && ( AddressLength < pAddress->sa_len )) { DEBUG (( DEBUG_TX, "ERROR - Invalid sin_len field in address\r\n" )); Status = EFI_INVALID_PARAMETER; pSocket->errno = EFAULT; } else { // // Verify the API // if ( NULL == pSocket->pApi->pfnTransmit ) { Status = EFI_UNSUPPORTED; pSocket->errno = ENOTSUP; } else { // // Synchronize with the socket layer // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); // // Poll the network to increase performance // EslSocketRxPoll ( pSocket ); // // Attempt to buffer the packet for transmission // Status = pSocket->pApi->pfnTransmit ( pSocket, Flags, BufferLength, pBuffer, pDataLength, pAddress, AddressLength ); // // Release the socket layer synchronization // RESTORE_TPL ( TplPrevious ); } } } } else { // // The transmitter was shutdown // pSocket->errno = EPIPE; Status = EFI_NOT_STARTED; } } } } // // Return the operation status // if ( NULL != pErrno ) { if ( NULL != pSocket ) { *pErrno = pSocket->errno; } else { Status = EFI_INVALID_PARAMETER; *pErrno = ENOTSOCK; } } DBG_EXIT_STATUS ( Status ); return Status; } /** Complete the transmit operation This support routine handles the transmit completion processing for the various network layers. It frees the ::ESL_IO_MGMT structure and and frees packet resources by calling ::EslSocketPacketFree. Transmit errors are logged in ESL_SOCKET::TxError. See the \ref TransmitEngine section. This routine is called by: @param [in] pIo Address of an ::ESL_IO_MGMT structure @param [in] LengthInBytes Length of the data in bytes @param [in] Status Transmit operation status @param [in] pQueueType Zero terminated string describing queue type @param [in] ppQueueHead Transmit queue head address @param [in] ppQueueTail Transmit queue tail address @param [in] ppActive Active transmit queue address @param [in] ppFree Free transmit queue address **/ VOID EslSocketTxComplete ( IN ESL_IO_MGMT * pIo, IN UINT32 LengthInBytes, IN EFI_STATUS Status, IN CONST CHAR8 * pQueueType, IN ESL_PACKET ** ppQueueHead, IN ESL_PACKET ** ppQueueTail, IN ESL_IO_MGMT ** ppActive, IN ESL_IO_MGMT ** ppFree ) { ESL_PACKET * pCurrentPacket; ESL_IO_MGMT * pIoNext; ESL_PACKET * pNextPacket; ESL_PACKET * pPacket; ESL_PORT * pPort; ESL_SOCKET * pSocket; DBG_ENTER ( ); VERIFY_AT_TPL ( TPL_SOCKETS ); // // Locate the active transmit packet // pPacket = pIo->pPacket; pPort = pIo->pPort; pSocket = pPort->pSocket; // // No more packet // pIo->pPacket = NULL; // // Remove the IO structure from the active list // pIoNext = *ppActive; while (( NULL != pIoNext ) && ( pIoNext != pIo ) && ( pIoNext->pNext != pIo )) { pIoNext = pIoNext->pNext; } ASSERT ( NULL != pIoNext ); if ( pIoNext == pIo ) { *ppActive = pIo->pNext; // Beginning of list } else { pIoNext->pNext = pIo->pNext; // Middle of list } // // Free the IO structure // pIo->pNext = *ppFree; *ppFree = pIo; // // Display the results // DEBUG (( DEBUG_TX | DEBUG_INFO, "0x%08x: pIo Released\r\n", pIo )); // // Save any transmit error // if ( EFI_ERROR ( Status )) { if ( !EFI_ERROR ( pSocket->TxError )) { pSocket->TxError = Status; } DEBUG (( DEBUG_TX | DEBUG_INFO, "ERROR - Transmit failure for %apacket 0x%08x, Status: %r\r\n", pQueueType, pPacket, Status )); // // Empty the normal transmit list // pCurrentPacket = pPacket; pNextPacket = *ppQueueHead; while ( NULL != pNextPacket ) { pPacket = pNextPacket; pNextPacket = pPacket->pNext; EslSocketPacketFree ( pPacket, DEBUG_TX ); } *ppQueueHead = NULL; *ppQueueTail = NULL; pPacket = pCurrentPacket; } else { DEBUG (( DEBUG_TX | DEBUG_INFO, "0x%08x: %apacket transmitted %d bytes successfully\r\n", pPacket, pQueueType, LengthInBytes )); // // Verify the transmit engine is still running // if ( !pPort->bCloseNow ) { // // Start the next packet transmission // EslSocketTxStart ( pPort, ppQueueHead, ppQueueTail, ppActive, ppFree ); } } // // Release this packet // EslSocketPacketFree ( pPacket, DEBUG_TX ); // // Finish the close operation if necessary // if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) { // // Indicate that the transmit is complete // EslSocketPortCloseTxDone ( pPort ); } DBG_EXIT ( ); } /** Transmit data using a network connection. This support routine starts a transmit operation on the underlying network layer. The network specific code calls this routine to start a transmit operation. See the \ref TransmitEngine section. @param [in] pPort Address of an ::ESL_PORT structure @param [in] ppQueueHead Transmit queue head address @param [in] ppQueueTail Transmit queue tail address @param [in] ppActive Active transmit queue address @param [in] ppFree Free transmit queue address **/ VOID EslSocketTxStart ( IN ESL_PORT * pPort, IN ESL_PACKET ** ppQueueHead, IN ESL_PACKET ** ppQueueTail, IN ESL_IO_MGMT ** ppActive, IN ESL_IO_MGMT ** ppFree ) { UINT8 * pBuffer; ESL_IO_MGMT * pIo; ESL_PACKET * pNextPacket; ESL_PACKET * pPacket; VOID ** ppTokenData; ESL_SOCKET * pSocket; EFI_STATUS Status; DBG_ENTER ( ); // // Assume success // Status = EFI_SUCCESS; // // Get the packet from the queue head // pPacket = *ppQueueHead; pIo = *ppFree; if (( NULL != pPacket ) && ( NULL != pIo )) { pSocket = pPort->pSocket; // // *ppQueueHead: pSocket->pRxPacketListHead or pSocket->pRxOobPacketListHead // | // V // +------------+ +------------+ +------------+ // Data | ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL // +------------+ +------------+ +------------+ // ^ // | // *ppQueueTail: pSocket->pRxPacketListTail or pSocket->pRxOobPacketListTail // // // Remove the packet from the queue // pNextPacket = pPacket->pNext; *ppQueueHead = pNextPacket; if ( NULL == pNextPacket ) { *ppQueueTail = NULL; } pPacket->pNext = NULL; // // Eliminate the need for IP4 and UDP4 specific routines by // connecting the token with the TX data control structure here. // // +--------------------+ +--------------------+ // | ESL_IO_MGMT | | ESL_PACKET | // | | | | // | +---------------+ +----------------+ | // | | Token | | Buffer Length | | // | | TxData --> | Buffer Address | | // | | | +----------------+---+ // | | Event | | Data Buffer | // +----+---------------+ | | // +--------------------+ // // Compute the address of the TxData pointer in the token // pBuffer = (UINT8 *)&pIo->Token; pBuffer = &pBuffer[ pSocket->TxTokenOffset ]; ppTokenData = (VOID **)pBuffer; // // Compute the address of the TX data control structure in the packet // // * EFI_IP4_TRANSMIT_DATA // * EFI_TCP4_TRANSMIT_DATA // * EFI_UDP4_TRANSMIT_DATA // pBuffer = (UINT8 *)pPacket; pBuffer = &pBuffer[ pSocket->TxPacketOffset ]; // // Connect the token to the transmit data control structure // *ppTokenData = (VOID **)pBuffer; // // Display the results // DEBUG (( DEBUG_TX | DEBUG_INFO, "0x%08x: pIo allocated for pPacket: 0x%08x\r\n", pIo, pPacket )); // // Start the transmit operation // Status = pPort->pfnTxStart ( pPort->pProtocol.v, &pIo->Token ); if ( !EFI_ERROR ( Status )) { // // Connect the structures // pIo->pPacket = pPacket; // // +-------------+ +-------------+ +-------------+ // Free | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL // +-------------+ +-------------+ +-------------+ // ^ // | // *ppFree: pPort->pTxFree or pTxOobFree // // // Remove the IO structure from the queue // *ppFree = pIo->pNext; // // *ppActive: pPort->pTxActive or pTxOobActive // | // V // +-------------+ +-------------+ +-------------+ // Active | ESL_IO_MGMT |-->| ESL_IO_MGMT |-->| ESL_IO_MGMT |--> NULL // +-------------+ +-------------+ +-------------+ // // // Mark this packet as active // pIo->pPacket = pPacket; pIo->pNext = *ppActive; *ppActive = pIo; } else { if ( EFI_SUCCESS == pSocket->TxError ) { pSocket->TxError = Status; } // // Discard the transmit buffer // EslSocketPacketFree ( pPacket, DEBUG_TX ); } } DBG_EXIT ( ); }