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