/** @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) 2010 - 2024, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent


  \section DataStructures Data Structures

  <code><pre>

                +---------------+   +-------------+   +-------------+
  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)

  </pre></code>

  ::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:
  <ul>
    <li>ESL_SOCKET::pRxPacketListHead - Normal (low) priority receive data</li>
    <li>ESL_SOCKET::pRxOobPacketListHead - High (out-of-band or urgent) priority receive data</li>
    <li>ESL_SOCKET::pTxPacketListHead - Normal (low) priority transmit data</li>
    <li>ESL_SOCKET::pTxOobPacketListHead - High (out-of-band or urgent) priority transmit data</li>
  </ul>
  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:

  <code><pre>

      +--------------------------+
      |          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          |
      +--------------------------+

  </pre></code>

  <ul>
    <li>Arc: ::EslSocketPortCloseStart - Marks the port as closing and
      initiates the port close operation</li>
    <li>State: PORT_STATE_CLOSE_STARTED</li>
    <li>Arc: ::EslSocketPortCloseTxDone - Waits until all of the transmit
      operations to complete.  After all of the transmits are complete,
      this routine initiates the network specific close operation by calling
      through ESL_PROTOCOL_API::pfnPortCloseOp.  One such routine is
      ::EslTcp4PortCloseOp.
    </li>
    <li>State: PORT_STATE_CLOSE_TX_DONE</li>
    <li>Arc: ::EslSocketPortCloseComplete - Called when the close operation is
      complete.  After the transition to PORT_STATE_CLOSE_DONE,
      this routine calls ::EslSocketRxCancel to abort the pending receive operations.
    </li>
    <li>State: PORT_STATE_CLOSE_DONE</li>
    <li>Arc: ::EslSocketPortCloseRxDone - Waits until all of the receive
      operation have been cancelled.  After the transition to
      PORT_STATE_CLOSE_RX_DONE, this routine calls ::EslSocketPortClose.
    </li>
    <li>State: PORT_STATE_CLOSE_RX_DONE</li>
    <li>Arc: ::EslSocketPortClose - This routine discards any receive buffers
      using a network specific support routine via ESL_PROTOCOL_API::pfnPacketFree.
      This routine then releases the port resources allocated by ::EslSocketPortAllocate
      and calls the network specific port close routine (e.g. ::EslTcp4PortClose)
      via ESL_PROTOCOL_API::pfnPortClose to release any network specific resources.
    </li>
  </ul>


  \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.

<code><pre>

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

</pre></code>

  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.

<code><pre>

         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
</pre></code>

  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.

<code><pre>

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

</pre></code>

  ::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.

<code><pre>

  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        |
      +----+---------------+   |                          |
                               |                          |
                               +--------------------------+

</pre></code>

  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.

<code><pre>

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

</pre></code>

  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.

<code><pre>

      +--------------------+   +--------------------+
      | ESL_IO_MGMT        |   | ESL_PACKET         |
      |                    |   |                    |
      |    +---------------+   +----------------+   |
      |    | Token         |   | Buffer Length  |   |
      |    |        TxData --> | Buffer Address |   |
      |    |               |   +----------------+---+
      |    |        Event  |   | Data Buffer        |
      +----+---------------+   |                    |
                               +--------------------+
</pre></code>

  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.

<code><pre>

         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

</pre></code>

  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 error_num;

  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
  error_num = 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;
      error_num = 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;
      error_num = 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" ));
        error_num = 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" ));
      error_num = EPROTONOSUPPORT;
      error_num = 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 = error_num;
  }
  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;

  //
  //  Validate that the port is connected
  //
  Status = pPort->pSocket->pApi->pfnVerifyLocalIpAddress ( pPort, pBuffer );
  if ( EFI_ERROR ( Status )) {
    DEBUG (( DEBUG_WARN | DEBUG_BIND,
              "WARNING - Port 0x%08x invalid IP address: %r\r\n",
              pPort,
              Status ));
    pPort->pSocket->errno = ErrnoValue;
  }
  else {
    //
    //  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 error_num;
  ESL_LAYER * pLayer;
  ESL_SOCKET * pNextSocket;
  ESL_SOCKET * pSocket;
  EFI_STATUS Status;
  EFI_TPL TplPrevious;

  DBG_ENTER ( );

  //
  //  Assume success
  //
  error_num = 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;
          error_num = EAGAIN;
        }
      }
      else {
        //
        //  SocketCloseStart was not called
        //
        Status = EFI_NOT_STARTED;
        error_num = 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;
    error_num = ENOTSOCK;
  }

  //
  //  Release the socket layer synchronization
  //
  RESTORE_TPL ( TplPrevious );

  //
  //  Return the operation status
  //
  if ( NULL != pErrno ) {
    *pErrno = error_num;
  }
  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 error_num;
  ESL_PORT * pNextPort;
  ESL_PORT * pPort;
  ESL_SOCKET * pSocket;
  EFI_STATUS Status;
  EFI_TPL TplPrevious;

  DBG_ENTER ( );

  //
  //  Assume success
  //
  Status = EFI_SUCCESS;
  error_num = 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 )) {
        error_num = EIO;
        break;
      }

      //
      //  Set the next port
      //
      pPort = pNextPort;
    }

    //
    //  Attempt to finish closing the socket
    //
    if ( NULL == pPort ) {
      Status = EslSocketClosePoll ( pSocketProtocol, &error_num );
    }
  }
  else {
    Status = EFI_NOT_READY;
    error_num = EAGAIN;
  }

  //
  //  Release the socket layer synchronization
  //
  RESTORE_TPL ( TplPrevious );

  //
  //  Return the operation status
  //
  if ( NULL != pErrno ) {
    *pErrno = error_num;
  }
  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_BOUND;
            }
          }
        }
        break;

      case SOCKET_STATE_CONNECTED:
        //
        //  Connected
        //
        Status = EFI_SUCCESS;
        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 error_num;
  ESL_LAYER * pLayer;
  ESL_SOCKET * pSocket;
  ESL_SOCKET * pSocketPrevious;
  EFI_STATUS Status;
  EFI_TPL TplPrevious;

  DBG_ENTER ( );

  //
  //  Assume failure
  //
  error_num = 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 )));
          error_num = 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 error_num value if possible
  //
  if ( NULL != pErrno ) {
    *pErrno = error_num;
  }

  //
  //  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:
  <ul>
    <li>::EslSocketGetLocalAddress</li>
    <li>::EslSocketGetPeerAddress</li>
    <li>::EslSocketPoll</li>
    <li>::EslSocketReceive</li>
    <li>::EslSocketTransmit</li>
  </ul>

  @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 error_num;
  socklen_t LengthInBytes;
  socklen_t MaxBytes;
  CONST UINT8 * pOptionData;
  ESL_SOCKET * pSocket;
  EFI_STATUS Status;

  DBG_ENTER ( );

  //
  //  Assume failure
  //
  error_num = 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 );
          error_num = 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 ));
      }
      error_num = 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" ));
        error_num = 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;
      case SO_ERROR:
        //
        //  Return the socket type
        //
        pOptionData = (CONST UINT8 *)&pSocket->errno;
        LengthInBytes = sizeof ( pSocket->errno );
        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 );
      }
      error_num = 0;
      Status = EFI_SUCCESS;
    }
  }

  //
  //  Return the operation status
  //
  if ( NULL != pErrno ) {
    *pErrno = error_num;
  }
  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 error_num;
  socklen_t LengthInBytes;
  UINT8 * pOptionData;
  ESL_SOCKET * pSocket;
  EFI_STATUS Status;

  DBG_ENTER ( );

  //
  //  Assume failure
  //
  error_num = 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 );
            error_num = 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 ));
        }
        error_num = 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" ));
          error_num = 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 );
          error_num = 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 = error_num;
  }
  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;
  int   _errno = EINVAL;

  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;
      }
      _errno = pSocket->errno;
    }
  }

  //
  //  Return the detected events
  //
  *pEvents = DetectedEvents & ( Events
                              | POLLERR
                              | POLLHUP
                              | POLLNVAL );
  if ( NULL != pErrno ) {
    *pErrno = _errno;
  }
  //
  //  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:
  <ul>
    <li>::EslSocketBind</li>
    <li>::EslTcp4ListenComplete</li>
  </ul>
  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:
  <ul>
    <li>::EslSocketPortAllocate - Port initialization failure</li>
    <li>::EslSocketPortCloseRxDone - Last step of close processing</li>
    <li>::EslTcp4ConnectComplete - Connection failure and reducing the port list to a single port</li>
  </ul>
  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 EFIAPI
EslSocketPortCloseComplete (
  IN EFI_EVENT Event,
  IN VOID *context
  )
{
  ESL_IO_MGMT * pIo;
  EFI_STATUS Status;
  ESL_PORT * pPort = (ESL_PORT*) context;

  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 );
  --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
  <ul>
    <li>::EslSocketPortCloseComplete</li>
    <li>::EslSocketPortCloseTxDone</li>
    <li>::EslSocketRxComplete</li>
  </ul>
  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:
  <ul>
    <li>::EslIp4RxComplete</li>
    <li>::EslTcp4RxComplete</li>
    <li>::EslUdp4RxComplete</li>
  </ul>

  @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:
  <ul>
    <li>::EslIp4Receive to restart the receive engine to release flow control.</li>
    <li>::EslIp4RxComplete to continue the operation of the receive engine if flow control is not being applied.</li>
    <li>::EslIp4SocketIsConfigured to start the receive engine for the new socket.</li>
    <li>::EslTcp4ListenComplete to start the recevie engine for the new socket.</li>
    <li>::EslTcp4Receive to restart the receive engine to release flow control.</li>
    <li>::EslTcp4RxComplete to continue the operation of the receive engine if flow control is not being applied.</li>
    <li>::EslUdp4Receive to restart the receive engine to release flow control.</li>
    <li>::EslUdp4RxComplete to continue the operation of the receive engine if flow control is not being applied.</li>
    <li>::EslUdp4SocketIsConfigured to start the recevie engine for the new socket.</li>
  </ul>

  @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:
  <ul>
    <li>::EslIp4TxComplete</li>
    <li>::EslTcp4TxComplete</li>
    <li>::EslTcp4TxOobComplete</li>
    <li>::EslUdp4TxComplete</li>
  </ul>

  @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 {
      //
      //  Display the transmit error
      //
      DEBUG (( DEBUG_TX | DEBUG_INFO,
                "0x%08x, 0x%08x: pIo, pPacket transmit failure: %r\r\n",
                pIo,
                pPacket,
                Status ));
      if ( EFI_SUCCESS == pSocket->TxError ) {
        pSocket->TxError = Status;
      }

      //
      //  Free the IO structure
      //
      pIo->pNext = *ppFree;
      *ppFree = pIo;

      //
      //  Discard the transmit buffer
      //
      EslSocketPacketFree ( pPacket, DEBUG_TX );
    }
  }

  DBG_EXIT ( );
}
