/** @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) 2011, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which 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
::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:
+-------------+ +-------------+ +-------------+
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)
+--------------------------+
| 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 |
+--------------------------+
+------------------+
| ::ESL_PORT |
| |
+------------------+
| ::ESL_IO_MGMT |
+------------------+
| ESL_IO_MGMT |
+------------------+
. .
. ESL_IO_MGMT .
. .
+------------------+
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.
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
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.
+------------+ +------------+
High .----->| ESL_PACKET |-->| ESL_PACKET |--> NULL (pNext)
Priority | +------------+ +------------+
|
| pRxOobPacketListHead
+------------+
| ::ESL_SOCKET |
+------------+
| pRxPacketListHead
Low |
Priority | +------------+ +------------+ +------------+
`----->| ::ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL
+------------+ +------------+ +------------+
::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.
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 |
+----+---------------+ | |
| |
+--------------------------+
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.
*ppQueueHead: pSocket->pRxPacketListHead or pSocket->pRxOobPacketListHead
|
V
+------------+ +------------+ +------------+
Data | ESL_PACKET |-->| ESL_PACKET |-->| ESL_PACKET |--> NULL
+------------+ +------------+ +------------+
^
|
*ppQueueTail: pSocket->pRxPacketListTail or pSocket->pRxOobPacketListTail
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.
+--------------------+ +--------------------+
| ESL_IO_MGMT | | ESL_PACKET |
| | | |
| +---------------+ +----------------+ |
| | Token | | Buffer Length | |
| | TxData --> | Buffer Address | |
| | | +----------------+---+
| | Event | | Data Buffer |
+----+---------------+ | |
+--------------------+
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.
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
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: