mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-10-28 07:29:41 +00:00 
			
		
		
		
	 b81cc7d624
			
		
	
	
		b81cc7d624
		
	
	
	
	
		
			
			Removed variables that had no effect on code behavior. Normalized comment formatting. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Olivier Martin <olivier.martin@arm.com> Reviewed by: Daryl McDaniel <daryl.mcdaniel@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16286 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			5796 lines
		
	
	
		
			172 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			5796 lines
		
	
	
		
			172 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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 - 2014, Intel Corporation. All rights reserved.<BR>
 | |
|   This program and the accompanying materials are licensed and made available under
 | |
|   the terms and conditions of the BSD License that accompanies this distribution.
 | |
|   The full text of the license may be found at
 | |
|   http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
|   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
|   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| 
 | |
|   \section DataStructures Data Structures
 | |
| 
 | |
|   <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 errno;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //  Locate the socket
 | |
|   pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
 | |
| 
 | |
|   //  Set the default domain if necessary
 | |
|   if ( AF_UNSPEC == domain ) {
 | |
|     domain = AF_INET;
 | |
|   }
 | |
| 
 | |
|   //  Assume success
 | |
|   errno = 0;
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   //  Use break instead of goto
 | |
|   for ( ; ; ) {
 | |
|     //  Validate the domain value
 | |
|     if (( AF_INET != domain )
 | |
|       && ( AF_INET6 != domain )
 | |
|       && ( AF_LOCAL != domain )) {
 | |
|       DEBUG (( DEBUG_ERROR | DEBUG_SOCKET,
 | |
|                 "ERROR - Invalid domain value\r\n" ));
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       errno = EAFNOSUPPORT;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     //  Determine the protocol APIs
 | |
|     ppApiArray = NULL;
 | |
|     ApiArraySize = 0;
 | |
|     if (( AF_INET == domain )
 | |
|       || ( AF_LOCAL == domain )) {
 | |
|       ppApiArray = &cEslAfInetApi[0];
 | |
|       ApiArraySize = cEslAfInetApiSize;
 | |
|     }
 | |
|     else {
 | |
|       ppApiArray = &cEslAfInet6Api[0];
 | |
|       ApiArraySize = cEslAfInet6ApiSize;
 | |
|     }
 | |
| 
 | |
|     //  Set the default type if necessary
 | |
|     if ( 0 == type ) {
 | |
|       type = SOCK_STREAM;
 | |
|     }
 | |
| 
 | |
|     //  Validate the type value
 | |
|     if (( type >= ApiArraySize )
 | |
|       || ( NULL == ppApiArray )
 | |
|       || ( NULL == ppApiArray[ type ])) {
 | |
|       DEBUG (( DEBUG_ERROR | DEBUG_SOCKET,
 | |
|                 "ERROR - Invalid type value\r\n" ));
 | |
|       //  The socket type is not supported
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       errno = EPROTOTYPE;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     //  Set the default protocol if necessary
 | |
|     pApi = ppApiArray[ type ];
 | |
|     if ( 0 == protocol ) {
 | |
|       protocol = pApi->DefaultProtocol;
 | |
|     }
 | |
| 
 | |
|     //  Validate the protocol value
 | |
|     if (( pApi->DefaultProtocol != protocol )
 | |
|       && ( SOCK_RAW != type )) {
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
| 
 | |
|       //  Assume that the driver supports this protocol
 | |
|       ppApiArray = &cEslAfInetApi[0];
 | |
|       ppApiArrayEnd = &ppApiArray [ cEslAfInetApiSize ];
 | |
|       while ( ppApiArrayEnd > ppApiArray ) {
 | |
|         pApi = *ppApiArray;
 | |
|         if ( protocol == pApi->DefaultProtocol ) {
 | |
|           break;
 | |
|         }
 | |
|         ppApiArray += 1;
 | |
|       }
 | |
|       if ( ppApiArrayEnd <= ppApiArray ) {
 | |
|         //  Verify against the IPv6 table
 | |
|         ppApiArray = &cEslAfInet6Api[0];
 | |
|         ppApiArrayEnd = &ppApiArray [ cEslAfInet6ApiSize ];
 | |
|         while ( ppApiArrayEnd > ppApiArray ) {
 | |
|           pApi = *ppApiArray;
 | |
|           if ( protocol == pApi->DefaultProtocol ) {
 | |
|             break;
 | |
|           }
 | |
|           ppApiArray += 1;
 | |
|         }
 | |
|       }
 | |
|       if ( ppApiArrayEnd <= ppApiArray ) {
 | |
|         DEBUG (( DEBUG_ERROR | DEBUG_SOCKET,
 | |
|                   "ERROR - The protocol is not supported!\r\n" ));
 | |
|         errno = EPROTONOSUPPORT;
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       //  The driver does not support this protocol
 | |
|       DEBUG (( DEBUG_ERROR | DEBUG_SOCKET,
 | |
|                 "ERROR - The protocol does not support this socket type!\r\n" ));
 | |
|       errno = EPROTONOSUPPORT;
 | |
|       errno = EPROTOTYPE;
 | |
|       break;
 | |
|     }
 | |
|     //  Save the socket attributes
 | |
|     pSocket->pApi = pApi;
 | |
|     pSocket->Domain = domain;
 | |
|     pSocket->Type = type;
 | |
|     pSocket->Protocol = protocol;
 | |
| 
 | |
|     //  Done
 | |
|     break;
 | |
|   }
 | |
|   //  Return the operation status
 | |
|   if ( NULL != pErrno ) {
 | |
|     *pErrno = errno;
 | |
|   }
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Accept a network connection.
 | |
| 
 | |
|   This routine calls the network specific layer to remove the next
 | |
|   connection from the FIFO.
 | |
| 
 | |
|   The ::accept calls this routine to poll for a network
 | |
|   connection to the socket.  When a connection is available
 | |
|   this routine returns the ::EFI_SOCKET_PROTOCOL structure address
 | |
|   associated with the new socket and the remote network address
 | |
|   if requested.
 | |
| 
 | |
|   @param[in]      pSocketProtocol   Address of an ::EFI_SOCKET_PROTOCOL structure.
 | |
|   @param[in]      pSockAddr         Address of a buffer to receive the remote
 | |
|                                     network address.
 | |
|   @param[in,out]  pSockAddrLength   Length in bytes of the address buffer.
 | |
|                                     On output specifies the length of the
 | |
|                                     remote network address.
 | |
|   @param[out]     ppSocketProtocol  Address of a buffer to receive the
 | |
|                                     ::EFI_SOCKET_PROTOCOL instance
 | |
|                                     associated with the new socket.
 | |
|   @param[out]     pErrno            Address to receive the errno value upon completion.
 | |
| 
 | |
|   @retval EFI_SUCCESS   New connection successfully created
 | |
|   @retval EFI_NOT_READY No connection is available
 | |
|  **/
 | |
| EFI_STATUS
 | |
| EslSocketAccept (
 | |
|   IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
 | |
|   IN struct sockaddr * pSockAddr,
 | |
|   IN OUT socklen_t * pSockAddrLength,
 | |
|   IN EFI_SOCKET_PROTOCOL ** ppSocketProtocol,
 | |
|   IN int * pErrno
 | |
|   )
 | |
| {
 | |
|   ESL_SOCKET * pNewSocket;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
|   EFI_TPL TplPrevious;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Assume success
 | |
|   //
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   //
 | |
|   //  Validate the socket
 | |
|   //
 | |
|   pSocket = NULL;
 | |
|   pNewSocket = NULL;
 | |
|   if ( NULL != pSocketProtocol ) {
 | |
|     pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
 | |
| 
 | |
|     //
 | |
|     //  Verify the API
 | |
|     //
 | |
|     if ( NULL == pSocket->pApi->pfnAccept ) {
 | |
|       Status = EFI_UNSUPPORTED;
 | |
|       pSocket->errno = ENOTSUP;
 | |
|     }
 | |
|     else {
 | |
|       //
 | |
|       //  Validate the sockaddr
 | |
|       //
 | |
|       if (( NULL != pSockAddr )
 | |
|         && ( NULL == pSockAddrLength )) {
 | |
|         DEBUG (( DEBUG_ACCEPT,
 | |
|                   "ERROR - pSockAddr is NULL!\r\n" ));
 | |
|         Status = EFI_INVALID_PARAMETER;
 | |
|         pSocket->errno = EFAULT;
 | |
|       }
 | |
|       else {
 | |
|         //
 | |
|         //  Synchronize with the socket layer
 | |
|         //
 | |
|         RAISE_TPL ( TplPrevious, TPL_SOCKETS );
 | |
| 
 | |
|         //
 | |
|         //  Verify that the socket is in the listen state
 | |
|         //
 | |
|         if ( SOCKET_STATE_LISTENING != pSocket->State ) {
 | |
|           DEBUG (( DEBUG_ACCEPT,
 | |
|                     "ERROR - Socket is not listening!\r\n" ));
 | |
|           if ( NULL == pSocket->pApi->pfnAccept ) {
 | |
|             //
 | |
|             //  Socket does not support listen
 | |
|             //
 | |
|             pSocket->errno = EOPNOTSUPP;
 | |
|             Status = EFI_UNSUPPORTED;
 | |
|           }
 | |
|           else {
 | |
|             //
 | |
|             //  Socket supports listen, but not in listen state
 | |
|             //
 | |
|             pSocket->errno = EINVAL;
 | |
|             Status = EFI_NOT_STARTED;
 | |
|           }
 | |
|         }
 | |
|         else {
 | |
|           //
 | |
|           //  Determine if a socket is available
 | |
|           //
 | |
|           if ( 0 == pSocket->FifoDepth ) {
 | |
|             //
 | |
|             //  No connections available
 | |
|             //  Determine if any ports are available
 | |
|             //
 | |
|             if ( NULL == pSocket->pPortList ) {
 | |
|               //
 | |
|               //  No ports available
 | |
|               //
 | |
|               Status = EFI_DEVICE_ERROR;
 | |
|               pSocket->errno = EINVAL;
 | |
| 
 | |
|               //
 | |
|               //  Update the socket state
 | |
|               //
 | |
|               pSocket->State = SOCKET_STATE_NO_PORTS;
 | |
|             }
 | |
|             else {
 | |
|               //
 | |
|               //  Ports are available
 | |
|               //  No connection requests at this time
 | |
|               //
 | |
|               Status = EFI_NOT_READY;
 | |
|               pSocket->errno = EAGAIN;
 | |
|             }
 | |
|           }
 | |
|           else {
 | |
| 
 | |
|             //
 | |
|             //  Attempt to accept the connection and
 | |
|             //  get the remote network address
 | |
|             //
 | |
|             pNewSocket = pSocket->pFifoHead;
 | |
|             ASSERT ( NULL != pNewSocket );
 | |
|             Status = pSocket->pApi->pfnAccept ( pNewSocket,
 | |
|                                                 pSockAddr,
 | |
|                                                 pSockAddrLength );
 | |
|             if ( !EFI_ERROR ( Status )) {
 | |
|               //
 | |
|               //  Remove the new socket from the list
 | |
|               //
 | |
|               pSocket->pFifoHead = pNewSocket->pNextConnection;
 | |
|               if ( NULL == pSocket->pFifoHead ) {
 | |
|                 pSocket->pFifoTail = NULL;
 | |
|               }
 | |
| 
 | |
|               //
 | |
|               //  Account for this socket
 | |
|               //
 | |
|               pSocket->FifoDepth -= 1;
 | |
| 
 | |
|               //
 | |
|               //  Update the new socket's state
 | |
|               //
 | |
|               pNewSocket->State = SOCKET_STATE_CONNECTED;
 | |
|               pNewSocket->bConfigured = TRUE;
 | |
|               DEBUG (( DEBUG_ACCEPT,
 | |
|                         "0x%08x: Socket connected\r\n",
 | |
|                         pNewSocket ));
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         //  Release the socket layer synchronization
 | |
|         //
 | |
|         RESTORE_TPL ( TplPrevious );
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the new socket
 | |
|   //
 | |
|   if (( NULL != ppSocketProtocol )
 | |
|     && ( NULL != pNewSocket )) {
 | |
|     *ppSocketProtocol = &pNewSocket->SocketProtocol;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   if ( NULL != pErrno ) {
 | |
|     if ( NULL != pSocket ) {
 | |
|       *pErrno = pSocket->errno;
 | |
|     }
 | |
|     else {
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       *pErrno = ENOTSOCK;
 | |
|     }
 | |
|   }
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Allocate and initialize a ESL_SOCKET structure.
 | |
| 
 | |
|   This support function allocates an ::ESL_SOCKET structure
 | |
|   and installs a protocol on ChildHandle.  If pChildHandle is a
 | |
|   pointer to NULL, then a new handle is created and returned in
 | |
|   pChildHandle.  If pChildHandle is not a pointer to NULL, then
 | |
|   the protocol installs on the existing pChildHandle.
 | |
| 
 | |
|   @param[in,out]  pChildHandle  Pointer to the handle of the child to create.
 | |
|                                 If it is NULL, then a new handle is created.
 | |
|                                 If it is a pointer to an existing UEFI handle,
 | |
|                                 then the protocol is added to the existing UEFI
 | |
|                                 handle.
 | |
|   @param[in]      DebugFlags    Flags for debug messages
 | |
|   @param[in,out]  ppSocket      The buffer to receive an ::ESL_SOCKET structure address.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The protocol was added to ChildHandle.
 | |
|   @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
 | |
|   @retval EFI_OUT_OF_RESOURCES  There are not enough resources available to create
 | |
|                                 the child
 | |
|   @retval other                 The child handle was not created
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EslSocketAllocate (
 | |
|   IN OUT EFI_HANDLE * pChildHandle,
 | |
|   IN     UINTN DebugFlags,
 | |
|   IN OUT ESL_SOCKET ** ppSocket
 | |
|   )
 | |
| {
 | |
|   UINTN LengthInBytes;
 | |
|   ESL_LAYER * pLayer;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
|   EFI_TPL TplPrevious;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Create a socket structure
 | |
|   //
 | |
|   LengthInBytes = sizeof ( *pSocket );
 | |
|   pSocket = (ESL_SOCKET *) AllocateZeroPool ( LengthInBytes );
 | |
|   if ( NULL != pSocket ) {
 | |
|     DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT,
 | |
|               "0x%08x: Allocate pSocket, %d bytes\r\n",
 | |
|               pSocket,
 | |
|               LengthInBytes ));
 | |
| 
 | |
|     //
 | |
|     //  Initialize the socket protocol
 | |
|     //
 | |
|     pSocket->Signature = SOCKET_SIGNATURE;
 | |
|     pSocket->SocketProtocol.pfnAccept = EslSocketAccept;
 | |
|     pSocket->SocketProtocol.pfnBind = EslSocketBind;
 | |
|     pSocket->SocketProtocol.pfnClosePoll = EslSocketClosePoll;
 | |
|     pSocket->SocketProtocol.pfnCloseStart = EslSocketCloseStart;
 | |
|     pSocket->SocketProtocol.pfnConnect = EslSocketConnect;
 | |
|     pSocket->SocketProtocol.pfnGetLocal = EslSocketGetLocalAddress;
 | |
|     pSocket->SocketProtocol.pfnGetPeer = EslSocketGetPeerAddress;
 | |
|     pSocket->SocketProtocol.pfnListen = EslSocketListen;
 | |
|     pSocket->SocketProtocol.pfnOptionGet = EslSocketOptionGet;
 | |
|     pSocket->SocketProtocol.pfnOptionSet = EslSocketOptionSet;
 | |
|     pSocket->SocketProtocol.pfnPoll = EslSocketPoll;
 | |
|     pSocket->SocketProtocol.pfnReceive = EslSocketReceive;
 | |
|     pSocket->SocketProtocol.pfnShutdown = EslSocketShutdown;
 | |
|     pSocket->SocketProtocol.pfnSocket = EslSocket;
 | |
|     pSocket->SocketProtocol.pfnTransmit = EslSocketTransmit;
 | |
| 
 | |
|     pSocket->MaxRxBuf = MAX_RX_DATA;
 | |
|     pSocket->MaxTxBuf = MAX_TX_DATA;
 | |
| 
 | |
|     //
 | |
|     //  Install the socket protocol on the specified handle
 | |
|     //
 | |
|     Status = gBS->InstallMultipleProtocolInterfaces (
 | |
|                     pChildHandle,
 | |
|                     &gEfiSocketProtocolGuid,
 | |
|                     &pSocket->SocketProtocol,
 | |
|                     NULL
 | |
|                     );
 | |
|     if ( !EFI_ERROR ( Status )) {
 | |
|       DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
 | |
|                 "Installed: gEfiSocketProtocolGuid on   0x%08x\r\n",
 | |
|                 *pChildHandle ));
 | |
|       pSocket->SocketProtocol.SocketHandle = *pChildHandle;
 | |
| 
 | |
|       //
 | |
|       //  Synchronize with the socket layer
 | |
|       //
 | |
|       RAISE_TPL ( TplPrevious, TPL_SOCKETS );
 | |
| 
 | |
|       //
 | |
|       //  Add this socket to the list
 | |
|       //
 | |
|       pLayer = &mEslLayer;
 | |
|       pSocket->pNext = pLayer->pSocketList;
 | |
|       pLayer->pSocketList = pSocket;
 | |
| 
 | |
|       //
 | |
|       //  Release the socket layer synchronization
 | |
|       //
 | |
|       RESTORE_TPL ( TplPrevious );
 | |
| 
 | |
|       //
 | |
|       //  Return the socket structure address
 | |
|       //
 | |
|       *ppSocket = pSocket;
 | |
|     }
 | |
|     else {
 | |
|       DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL | DEBUG_INIT,
 | |
|                 "ERROR - Failed to install gEfiSocketProtocolGuid on 0x%08x, Status: %r\r\n",
 | |
|                 *pChildHandle,
 | |
|                 Status ));
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     //  Release the socket if necessary
 | |
|     //
 | |
|     if ( EFI_ERROR ( Status )) {
 | |
|       gBS->FreePool ( pSocket );
 | |
|       DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT,
 | |
|                 "0x%08x: Free pSocket, %d bytes\r\n",
 | |
|                 pSocket,
 | |
|                 sizeof ( *pSocket )));
 | |
|       pSocket = NULL;
 | |
|     }
 | |
|   }
 | |
|   else {
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Bind a name to a socket.
 | |
| 
 | |
|   This routine calls the network specific layer to save the network
 | |
|   address of the local connection point.
 | |
| 
 | |
|   The ::bind routine calls this routine to connect a name
 | |
|   (network address and port) to a socket on the local machine.
 | |
| 
 | |
|   @param[in]  pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure.
 | |
|   @param[in]  pSockAddr       Address of a sockaddr structure that contains the
 | |
|                               connection point on the local machine.  An IPv4 address
 | |
|                               of INADDR_ANY specifies that the connection is made to
 | |
|                               all of the network stacks on the platform.  Specifying a
 | |
|                               specific IPv4 address restricts the connection to the
 | |
|                               network stack supporting that address.  Specifying zero
 | |
|                               for the port causes the network layer to assign a port
 | |
|                               number from the dynamic range.  Specifying a specific
 | |
|                               port number causes the network layer to use that port.
 | |
|   @param[in]  SockAddrLength  Specifies the length in bytes of the sockaddr structure.
 | |
|   @param[out] pErrno          Address to receive the errno value upon completion.
 | |
| 
 | |
|   @retval EFI_SUCCESS - Socket successfully created
 | |
| **/
 | |
| EFI_STATUS
 | |
| EslSocketBind (
 | |
|   IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
 | |
|   IN CONST struct sockaddr * pSockAddr,
 | |
|   IN socklen_t SockAddrLength,
 | |
|   OUT int * pErrno
 | |
|   )
 | |
| {
 | |
|   EFI_HANDLE ChildHandle;
 | |
|   UINT8 * pBuffer;
 | |
|   ESL_PORT * pPort;
 | |
|   ESL_SERVICE ** ppServiceListHead;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   ESL_SERVICE * pService;
 | |
|   EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding;
 | |
|   EFI_STATUS Status;
 | |
|   EFI_TPL TplPrevious;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Assume success
 | |
|   //
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   //
 | |
|   //  Validate the socket
 | |
|   //
 | |
|   pSocket = NULL;
 | |
|   if ( NULL != pSocketProtocol ) {
 | |
|     pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
 | |
| 
 | |
|     //
 | |
|     //  Validate the structure pointer
 | |
|     //
 | |
|     pSocket->errno = 0;
 | |
|     if ( NULL == pSockAddr ) {
 | |
|       DEBUG (( DEBUG_BIND,
 | |
|                 "ERROR - pSockAddr is NULL!\r\n" ));
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       pSocket->errno = EFAULT;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     //  Validate the local address length
 | |
|     //
 | |
|     else if ( SockAddrLength < pSocket->pApi->MinimumAddressLength ) {
 | |
|       DEBUG (( DEBUG_BIND,
 | |
|                 "ERROR - Invalid bind name length: %d\r\n",
 | |
|                 SockAddrLength ));
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       pSocket->errno = EINVAL;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     //  Validate the shutdown state
 | |
|     //
 | |
|     else if ( pSocket->bRxDisable || pSocket->bTxDisable ) {
 | |
|       DEBUG (( DEBUG_BIND,
 | |
|                 "ERROR - Shutdown has been called on socket 0x%08x\r\n",
 | |
|                 pSocket ));
 | |
|       pSocket->errno = EINVAL;
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     //  Verify the socket state
 | |
|     //
 | |
|     else if ( SOCKET_STATE_NOT_CONFIGURED != pSocket->State ) {
 | |
|       DEBUG (( DEBUG_BIND,
 | |
|                 "ERROR - The socket 0x%08x is already configured!\r\n",
 | |
|                 pSocket ));
 | |
|       pSocket->errno = EINVAL;
 | |
|       Status = EFI_ALREADY_STARTED;
 | |
|     }
 | |
|     else {
 | |
|       //
 | |
|       //  Synchronize with the socket layer
 | |
|       //
 | |
|       RAISE_TPL ( TplPrevious, TPL_SOCKETS );
 | |
| 
 | |
|       //
 | |
|       //  Assume no ports are available
 | |
|       //
 | |
|       pSocket->errno = EADDRNOTAVAIL;
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
| 
 | |
|       //
 | |
|       //  Walk the list of services
 | |
|       //
 | |
|       pBuffer = (UINT8 *)&mEslLayer;
 | |
|       pBuffer = &pBuffer[ pSocket->pApi->ServiceListOffset ];
 | |
|       ppServiceListHead = (ESL_SERVICE **)pBuffer;
 | |
|       pService = *ppServiceListHead;
 | |
|       while ( NULL != pService ) {
 | |
|         //
 | |
|         //  Create the port
 | |
|         //
 | |
|         pServiceBinding = pService->pServiceBinding;
 | |
|         ChildHandle = NULL;
 | |
|         Status = pServiceBinding->CreateChild ( pServiceBinding,
 | |
|                                                 &ChildHandle );
 | |
|         if ( !EFI_ERROR ( Status )) {
 | |
|           DEBUG (( DEBUG_BIND | DEBUG_POOL,
 | |
|                     "0x%08x: %s port handle created\r\n",
 | |
|                     ChildHandle,
 | |
|                     pService->pSocketBinding->pName ));
 | |
| 
 | |
|           //
 | |
|           //  Open the port
 | |
|           //
 | |
|           Status = EslSocketPortAllocate ( pSocket,
 | |
|                                            pService,
 | |
|                                            ChildHandle,
 | |
|                                            pSockAddr,
 | |
|                                            TRUE,
 | |
|                                            DEBUG_BIND,
 | |
|                                            &pPort );
 | |
|         }
 | |
|         else {
 | |
|           DEBUG (( DEBUG_BIND | DEBUG_POOL,
 | |
|                     "ERROR - Failed to open %s port handle, Status: %r\r\n",
 | |
|                     pService->pSocketBinding->pName,
 | |
|                     Status ));
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         //  Set the next service
 | |
|         //
 | |
|         pService = pService->pNext;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       //  Verify that at least one network connection was found
 | |
|       //
 | |
|       if ( NULL != pSocket->pPortList ) {
 | |
|         Status = EFI_SUCCESS;
 | |
|       }
 | |
|       else {
 | |
|         if ( EADDRNOTAVAIL == pSocket->errno ) {
 | |
|           DEBUG (( DEBUG_BIND | DEBUG_POOL | DEBUG_INIT,
 | |
|                     "ERROR - Socket address is not available!\r\n" ));
 | |
|         }
 | |
|         if ( EADDRINUSE == pSocket->errno ) {
 | |
|           DEBUG (( DEBUG_BIND | DEBUG_POOL | DEBUG_INIT,
 | |
|                     "ERROR - Socket address is in use!\r\n" ));
 | |
|         }
 | |
|         Status = EFI_INVALID_PARAMETER;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       //  Mark this socket as bound if successful
 | |
|       //
 | |
|       if ( !EFI_ERROR ( Status )) {
 | |
|         pSocket->State = SOCKET_STATE_BOUND;
 | |
|         pSocket->errno = 0;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       //  Release the socket layer synchronization
 | |
|       //
 | |
|       RESTORE_TPL ( TplPrevious );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   if ( NULL != pErrno ) {
 | |
|     if ( NULL != pSocket ) {
 | |
|       *pErrno = pSocket->errno;
 | |
|     }
 | |
|     else {
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       *pErrno = ENOTSOCK;
 | |
|     }
 | |
|   }
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Test the bind configuration.
 | |
| 
 | |
|   @param[in] pPort        Address of the ::ESL_PORT structure.
 | |
|   @param[in] ErrnoValue   errno value if test fails
 | |
| 
 | |
|   @retval EFI_SUCCESS   The connection was successfully established.
 | |
|   @retval Others        The connection attempt failed.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EslSocketBindTest (
 | |
|   IN ESL_PORT * pPort,
 | |
|   IN int ErrnoValue
 | |
|   )
 | |
| {
 | |
|   UINT8 * pBuffer;
 | |
|   VOID * pConfigData;
 | |
|   EFI_STATUS Status;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Locate the configuration data
 | |
|   //
 | |
|   pBuffer = (UINT8 *)pPort;
 | |
|   pBuffer = &pBuffer [ pPort->pSocket->pApi->ConfigDataOffset ];
 | |
|   pConfigData = (VOID *)pBuffer;
 | |
| 
 | |
|   //
 | |
|   //  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 errno;
 | |
|   ESL_LAYER * pLayer;
 | |
|   ESL_SOCKET * pNextSocket;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
|   EFI_TPL TplPrevious;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Assume success
 | |
|   //
 | |
|   errno = 0;
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   //
 | |
|   //  Synchronize with the socket layer
 | |
|   //
 | |
|   RAISE_TPL ( TplPrevious, TPL_SOCKETS );
 | |
| 
 | |
|   //
 | |
|   //  Locate the socket
 | |
|   //
 | |
|   pLayer = &mEslLayer;
 | |
|   pNextSocket = pLayer->pSocketList;
 | |
|   pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
 | |
|   while ( NULL != pNextSocket ) {
 | |
|     if ( pNextSocket == pSocket ) {
 | |
|       //
 | |
|       //  Determine if the socket is in the closing state
 | |
|       //
 | |
|       if ( SOCKET_STATE_CLOSED == pSocket->State ) {
 | |
|         //
 | |
|         //  Walk the list of ports
 | |
|         //
 | |
|         if ( NULL == pSocket->pPortList ) {
 | |
|           //
 | |
|           //  All the ports are closed
 | |
|           //  Close the WaitAccept event if necessary
 | |
|           //
 | |
|           if ( NULL != pSocket->WaitAccept ) {
 | |
|             Status = gBS->CloseEvent ( pSocket->WaitAccept );
 | |
|             if ( !EFI_ERROR ( Status )) {
 | |
|               DEBUG (( DEBUG_SOCKET | DEBUG_CLOSE | DEBUG_POOL,
 | |
|                         "0x%08x: Closed WaitAccept event\r\n",
 | |
|                         pSocket->WaitAccept ));
 | |
|               //
 | |
|               //  Return the transmit status
 | |
|               //
 | |
|               Status = pSocket->TxError;
 | |
|               if ( EFI_ERROR ( Status )) {
 | |
|                 pSocket->errno = EIO;
 | |
|               }
 | |
|             }
 | |
|             else {
 | |
|               DEBUG (( DEBUG_ERROR | DEBUG_SOCKET | DEBUG_CLOSE | DEBUG_POOL,
 | |
|                         "ERROR - Failed to close the WaitAccept event, Status: %r\r\n",
 | |
|                         Status ));
 | |
|               ASSERT ( EFI_SUCCESS == Status );
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         else {
 | |
|           //
 | |
|           //  At least one port is still open
 | |
|           //
 | |
|           Status = EFI_NOT_READY;
 | |
|           errno = EAGAIN;
 | |
|         }
 | |
|       }
 | |
|       else {
 | |
|         //
 | |
|         //  SocketCloseStart was not called
 | |
|         //
 | |
|         Status = EFI_NOT_STARTED;
 | |
|         errno = EPERM;
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     //  Set the next socket
 | |
|     //
 | |
|     pNextSocket = pNextSocket->pNext;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Handle the error case where the socket was already closed
 | |
|   //
 | |
|   if ( NULL == pSocket ) {
 | |
|     //
 | |
|     //  Socket not found
 | |
|     //
 | |
|     Status = EFI_NOT_FOUND;
 | |
|     errno = ENOTSOCK;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Release the socket layer synchronization
 | |
|   //
 | |
|   RESTORE_TPL ( TplPrevious );
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   if ( NULL != pErrno ) {
 | |
|     *pErrno = errno;
 | |
|   }
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Start the close operation on the socket.
 | |
| 
 | |
|   This routine calls the network specific layer to initiate the
 | |
|   close state machine.  This routine then calls the network
 | |
|   specific layer to determine if the close state machine has gone
 | |
|   to completion.  The result from this poll is returned to the
 | |
|   caller.
 | |
| 
 | |
|   The ::close routine calls this routine to start the close
 | |
|   operation which reverses the operations of the
 | |
|   ::EslSocketAllocate routine.  The close routine then polls
 | |
|   the ::EslSocketClosePoll routine to determine when the
 | |
|   socket is closed.
 | |
| 
 | |
|   @param[in] pSocketProtocol  Address of an ::EFI_SOCKET_PROTOCOL structure.
 | |
|   @param[in] bCloseNow        Boolean to control close behavior
 | |
|   @param[out] pErrno          Address to receive the errno value upon completion.
 | |
| 
 | |
|   @retval EFI_SUCCESS     Socket successfully closed
 | |
|   @retval EFI_NOT_READY   Close still in progress
 | |
|   @retval EFI_ALREADY     Close operation already in progress
 | |
|   @retval Other           Failed to close the socket
 | |
| **/
 | |
| EFI_STATUS
 | |
| EslSocketCloseStart (
 | |
|   IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
 | |
|   IN BOOLEAN bCloseNow,
 | |
|   IN int * pErrno
 | |
|   )
 | |
| {
 | |
|   int errno;
 | |
|   ESL_PORT * pNextPort;
 | |
|   ESL_PORT * pPort;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
|   EFI_TPL TplPrevious;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Assume success
 | |
|   //
 | |
|   Status = EFI_SUCCESS;
 | |
|   errno = 0;
 | |
| 
 | |
|   //
 | |
|   //  Synchronize with the socket layer
 | |
|   //
 | |
|   RAISE_TPL ( TplPrevious, TPL_SOCKETS );
 | |
| 
 | |
|   //
 | |
|   //  Determine if the socket is already closed
 | |
|   //
 | |
|   pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
 | |
|   if ( SOCKET_STATE_CLOSED > pSocket->State ) {
 | |
|     //
 | |
|     //  Update the socket state
 | |
|     //
 | |
|     pSocket->State = SOCKET_STATE_CLOSED;
 | |
| 
 | |
|     //
 | |
|     //  Walk the list of ports
 | |
|     //
 | |
|     pPort = pSocket->pPortList;
 | |
|     while ( NULL != pPort ) {
 | |
|       //
 | |
|       //  Start closing the ports
 | |
|       //
 | |
|       pNextPort = pPort->pLinkSocket;
 | |
|       Status = EslSocketPortCloseStart ( pPort,
 | |
|                                          bCloseNow,
 | |
|                                          DEBUG_CLOSE | DEBUG_LISTEN | DEBUG_CONNECTION );
 | |
|       if (( EFI_SUCCESS != Status )
 | |
|         && ( EFI_NOT_READY != Status )) {
 | |
|         errno = EIO;
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       //  Set the next port
 | |
|       //
 | |
|       pPort = pNextPort;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     //  Attempt to finish closing the socket
 | |
|     //
 | |
|     if ( NULL == pPort ) {
 | |
|       Status = EslSocketClosePoll ( pSocketProtocol, &errno );
 | |
|     }
 | |
|   }
 | |
|   else {
 | |
|     Status = EFI_NOT_READY;
 | |
|     errno = EAGAIN;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Release the socket layer synchronization
 | |
|   //
 | |
|   RESTORE_TPL ( TplPrevious );
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   if ( NULL != pErrno ) {
 | |
|     *pErrno = errno;
 | |
|   }
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Connect to a remote system via the network.
 | |
| 
 | |
|   This routine calls the network specific layer to establish
 | |
|   the remote system address and establish the connection to
 | |
|   the remote system.
 | |
| 
 | |
|   The ::connect routine calls this routine to establish a
 | |
|   connection with the specified remote system.  This routine
 | |
|   is designed to be polled by the connect routine for completion
 | |
|   of the network connection.
 | |
| 
 | |
|   @param[in]  pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure.
 | |
|   @param[in]  pSockAddr       Network address of the remote system.
 | |
|   @param[in]  SockAddrLength  Length in bytes of the network address.
 | |
|   @param[out] pErrno          Address to receive the errno value upon completion.
 | |
| 
 | |
|   @retval EFI_SUCCESS     The connection was successfully established.
 | |
|   @retval EFI_NOT_READY   The connection is in progress, call this routine again.
 | |
|   @retval Others          The connection attempt failed.
 | |
|  **/
 | |
| EFI_STATUS
 | |
| EslSocketConnect (
 | |
|   IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
 | |
|   IN const struct sockaddr * pSockAddr,
 | |
|   IN socklen_t SockAddrLength,
 | |
|   IN int * pErrno
 | |
|   )
 | |
| {
 | |
|   struct sockaddr_in6 LocalAddress;
 | |
|   ESL_PORT * pPort;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
|   EFI_TPL TplPrevious;
 | |
| 
 | |
|   DEBUG (( DEBUG_CONNECT, "Entering SocketConnect\r\n" ));
 | |
| 
 | |
|   //
 | |
|   //  Assume success
 | |
|   //
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   //
 | |
|   //  Validate the socket
 | |
|   //
 | |
|   pSocket = NULL;
 | |
|   if ( NULL != pSocketProtocol ) {
 | |
|     pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
 | |
| 
 | |
|     //
 | |
|     //  Validate the name length
 | |
|     //
 | |
|     if ( SockAddrLength < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data ))) {
 | |
|       DEBUG (( DEBUG_CONNECT,
 | |
|                 "ERROR - Invalid bind name length: %d\r\n",
 | |
|                 SockAddrLength ));
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       pSocket->errno = EINVAL;
 | |
|     }
 | |
|     else {
 | |
|       //
 | |
|       //  Assume success
 | |
|       //
 | |
|       pSocket->errno = 0;
 | |
| 
 | |
|       //
 | |
|       //  Synchronize with the socket layer
 | |
|       //
 | |
|       RAISE_TPL ( TplPrevious, TPL_SOCKETS );
 | |
| 
 | |
|       //
 | |
|       //  Validate the socket state
 | |
|       //
 | |
|       switch ( pSocket->State ) {
 | |
|       default:
 | |
|         //
 | |
|         //  Wrong socket state
 | |
|         //
 | |
|         pSocket->errno = EIO;
 | |
|         Status = EFI_DEVICE_ERROR;
 | |
|         break;
 | |
| 
 | |
|       case SOCKET_STATE_NOT_CONFIGURED:
 | |
|       case SOCKET_STATE_BOUND:
 | |
|         //
 | |
|         //  Validate the address length
 | |
|         //
 | |
|         if ( SockAddrLength >= pSocket->pApi->MinimumAddressLength ) {
 | |
|           //
 | |
|           //  Verify the API
 | |
|           //
 | |
|           if ( NULL == pSocket->pApi->pfnRemoteAddrSet ) {
 | |
|             //
 | |
|             //  Already connected
 | |
|             //
 | |
|             pSocket->errno = ENOTSUP;
 | |
|             Status = EFI_UNSUPPORTED;
 | |
|           }
 | |
|           else {
 | |
|             //
 | |
|             //  Determine if BIND was already called
 | |
|             //
 | |
|             if ( NULL == pSocket->pPortList ) {
 | |
|               //
 | |
|               //  Allow any local port
 | |
|               //
 | |
|               ZeroMem ( &LocalAddress, sizeof ( LocalAddress ));
 | |
|               LocalAddress.sin6_len = (uint8_t)pSocket->pApi->MinimumAddressLength;
 | |
|               LocalAddress.sin6_family = pSocket->pApi->AddressFamily;
 | |
|               Status = EslSocketBind ( &pSocket->SocketProtocol,
 | |
|                                        (struct sockaddr *)&LocalAddress,
 | |
|                                        LocalAddress.sin6_len,
 | |
|                                        &pSocket->errno );
 | |
|             }
 | |
|             if ( NULL != pSocket->pPortList ) {
 | |
|               //
 | |
|               //  Walk the list of ports
 | |
|               //
 | |
|               pPort = pSocket->pPortList;
 | |
|               while ( NULL != pPort ) {
 | |
|                 //
 | |
|                 //  Set the remote address
 | |
|                 //
 | |
|                 Status = pSocket->pApi->pfnRemoteAddrSet ( pPort,
 | |
|                                                            pSockAddr,
 | |
|                                                            SockAddrLength );
 | |
|                 if ( EFI_ERROR ( Status )) {
 | |
|                   break;
 | |
|                 }
 | |
| 
 | |
|                 //
 | |
|                 //  Set the next port
 | |
|                 //
 | |
|                 pPort = pPort->pLinkSocket;
 | |
|               }
 | |
| 
 | |
|               //
 | |
|               //  Verify the API
 | |
|               //
 | |
|               if (( !EFI_ERROR ( Status ))
 | |
|                 && ( NULL != pSocket->pApi->pfnConnectStart )) {
 | |
|                 //
 | |
|                 //  Initiate the connection with the remote system
 | |
|                 //
 | |
|                 Status = pSocket->pApi->pfnConnectStart ( pSocket );
 | |
| 
 | |
|                 //
 | |
|                 //  Set the next state if connecting
 | |
|                 //
 | |
|                 if ( EFI_NOT_READY == Status ) {
 | |
|                   pSocket->State = SOCKET_STATE_CONNECTING;
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         else {
 | |
|           DEBUG (( DEBUG_CONNECT,
 | |
|                     "ERROR - Invalid address length: %d\r\n",
 | |
|                     SockAddrLength ));
 | |
|           Status = EFI_INVALID_PARAMETER;
 | |
|           pSocket->errno = EINVAL;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       case SOCKET_STATE_CONNECTING:
 | |
|         //
 | |
|         //  Poll the network adapter
 | |
|         //
 | |
|         EslSocketRxPoll ( pSocket );
 | |
| 
 | |
|         //
 | |
|         //  Poll for connection completion
 | |
|         //
 | |
|         if ( NULL == pSocket->pApi->pfnConnectPoll ) {
 | |
|           //
 | |
|           //  Already connected
 | |
|           //
 | |
|           pSocket->errno = EISCONN;
 | |
|           Status = EFI_ALREADY_STARTED;
 | |
|         }
 | |
|         else {
 | |
|           Status = pSocket->pApi->pfnConnectPoll ( pSocket );
 | |
| 
 | |
|           //
 | |
|           //  Set the next state if connected
 | |
|           //
 | |
|           if ( EFI_NOT_READY != Status ) {
 | |
|             if ( EFI_ERROR ( Status )) {
 | |
|               pSocket->State = SOCKET_STATE_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 errno;
 | |
|   ESL_LAYER * pLayer;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   ESL_SOCKET * pSocketPrevious;
 | |
|   EFI_STATUS Status;
 | |
|   EFI_TPL TplPrevious;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Assume failure
 | |
|   //
 | |
|   errno = EIO;
 | |
|   pSocket = NULL;
 | |
|   Status = EFI_INVALID_PARAMETER;
 | |
| 
 | |
|   //
 | |
|   //  Validate the socket
 | |
|   //
 | |
|   pLayer = &mEslLayer;
 | |
|   if ( NULL != pSocketProtocol ) {
 | |
|     pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
 | |
| 
 | |
|     //
 | |
|     //  Synchronize with the socket layer
 | |
|     //
 | |
|     RAISE_TPL ( TplPrevious, TPL_SOCKETS );
 | |
| 
 | |
|     //
 | |
|     //  Walk the socket list
 | |
|     //
 | |
|     pSocketPrevious = pLayer->pSocketList;
 | |
|     if ( NULL != pSocketPrevious ) {
 | |
|       if ( pSocket == pSocketPrevious ) {
 | |
|         //
 | |
|         //  Remove the socket from the head of the list
 | |
|         //
 | |
|         pLayer->pSocketList = pSocket->pNext;
 | |
|       }
 | |
|       else {
 | |
|         //
 | |
|         //  Find the socket in the middle of the list
 | |
|         //
 | |
|         while (( NULL != pSocketPrevious )
 | |
|           && ( pSocket != pSocketPrevious->pNext )) {
 | |
|           //
 | |
|           //  Set the next socket
 | |
|           //
 | |
|           pSocketPrevious = pSocketPrevious->pNext;
 | |
|         }
 | |
|         if ( NULL != pSocketPrevious ) {
 | |
|           //
 | |
|           //  Remove the socket from the middle of the list
 | |
|           //
 | |
|           pSocketPrevious = pSocket->pNext;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       DEBUG (( DEBUG_ERROR | DEBUG_POOL,
 | |
|                 "ERROR - Socket list is empty!\r\n" ));
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     //  Release the socket layer synchronization
 | |
|     //
 | |
|     RESTORE_TPL ( TplPrevious );
 | |
| 
 | |
|     //
 | |
|     //  Determine if the socket was found
 | |
|     //
 | |
|     if ( NULL != pSocketPrevious ) {
 | |
|       pSocket->pNext = NULL;
 | |
| 
 | |
|       //
 | |
|       //  Remove the socket protocol
 | |
|       //
 | |
|       ChildHandle = pSocket->SocketProtocol.SocketHandle;
 | |
|       Status = gBS->UninstallMultipleProtocolInterfaces (
 | |
|                 ChildHandle,
 | |
|                 &gEfiSocketProtocolGuid,
 | |
|                 &pSocket->SocketProtocol,
 | |
|                 NULL );
 | |
|       if ( !EFI_ERROR ( Status )) {
 | |
|         DEBUG (( DEBUG_POOL | DEBUG_INFO,
 | |
|                     "Removed:   gEfiSocketProtocolGuid from 0x%08x\r\n",
 | |
|                     ChildHandle ));
 | |
| 
 | |
|         //
 | |
|         //  Free the socket structure
 | |
|         //
 | |
|         Status = gBS->FreePool ( pSocket );
 | |
|         if ( !EFI_ERROR ( Status )) {
 | |
|           DEBUG (( DEBUG_POOL,
 | |
|                     "0x%08x: Free pSocket, %d bytes\r\n",
 | |
|                     pSocket,
 | |
|                     sizeof ( *pSocket )));
 | |
|           errno = 0;
 | |
|         }
 | |
|         else {
 | |
|           DEBUG (( DEBUG_ERROR | DEBUG_POOL,
 | |
|                     "ERROR - Failed to free pSocket 0x%08x, Status: %r\r\n",
 | |
|                     pSocket,
 | |
|                     Status ));
 | |
|         }
 | |
|       }
 | |
|       else {
 | |
|         DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INFO,
 | |
|                     "ERROR - Failed to remove gEfiSocketProtocolGuid from 0x%08x, Status: %r\r\n",
 | |
|                     ChildHandle,
 | |
|                     Status ));
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       DEBUG (( DEBUG_ERROR | DEBUG_INFO,
 | |
|                 "ERROR - The socket was not in the socket list!\r\n" ));
 | |
|       Status = EFI_NOT_FOUND;
 | |
|     }
 | |
|   }
 | |
|   else {
 | |
|     DEBUG (( DEBUG_ERROR,
 | |
|               "ERROR - Invalid parameter pSocketProtocol is NULL\r\n" ));
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the errno value if possible
 | |
|   //
 | |
|   if ( NULL != pErrno ) {
 | |
|     *pErrno = errno;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Get the local address.
 | |
| 
 | |
|   This routine calls the network specific layer to get the network
 | |
|   address of the local host connection point.
 | |
| 
 | |
|   The ::getsockname routine calls this routine to obtain the network
 | |
|   address associated with the local host connection point.
 | |
| 
 | |
|   @param[in]      pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure.
 | |
|   @param[out]     pAddress        Network address to receive the local system address
 | |
|   @param[in,out]  pAddressLength  Length of the local network address structure
 | |
|   @param[out]     pErrno          Address to receive the errno value upon completion.
 | |
| 
 | |
|   @retval EFI_SUCCESS - Local address successfully returned
 | |
|  **/
 | |
| EFI_STATUS
 | |
| EslSocketGetLocalAddress (
 | |
|   IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
 | |
|   OUT struct sockaddr * pAddress,
 | |
|   IN OUT socklen_t * pAddressLength,
 | |
|   IN int * pErrno
 | |
|   )
 | |
| {
 | |
|   socklen_t LengthInBytes;
 | |
|   ESL_PORT * pPort;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
|   EFI_TPL TplPrevious;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Assume success
 | |
|   //
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   //
 | |
|   //  Validate the socket
 | |
|   //
 | |
|   pSocket = NULL;
 | |
|   if ( NULL != pSocketProtocol ) {
 | |
|     pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
 | |
| 
 | |
|     //
 | |
|     //  Verify the socket state
 | |
|     //
 | |
|     EslSocketIsConfigured ( pSocket );
 | |
|     if ( pSocket->bAddressSet ) {
 | |
|       //
 | |
|       //  Verify the address buffer and length address
 | |
|       //
 | |
|       if (( NULL != pAddress ) && ( NULL != pAddressLength )) {
 | |
|         //
 | |
|         //  Verify the API
 | |
|         //
 | |
|         if ( NULL == pSocket->pApi->pfnLocalAddrGet ) {
 | |
|           Status = EFI_UNSUPPORTED;
 | |
|           pSocket->errno = ENOTSUP;
 | |
|         }
 | |
|         else {
 | |
|           //
 | |
|           //  Synchronize with the socket layer
 | |
|           //
 | |
|           RAISE_TPL ( TplPrevious, TPL_SOCKETS );
 | |
| 
 | |
|           //
 | |
|           //  Verify that there is just a single connection
 | |
|           //
 | |
|           pPort = pSocket->pPortList;
 | |
|           if ( NULL != pPort ) {
 | |
|             //
 | |
|             //  Verify the address length
 | |
|             //
 | |
|             LengthInBytes = pSocket->pApi->AddressLength;
 | |
|             if (( LengthInBytes <= *pAddressLength )
 | |
|               && ( 255 >= LengthInBytes )) {
 | |
|               //
 | |
|               //  Return the local address and address length
 | |
|               //
 | |
|               ZeroMem ( pAddress, LengthInBytes );
 | |
|               pAddress->sa_len = (uint8_t)LengthInBytes;
 | |
|               *pAddressLength = pAddress->sa_len;
 | |
|               pSocket->pApi->pfnLocalAddrGet ( pPort, pAddress );
 | |
|               pSocket->errno = 0;
 | |
|               Status = EFI_SUCCESS;
 | |
|             }
 | |
|             else {
 | |
|               pSocket->errno = EINVAL;
 | |
|               Status = EFI_INVALID_PARAMETER;
 | |
|             }
 | |
|           }
 | |
|           else {
 | |
|             pSocket->errno = ENOTCONN;
 | |
|             Status = EFI_NOT_STARTED;
 | |
|           }
 | |
| 
 | |
|           //
 | |
|           //  Release the socket layer synchronization
 | |
|           //
 | |
|           RESTORE_TPL ( TplPrevious );
 | |
|         }
 | |
|       }
 | |
|       else {
 | |
|         pSocket->errno = EINVAL;
 | |
|         Status = EFI_INVALID_PARAMETER;
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       //
 | |
|       //  Address not set
 | |
|       //
 | |
|       Status = EFI_NOT_STARTED;
 | |
|       pSocket->errno = EADDRNOTAVAIL;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   if ( NULL != pErrno ) {
 | |
|     if ( NULL != pSocket ) {
 | |
|       *pErrno = pSocket->errno;
 | |
|     }
 | |
|     else {
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       *pErrno = ENOTSOCK;
 | |
|     }
 | |
|   }
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Get the peer address.
 | |
| 
 | |
|   This routine calls the network specific layer to get the remote
 | |
|   system connection point.
 | |
| 
 | |
|   The ::getpeername routine calls this routine to obtain the network
 | |
|   address of the remote connection point.
 | |
| 
 | |
|   @param[in]      pSocketProtocol   Address of an ::EFI_SOCKET_PROTOCOL structure.
 | |
|   @param[out]     pAddress          Network address to receive the remote system address
 | |
|   @param[in,out]  pAddressLength    Length of the remote network address structure
 | |
|   @param[out]     pErrno            Address to receive the errno value upon completion.
 | |
| 
 | |
|   @retval EFI_SUCCESS - Remote address successfully returned
 | |
|  **/
 | |
| EFI_STATUS
 | |
| EslSocketGetPeerAddress (
 | |
|   IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
 | |
|   OUT struct sockaddr * pAddress,
 | |
|   IN OUT socklen_t * pAddressLength,
 | |
|   IN int * pErrno
 | |
|   )
 | |
| {
 | |
|   socklen_t LengthInBytes;
 | |
|   ESL_PORT * pPort;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
|   EFI_TPL TplPrevious;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Assume success
 | |
|   //
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   //
 | |
|   //  Validate the socket
 | |
|   //
 | |
|   pSocket = NULL;
 | |
|   if ( NULL != pSocketProtocol ) {
 | |
|     pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
 | |
| 
 | |
|     //
 | |
|     //  Verify the socket state
 | |
|     //
 | |
|     Status = EslSocketIsConfigured ( pSocket );
 | |
|     if ( !EFI_ERROR ( Status )) {
 | |
|       //
 | |
|       //  Verify the API
 | |
|       //
 | |
|       if ( NULL == pSocket->pApi->pfnRemoteAddrGet ) {
 | |
|         Status = EFI_UNSUPPORTED;
 | |
|         pSocket->errno = ENOTSUP;
 | |
|       }
 | |
|       else {
 | |
|         //
 | |
|         //  Verify the address buffer and length address
 | |
|         //
 | |
|         if (( NULL != pAddress ) && ( NULL != pAddressLength )) {
 | |
|           //
 | |
|           //  Verify the socket state
 | |
|           //
 | |
|           if ( SOCKET_STATE_CONNECTED == pSocket->State ) {
 | |
|             //
 | |
|             //  Synchronize with the socket layer
 | |
|             //
 | |
|             RAISE_TPL ( TplPrevious, TPL_SOCKETS );
 | |
| 
 | |
|             //
 | |
|             //  Verify that there is just a single connection
 | |
|             //
 | |
|             pPort = pSocket->pPortList;
 | |
|             if (( NULL != pPort ) && ( NULL == pPort->pLinkSocket )) {
 | |
|               //
 | |
|               //  Verify the address length
 | |
|               //
 | |
|               LengthInBytes = pSocket->pApi->AddressLength;
 | |
|               if ( LengthInBytes <= *pAddressLength ) {
 | |
|                 //
 | |
|                 //  Return the local address
 | |
|                 //
 | |
|                 ZeroMem ( pAddress, LengthInBytes );
 | |
|                 pAddress->sa_len = (uint8_t)LengthInBytes;
 | |
|                 *pAddressLength = pAddress->sa_len;
 | |
|                 pSocket->pApi->pfnRemoteAddrGet ( pPort, pAddress );
 | |
|                 pSocket->errno = 0;
 | |
|                 Status = EFI_SUCCESS;
 | |
|               }
 | |
|               else {
 | |
|                 pSocket->errno = EINVAL;
 | |
|                 Status = EFI_INVALID_PARAMETER;
 | |
|               }
 | |
|             }
 | |
|             else {
 | |
|               pSocket->errno = ENOTCONN;
 | |
|               Status = EFI_NOT_STARTED;
 | |
|             }
 | |
| 
 | |
|             //
 | |
|             //  Release the socket layer synchronization
 | |
|             //
 | |
|             RESTORE_TPL ( TplPrevious );
 | |
|           }
 | |
|           else {
 | |
|             pSocket->errno = ENOTCONN;
 | |
|             Status = EFI_NOT_STARTED;
 | |
|           }
 | |
|         }
 | |
|         else {
 | |
|           pSocket->errno = EINVAL;
 | |
|           Status = EFI_INVALID_PARAMETER;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   if ( NULL != pErrno ) {
 | |
|     if ( NULL != pSocket ) {
 | |
|       *pErrno = pSocket->errno;
 | |
|     }
 | |
|     else {
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       *pErrno = ENOTSOCK;
 | |
|     }
 | |
|   }
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Free the ESL_IO_MGMT event and structure.
 | |
| 
 | |
|   This support routine walks the free list to close the event in
 | |
|   the ESL_IO_MGMT structure and remove the structure from the free
 | |
|   list.
 | |
| 
 | |
|   See the \ref TransmitEngine section.
 | |
| 
 | |
|   @param[in]  pPort         Address of an ::ESL_PORT structure
 | |
|   @param[in]  ppFreeQueue   Address of the free queue head
 | |
|   @param[in]  DebugFlags    Flags for debug messages
 | |
|   @param[in]  pEventName    Zero terminated string containing the event name
 | |
| 
 | |
|   @retval EFI_SUCCESS - The structures were properly initialized
 | |
| **/
 | |
| EFI_STATUS
 | |
| EslSocketIoFree (
 | |
|   IN ESL_PORT * pPort,
 | |
|   IN ESL_IO_MGMT ** ppFreeQueue,
 | |
|   IN UINTN DebugFlags,
 | |
|   IN CHAR8 * pEventName
 | |
|   )
 | |
| {
 | |
|   UINT8 * pBuffer;
 | |
|   EFI_EVENT * pEvent;
 | |
|   ESL_IO_MGMT * pIo;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Assume success
 | |
|   //
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   //
 | |
|   //  Walk the list of IO structures
 | |
|   //
 | |
|   pSocket = pPort->pSocket;
 | |
|   while ( *ppFreeQueue ) {
 | |
|     //
 | |
|     //  Free the event for this structure
 | |
|     //
 | |
|     pIo = *ppFreeQueue;
 | |
|     pBuffer = (UINT8 *)pIo;
 | |
|     pBuffer = &pBuffer[ pSocket->TxTokenEventOffset ];
 | |
|     pEvent = (EFI_EVENT *)pBuffer;
 | |
|     Status = gBS->CloseEvent ( *pEvent );
 | |
|     if ( EFI_ERROR ( Status )) {
 | |
|       DEBUG (( DEBUG_ERROR | DebugFlags,
 | |
|                 "ERROR - Failed to close the %a event, Status: %r\r\n",
 | |
|                 pEventName,
 | |
|                 Status ));
 | |
|       pSocket->errno = ENOMEM;
 | |
|       break;
 | |
|     }
 | |
|     DEBUG (( DebugFlags,
 | |
|               "0x%08x: Closed %a event 0x%08x\r\n",
 | |
|               pIo,
 | |
|               pEventName,
 | |
|               *pEvent ));
 | |
| 
 | |
|     //
 | |
|     //  Remove this structure from the queue
 | |
|     //
 | |
|     *ppFreeQueue = pIo->pNext;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Initialize the ESL_IO_MGMT structures.
 | |
| 
 | |
|   This support routine initializes the ESL_IO_MGMT structure and
 | |
|   places them on to a free list.
 | |
| 
 | |
|   This routine is called by ::EslSocketPortAllocate routines to prepare
 | |
|   the transmit engines.  See the \ref TransmitEngine section.
 | |
| 
 | |
|   @param[in]        pPort         Address of an ::ESL_PORT structure
 | |
|   @param[in, out]   ppIo          Address containing the first structure address.  Upon
 | |
|                                   return this buffer contains the next structure address.
 | |
|   @param[in]        TokenCount    Number of structures to initialize
 | |
|   @param[in]        ppFreeQueue   Address of the free queue head
 | |
|   @param[in]        DebugFlags    Flags for debug messages
 | |
|   @param[in]        pEventName    Zero terminated string containing the event name
 | |
|   @param[in]        pfnCompletion Completion routine address
 | |
| 
 | |
|   @retval EFI_SUCCESS - The structures were properly initialized
 | |
| **/
 | |
| EFI_STATUS
 | |
| EslSocketIoInit (
 | |
|   IN ESL_PORT * pPort,
 | |
|   IN ESL_IO_MGMT ** ppIo,
 | |
|   IN UINTN TokenCount,
 | |
|   IN ESL_IO_MGMT ** ppFreeQueue,
 | |
|   IN UINTN DebugFlags,
 | |
|   IN CHAR8 * pEventName,
 | |
|   IN PFN_API_IO_COMPLETE pfnCompletion
 | |
|   )
 | |
| {
 | |
|   ESL_IO_MGMT * pEnd;
 | |
|   EFI_EVENT * pEvent;
 | |
|   ESL_IO_MGMT * pIo;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Assume success
 | |
|   //
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   //
 | |
|   //  Walk the list of IO structures
 | |
|   //
 | |
|   pSocket = pPort->pSocket;
 | |
|   pIo = *ppIo;
 | |
|   pEnd = &pIo [ TokenCount ];
 | |
|   while ( pEnd > pIo ) {
 | |
|     //
 | |
|     //  Initialize the IO structure
 | |
|     //
 | |
|     pIo->pPort = pPort;
 | |
|     pIo->pPacket = NULL;
 | |
| 
 | |
|     //
 | |
|     //  Allocate the event for this structure
 | |
|     //
 | |
|     pEvent = (EFI_EVENT *)&(((UINT8 *)pIo)[ pSocket->TxTokenEventOffset ]);
 | |
|     Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,
 | |
|                                 TPL_SOCKETS,
 | |
|                                 (EFI_EVENT_NOTIFY)pfnCompletion,
 | |
|                                 pIo,
 | |
|                                 pEvent );
 | |
|     if ( EFI_ERROR ( Status )) {
 | |
|       DEBUG (( DEBUG_ERROR | DebugFlags,
 | |
|                 "ERROR - Failed to create the %a event, Status: %r\r\n",
 | |
|                 pEventName,
 | |
|                 Status ));
 | |
|       pSocket->errno = ENOMEM;
 | |
|       break;
 | |
|     }
 | |
|     DEBUG (( DebugFlags,
 | |
|               "0x%08x: Created %a event 0x%08x\r\n",
 | |
|               pIo,
 | |
|               pEventName,
 | |
|               *pEvent ));
 | |
| 
 | |
|     //
 | |
|     //  Add this structure to the queue
 | |
|     //
 | |
|     pIo->pNext = *ppFreeQueue;
 | |
|     *ppFreeQueue = pIo;
 | |
| 
 | |
|     //
 | |
|     //  Set the next structure
 | |
|     //
 | |
|     pIo += 1;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Save the next structure
 | |
|   //
 | |
|   *ppIo = pIo;
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Determine if the socket is configured.
 | |
| 
 | |
|   This support routine is called to determine if the socket if the
 | |
|   configuration call was made to the network layer.  The following
 | |
|   routines call this routine to verify that they may be successful
 | |
|   in their operations:
 | |
|   <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 errno;
 | |
|   socklen_t LengthInBytes;
 | |
|   socklen_t MaxBytes;
 | |
|   CONST UINT8 * pOptionData;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Assume failure
 | |
|   //
 | |
|   errno = EINVAL;
 | |
|   Status = EFI_INVALID_PARAMETER;
 | |
| 
 | |
|   //
 | |
|   //  Validate the socket
 | |
|   //
 | |
|   pSocket = NULL;
 | |
|   if ( NULL == pSocketProtocol ) {
 | |
|     DEBUG (( DEBUG_OPTION, "ERROR - pSocketProtocol is NULL!\r\n" ));
 | |
|   }
 | |
|   else if ( NULL == pOptionValue ) {
 | |
|     DEBUG (( DEBUG_OPTION, "ERROR - No option buffer specified\r\n" ));
 | |
|   }
 | |
|   else if ( NULL == pOptionLength ) {
 | |
|     DEBUG (( DEBUG_OPTION, "ERROR - Option length not specified!\r\n" ));
 | |
|   }
 | |
|   else {
 | |
|     pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
 | |
|     LengthInBytes = 0;
 | |
|     MaxBytes = *pOptionLength;
 | |
|     pOptionData = NULL;
 | |
|     switch ( level ) {
 | |
|     default:
 | |
|       //
 | |
|       //  See if the protocol will handle the option
 | |
|       //
 | |
|       if ( NULL != pSocket->pApi->pfnOptionGet ) {
 | |
|         if ( pSocket->pApi->DefaultProtocol == level ) {
 | |
|           Status = pSocket->pApi->pfnOptionGet ( pSocket,
 | |
|                                                  OptionName,
 | |
|                                                  (CONST void ** __restrict)&pOptionData,
 | |
|                                                  &LengthInBytes );
 | |
|           errno = pSocket->errno;
 | |
|           break;
 | |
|         }
 | |
|         else {
 | |
|           //
 | |
|           //  Protocol not supported
 | |
|           //
 | |
|           DEBUG (( DEBUG_OPTION,
 | |
|                     "ERROR - The socket does not support this protocol!\r\n" ));
 | |
|         }
 | |
|       }
 | |
|       else {
 | |
|         //
 | |
|         //  Protocol level not supported
 | |
|         //
 | |
|         DEBUG (( DEBUG_OPTION,
 | |
|                   "ERROR - %a does not support any options!\r\n",
 | |
|                   pSocket->pApi->pName ));
 | |
|       }
 | |
|       errno = ENOPROTOOPT;
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       break;
 | |
| 
 | |
|     case SOL_SOCKET:
 | |
|       switch ( OptionName ) {
 | |
|       default:
 | |
|         //
 | |
|         //  Socket option not supported
 | |
|         //
 | |
|         DEBUG (( DEBUG_INFO | DEBUG_OPTION, "ERROR - Invalid socket option!\r\n" ));
 | |
|         errno = EINVAL;
 | |
|         Status = EFI_INVALID_PARAMETER;
 | |
|         break;
 | |
| 
 | |
|       case SO_ACCEPTCONN:
 | |
|         //
 | |
|         //  Return the listen flag
 | |
|         //
 | |
|         pOptionData = (CONST UINT8 *)&pSocket->bListenCalled;
 | |
|         LengthInBytes = sizeof ( pSocket->bListenCalled );
 | |
|         break;
 | |
| 
 | |
|       case SO_DEBUG:
 | |
|         //
 | |
|         //  Return the debug flags
 | |
|         //
 | |
|         pOptionData = (CONST UINT8 *)&pSocket->bOobInLine;
 | |
|         LengthInBytes = sizeof ( pSocket->bOobInLine );
 | |
|         break;
 | |
| 
 | |
|       case SO_OOBINLINE:
 | |
|         //
 | |
|         //  Return the out-of-band inline flag
 | |
|         //
 | |
|         pOptionData = (CONST UINT8 *)&pSocket->bOobInLine;
 | |
|         LengthInBytes = sizeof ( pSocket->bOobInLine );
 | |
|         break;
 | |
| 
 | |
|       case SO_RCVTIMEO:
 | |
|         //
 | |
|         //  Return the receive timeout
 | |
|         //
 | |
|         pOptionData = (CONST UINT8 *)&pSocket->RxTimeout;
 | |
|         LengthInBytes = sizeof ( pSocket->RxTimeout );
 | |
|         break;
 | |
| 
 | |
|       case SO_RCVBUF:
 | |
|         //
 | |
|         //  Return the maximum receive buffer size
 | |
|         //
 | |
|         pOptionData = (CONST UINT8 *)&pSocket->MaxRxBuf;
 | |
|         LengthInBytes = sizeof ( pSocket->MaxRxBuf );
 | |
|         break;
 | |
| 
 | |
|       case SO_REUSEADDR:
 | |
|         //
 | |
|         //  Return the address reuse flag
 | |
|         //
 | |
|         pOptionData = (UINT8 *)&pSocket->bReUseAddr;
 | |
|         LengthInBytes = sizeof ( pSocket->bReUseAddr );
 | |
|         break;
 | |
| 
 | |
|       case SO_SNDBUF:
 | |
|         //
 | |
|         //  Return the maximum transmit buffer size
 | |
|         //
 | |
|         pOptionData = (CONST UINT8 *)&pSocket->MaxTxBuf;
 | |
|         LengthInBytes = sizeof ( pSocket->MaxTxBuf );
 | |
|         break;
 | |
| 
 | |
|       case SO_TYPE:
 | |
|         //
 | |
|         //  Return the socket type
 | |
|         //
 | |
|         pOptionData = (CONST UINT8 *)&pSocket->Type;
 | |
|         LengthInBytes = sizeof ( pSocket->Type );
 | |
|         break;
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     //  Return the option length
 | |
|     //
 | |
|     *pOptionLength = LengthInBytes;
 | |
| 
 | |
|     //
 | |
|     //  Determine if the option is present
 | |
|     //
 | |
|     if ( 0 != LengthInBytes ) {
 | |
|       //
 | |
|       //  Silently truncate the value length
 | |
|       //
 | |
|       if ( LengthInBytes > MaxBytes ) {
 | |
|         DEBUG (( DEBUG_OPTION,
 | |
|                   "INFO - Truncating option from %d to %d bytes\r\n",
 | |
|                   LengthInBytes,
 | |
|                   MaxBytes ));
 | |
|         LengthInBytes = MaxBytes;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       //  Return the value
 | |
|       //
 | |
|       CopyMem ( pOptionValue, pOptionData, LengthInBytes );
 | |
| 
 | |
|       //
 | |
|       //  Zero fill any remaining space
 | |
|       //
 | |
|       if ( LengthInBytes < MaxBytes ) {
 | |
|         ZeroMem ( &((UINT8 *)pOptionValue)[LengthInBytes], MaxBytes - LengthInBytes );
 | |
|       }
 | |
|       errno = 0;
 | |
|       Status = EFI_SUCCESS;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   if ( NULL != pErrno ) {
 | |
|     *pErrno = errno;
 | |
|   }
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Set the socket options.
 | |
| 
 | |
|   This routine handles the socket level options and passes the
 | |
|   others to the network specific layer.
 | |
| 
 | |
|   The ::setsockopt routine calls this routine to adjust the socket
 | |
|   options one at a time by name.
 | |
| 
 | |
|   @param[in]  pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure.
 | |
|   @param[in]  level           Option protocol level
 | |
|   @param[in]  OptionName      Name of the option
 | |
|   @param[in]  pOptionValue    Buffer containing the option value
 | |
|   @param[in]  OptionLength    Length of the buffer in bytes
 | |
|   @param[out] pErrno          Address to receive the errno value upon completion.
 | |
| 
 | |
|   @retval EFI_SUCCESS - Option successfully set
 | |
| **/
 | |
| EFI_STATUS
 | |
| EslSocketOptionSet (
 | |
|   IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
 | |
|   IN int level,
 | |
|   IN int OptionName,
 | |
|   IN CONST void * pOptionValue,
 | |
|   IN socklen_t OptionLength,
 | |
|   IN int * pErrno
 | |
|   )
 | |
| {
 | |
|   BOOLEAN bTrueFalse;
 | |
|   int errno;
 | |
|   socklen_t LengthInBytes;
 | |
|   UINT8 * pOptionData;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Assume failure
 | |
|   //
 | |
|   errno = EINVAL;
 | |
|   Status = EFI_INVALID_PARAMETER;
 | |
| 
 | |
|   //
 | |
|   //  Validate the socket
 | |
|   //
 | |
|   pSocket = NULL;
 | |
|   if ( NULL == pSocketProtocol ) {
 | |
|     DEBUG (( DEBUG_OPTION, "ERROR - pSocketProtocol is NULL!\r\n" ));
 | |
|   }
 | |
|   else if ( NULL == pOptionValue ) {
 | |
|     DEBUG (( DEBUG_OPTION, "ERROR - No option buffer specified\r\n" ));
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
 | |
|     if ( pSocket->bRxDisable || pSocket->bTxDisable ) {
 | |
|       DEBUG (( DEBUG_OPTION, "ERROR - Socket has been shutdown!\r\n" ));
 | |
|     }
 | |
|     else {
 | |
|       LengthInBytes = 0;
 | |
|       pOptionData = NULL;
 | |
|       switch ( level ) {
 | |
|       default:
 | |
|         //
 | |
|         //  See if the protocol will handle the option
 | |
|         //
 | |
|         if ( NULL != pSocket->pApi->pfnOptionSet ) {
 | |
|           if ( pSocket->pApi->DefaultProtocol == level ) {
 | |
|             Status = pSocket->pApi->pfnOptionSet ( pSocket,
 | |
|                                                    OptionName,
 | |
|                                                    pOptionValue,
 | |
|                                                    OptionLength );
 | |
|             errno = pSocket->errno;
 | |
|             break;
 | |
|           }
 | |
|           else {
 | |
|             //
 | |
|             //  Protocol not supported
 | |
|             //
 | |
|             DEBUG (( DEBUG_OPTION,
 | |
|                       "ERROR - The socket does not support this protocol!\r\n" ));
 | |
|           }
 | |
|         }
 | |
|         else {
 | |
|           //
 | |
|           //  Protocol level not supported
 | |
|           //
 | |
|           DEBUG (( DEBUG_OPTION,
 | |
|                     "ERROR - %a does not support any options!\r\n",
 | |
|                     pSocket->pApi->pName ));
 | |
|         }
 | |
|         errno = ENOPROTOOPT;
 | |
|         Status = EFI_INVALID_PARAMETER;
 | |
|         break;
 | |
| 
 | |
|       case SOL_SOCKET:
 | |
|         switch ( OptionName ) {
 | |
|         default:
 | |
|           //
 | |
|           //  Option not supported
 | |
|           //
 | |
|           DEBUG (( DEBUG_OPTION,
 | |
|                     "ERROR - Sockets does not support this option!\r\n" ));
 | |
|           errno = EINVAL;
 | |
|           Status = EFI_INVALID_PARAMETER;
 | |
|           break;
 | |
| 
 | |
|         case SO_DEBUG:
 | |
|           //
 | |
|           //  Set the debug flags
 | |
|           //
 | |
|           pOptionData = (UINT8 *)&pSocket->bOobInLine;
 | |
|           LengthInBytes = sizeof ( pSocket->bOobInLine );
 | |
|           break;
 | |
| 
 | |
|         case SO_OOBINLINE:
 | |
|           pOptionData = (UINT8 *)&pSocket->bOobInLine;
 | |
|           LengthInBytes = sizeof ( pSocket->bOobInLine );
 | |
| 
 | |
|           //
 | |
|           //  Validate the option length
 | |
|           //
 | |
|           if ( sizeof ( UINT32 ) == OptionLength ) {
 | |
|             //
 | |
|             //  Restrict the input to TRUE or FALSE
 | |
|             //
 | |
|             bTrueFalse = TRUE;
 | |
|             if ( 0 == *(UINT32 *)pOptionValue ) {
 | |
|               bTrueFalse = FALSE;
 | |
|             }
 | |
|             pOptionValue = &bTrueFalse;
 | |
|           }
 | |
|           else {
 | |
|             //
 | |
|             //  Force an invalid option length error
 | |
|             //
 | |
|             OptionLength = LengthInBytes - 1;
 | |
|           }
 | |
|           break;
 | |
| 
 | |
|         case SO_RCVTIMEO:
 | |
|           //
 | |
|           //  Return the receive timeout
 | |
|           //
 | |
|           pOptionData = (UINT8 *)&pSocket->RxTimeout;
 | |
|           LengthInBytes = sizeof ( pSocket->RxTimeout );
 | |
|           break;
 | |
| 
 | |
|         case SO_RCVBUF:
 | |
|           //
 | |
|           //  Return the maximum receive buffer size
 | |
|           //
 | |
|           pOptionData = (UINT8 *)&pSocket->MaxRxBuf;
 | |
|           LengthInBytes = sizeof ( pSocket->MaxRxBuf );
 | |
|           break;
 | |
| 
 | |
|         case SO_REUSEADDR:
 | |
|           //
 | |
|           //  Return the address reuse flag
 | |
|           //
 | |
|           pOptionData = (UINT8 *)&pSocket->bReUseAddr;
 | |
|           LengthInBytes = sizeof ( pSocket->bReUseAddr );
 | |
|           break;
 | |
| 
 | |
|         case SO_SNDBUF:
 | |
|           //
 | |
|           //  Send buffer size
 | |
|           //
 | |
|           //
 | |
|           //  Return the maximum transmit buffer size
 | |
|           //
 | |
|           pOptionData = (UINT8 *)&pSocket->MaxTxBuf;
 | |
|           LengthInBytes = sizeof ( pSocket->MaxTxBuf );
 | |
|           break;
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       //  Determine if an option was found
 | |
|       //
 | |
|       if ( 0 != LengthInBytes ) {
 | |
|         //
 | |
|         //  Validate the option length
 | |
|         //
 | |
|         if ( LengthInBytes <= OptionLength ) {
 | |
|           //
 | |
|           //  Set the option value
 | |
|           //
 | |
|           CopyMem ( pOptionData, pOptionValue, LengthInBytes );
 | |
|           errno = 0;
 | |
|           Status = EFI_SUCCESS;
 | |
|         }
 | |
|         else {
 | |
|           DEBUG (( DEBUG_OPTION,
 | |
|                     "ERROR - Buffer to small, %d bytes < %d bytes!\r\n",
 | |
|                     OptionLength,
 | |
|                     LengthInBytes ));
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   if ( NULL != pErrno ) {
 | |
|     *pErrno = errno;
 | |
|   }
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**  Allocate a packet for a receive or transmit operation.
 | |
| 
 | |
|   This support routine is called by ::EslSocketRxStart and the
 | |
|   network specific TxBuffer routines to get buffer space for the
 | |
|   next operation.
 | |
| 
 | |
|   @param[in]  ppPacket      Address to receive the ::ESL_PACKET structure
 | |
|   @param[in]  LengthInBytes Length of the packet structure
 | |
|   @param[in]  ZeroBytes     Length of packet to zero
 | |
|   @param[in]  DebugFlags    Flags for debug messages
 | |
| 
 | |
|   @retval EFI_SUCCESS - The packet was allocated successfully
 | |
| **/
 | |
| EFI_STATUS
 | |
| EslSocketPacketAllocate (
 | |
|   IN ESL_PACKET ** ppPacket,
 | |
|   IN size_t LengthInBytes,
 | |
|   IN size_t ZeroBytes,
 | |
|   IN UINTN DebugFlags
 | |
|   )
 | |
| {
 | |
|   ESL_PACKET * pPacket;
 | |
|   EFI_STATUS Status;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Allocate a packet structure
 | |
|   //
 | |
|   LengthInBytes += sizeof ( *pPacket )
 | |
|                 - sizeof ( pPacket->Op );
 | |
|   Status = gBS->AllocatePool ( EfiRuntimeServicesData,
 | |
|                                LengthInBytes,
 | |
|                                (VOID **)&pPacket );
 | |
|   if ( !EFI_ERROR ( Status )) {
 | |
|     DEBUG (( DebugFlags | DEBUG_POOL,
 | |
|               "0x%08x: Allocate pPacket, %d bytes\r\n",
 | |
|               pPacket,
 | |
|               LengthInBytes ));
 | |
|     if ( 0 != ZeroBytes ) {
 | |
|       ZeroMem ( &pPacket->Op, ZeroBytes );
 | |
|     }
 | |
|     pPacket->PacketSize = LengthInBytes;
 | |
|   }
 | |
|   else {
 | |
|     DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INFO,
 | |
|               "ERROR - Packet allocation failed for %d bytes, Status: %r\r\n",
 | |
|               LengthInBytes,
 | |
|               Status ));
 | |
|     pPacket = NULL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the packet
 | |
|   //
 | |
|   *ppPacket = pPacket;
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Free a packet used for receive or transmit operation.
 | |
| 
 | |
|   This support routine is called by the network specific Close
 | |
|   and TxComplete routines and during error cases in RxComplete
 | |
|   and TxBuffer.  Note that the network layers typically place
 | |
|   receive packets on the ESL_SOCKET::pRxFree list for reuse.
 | |
| 
 | |
|   @param[in]  pPacket     Address of an ::ESL_PACKET structure
 | |
|   @param[in]  DebugFlags  Flags for debug messages
 | |
| 
 | |
|   @retval EFI_SUCCESS - The packet was allocated successfully
 | |
| **/
 | |
| EFI_STATUS
 | |
| EslSocketPacketFree (
 | |
|   IN ESL_PACKET * pPacket,
 | |
|   IN UINTN DebugFlags
 | |
|   )
 | |
| {
 | |
|   UINTN LengthInBytes;
 | |
|   EFI_STATUS Status;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
| 
 | |
|   //
 | |
|   //  Free a packet structure
 | |
|   //
 | |
|   LengthInBytes = pPacket->PacketSize;
 | |
|   Status = gBS->FreePool ( pPacket );
 | |
|   if ( !EFI_ERROR ( Status )) {
 | |
|     DEBUG (( DebugFlags | DEBUG_POOL,
 | |
|               "0x%08x: Free pPacket, %d bytes\r\n",
 | |
|               pPacket,
 | |
|               LengthInBytes ));
 | |
|   }
 | |
|   else {
 | |
|     DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INFO,
 | |
|               "ERROR - Failed to free packet 0x%08x, Status: %r\r\n",
 | |
|               pPacket,
 | |
|               Status ));
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Return the operation status
 | |
|   //
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Poll a socket for pending activity.
 | |
| 
 | |
|   This routine builds a detected event mask which is returned to
 | |
|   the caller in the buffer provided.
 | |
| 
 | |
|   The ::poll routine calls this routine to determine if the socket
 | |
|   needs to be serviced as a result of connection, error, receive or
 | |
|   transmit activity.
 | |
| 
 | |
|   @param[in]  pSocketProtocol Address of an ::EFI_SOCKET_PROTOCOL structure.
 | |
|   @param[in]  Events          Events of interest for this socket
 | |
|   @param[in]  pEvents         Address to receive the detected events
 | |
|   @param[out] pErrno          Address to receive the errno value upon completion.
 | |
| 
 | |
|   @retval EFI_SUCCESS - Socket successfully polled
 | |
|   @retval EFI_INVALID_PARAMETER - When pEvents is NULL
 | |
| **/
 | |
| EFI_STATUS
 | |
| EslSocketPoll (
 | |
|   IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
 | |
|   IN short Events,
 | |
|   IN short * pEvents,
 | |
|   IN int * pErrno
 | |
|   )
 | |
| {
 | |
|   short DetectedEvents;
 | |
|   ESL_SOCKET * pSocket;
 | |
|   EFI_STATUS Status;
 | |
|   EFI_TPL TplPrevious;
 | |
|   short ValidEvents;
 | |
|   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
 | |
| EslSocketPortCloseComplete (
 | |
|   IN EFI_EVENT Event,
 | |
|   IN ESL_PORT * pPort
 | |
|   )
 | |
| {
 | |
|   ESL_IO_MGMT * pIo;
 | |
|   EFI_STATUS Status;
 | |
| 
 | |
|   DBG_ENTER ( );
 | |
|   VERIFY_AT_TPL ( TPL_SOCKETS );
 | |
| 
 | |
|   //  Update the port state
 | |
|   pPort->State = PORT_STATE_CLOSE_DONE;
 | |
|   DEBUG (( DEBUG_CLOSE | DEBUG_INFO,
 | |
|             "0x%08x: Port Close State: PORT_STATE_CLOSE_DONE\r\n",
 | |
|             pPort ));
 | |
| 
 | |
|   //  Shutdown the receive operation on the port
 | |
|   if ( NULL != pPort->pfnRxCancel ) {
 | |
|     pIo = pPort->pRxActive;
 | |
|     while ( NULL != pIo ) {
 | |
|       EslSocketRxCancel ( pPort, pIo );
 | |
|       pIo = pIo->pNext;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //  Determine if the receive operation is pending
 | |
|   Status = EslSocketPortCloseRxDone ( pPort );
 | |
|   DBG_EXIT_STATUS ( Status );
 | |
|   --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 ( );
 | |
| }
 |