blob: d782b435e658a8251cd02447eb00d538ee0e1fd7 [file] [log] [blame]
/** @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 ( );
}