mirror of https://github.com/acidanthera/audk.git
5958 lines
172 KiB
C
5958 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;
|
|
|
|
DEBUG (( DEBUG_POLL, "Entering SocketPoll\r\n" ));
|
|
|
|
//
|
|
// Assume success
|
|
//
|
|
Status = EFI_SUCCESS;
|
|
DetectedEvents = 0;
|
|
pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
|
|
pSocket->errno = 0;
|
|
|
|
//
|
|
// Verify the socket state
|
|
//
|
|
Status = EslSocketIsConfigured ( pSocket );
|
|
if ( !EFI_ERROR ( Status )) {
|
|
//
|
|
// Check for invalid events
|
|
//
|
|
ValidEvents = POLLIN
|
|
| POLLPRI
|
|
| POLLOUT | POLLWRNORM
|
|
| POLLERR
|
|
| POLLHUP
|
|
| POLLNVAL
|
|
| POLLRDNORM
|
|
| POLLRDBAND
|
|
| POLLWRBAND ;
|
|
if ( 0 != ( Events & ( ~ValidEvents ))) {
|
|
DetectedEvents |= POLLNVAL;
|
|
DEBUG (( DEBUG_INFO | DEBUG_POLL,
|
|
"ERROR - Invalid event mask, Valid Events: 0x%04x, Invalid Events: 0x%04x\r\n",
|
|
Events & ValidEvents,
|
|
Events & ( ~ValidEvents )));
|
|
}
|
|
else {
|
|
//
|
|
// Synchronize with the socket layer
|
|
//
|
|
RAISE_TPL ( TplPrevious, TPL_SOCKETS );
|
|
|
|
//
|
|
// Increase the network performance by extending the
|
|
// polling (idle) loop down into the LAN driver
|
|
//
|
|
EslSocketRxPoll ( pSocket );
|
|
|
|
//
|
|
// Release the socket layer synchronization
|
|
//
|
|
RESTORE_TPL ( TplPrevious );
|
|
|
|
//
|
|
// Check for pending connections
|
|
//
|
|
if ( 0 != pSocket->FifoDepth ) {
|
|
//
|
|
// A connection is waiting for an accept call
|
|
// See posix connect documentation at
|
|
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.htm
|
|
//
|
|
DetectedEvents |= POLLIN | POLLRDNORM;
|
|
}
|
|
if ( pSocket->bConnected ) {
|
|
//
|
|
// A connection is present
|
|
// See posix connect documentation at
|
|
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.htm
|
|
//
|
|
DetectedEvents |= POLLOUT | POLLWRNORM;
|
|
}
|
|
|
|
//
|
|
// The following bits are set based upon the POSIX poll documentation at
|
|
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html
|
|
//
|
|
|
|
//
|
|
// Check for urgent receive data
|
|
//
|
|
if ( 0 < pSocket->RxOobBytes ) {
|
|
DetectedEvents |= POLLRDBAND | POLLPRI | POLLIN;
|
|
}
|
|
|
|
//
|
|
// Check for normal receive data
|
|
//
|
|
if (( 0 < pSocket->RxBytes )
|
|
|| ( EFI_SUCCESS != pSocket->RxError )) {
|
|
DetectedEvents |= POLLRDNORM | POLLIN;
|
|
}
|
|
|
|
//
|
|
// Handle the receive errors
|
|
//
|
|
if (( EFI_SUCCESS != pSocket->RxError )
|
|
&& ( 0 == ( DetectedEvents & POLLIN ))) {
|
|
DetectedEvents |= POLLERR | POLLIN | POLLRDNORM | POLLRDBAND;
|
|
}
|
|
|
|
//
|
|
// Check for urgent transmit data buffer space
|
|
//
|
|
if (( MAX_TX_DATA > pSocket->TxOobBytes )
|
|
|| ( EFI_SUCCESS != pSocket->TxError )) {
|
|
DetectedEvents |= POLLWRBAND;
|
|
}
|
|
|
|
//
|
|
// Check for normal transmit data buffer space
|
|
//
|
|
if (( MAX_TX_DATA > pSocket->TxBytes )
|
|
|| ( EFI_SUCCESS != pSocket->TxError )) {
|
|
DetectedEvents |= POLLWRNORM;
|
|
}
|
|
|
|
//
|
|
// Handle the transmit error
|
|
//
|
|
if ( EFI_ERROR ( pSocket->TxError )) {
|
|
DetectedEvents |= POLLERR;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the detected events
|
|
//
|
|
*pEvents = DetectedEvents & ( Events
|
|
| POLLERR
|
|
| POLLHUP
|
|
| POLLNVAL );
|
|
|
|
//
|
|
// Return the operation status
|
|
//
|
|
DEBUG (( DEBUG_POLL, "Exiting SocketPoll, Status: %r\r\n", Status ));
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Allocate and initialize a ESL_PORT structure.
|
|
|
|
This routine initializes an ::ESL_PORT structure for use by
|
|
the socket. This routine calls a routine via
|
|
ESL_PROTOCOL_API::pfnPortAllocate to initialize the network
|
|
specific resources. The resources are released later by the
|
|
\ref PortCloseStateMachine.
|
|
|
|
This support routine is called by:
|
|
<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 );
|
|
}
|
|
|
|
|
|
/**
|
|
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 ( );
|
|
}
|