/** @file | |
Implement the UDP4 driver support for the socket layer. | |
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. | |
**/ | |
#include "Socket.h" | |
/** | |
Get the local socket address | |
This routine returns the IPv4 address and UDP port number associated | |
with the local socket. | |
This routine is called by ::EslSocketGetLocalAddress to determine the | |
network address for the SOCK_DGRAM socket. | |
@param [in] pPort Address of an ::ESL_PORT structure. | |
@param [out] pSockAddr Network address to receive the local system address | |
**/ | |
VOID | |
EslUdp4LocalAddressGet ( | |
IN ESL_PORT * pPort, | |
OUT struct sockaddr * pSockAddr | |
) | |
{ | |
struct sockaddr_in * pLocalAddress; | |
ESL_UDP4_CONTEXT * pUdp4; | |
DBG_ENTER ( ); | |
// | |
// Return the local address | |
// | |
pUdp4 = &pPort->Context.Udp4; | |
pLocalAddress = (struct sockaddr_in *)pSockAddr; | |
pLocalAddress->sin_family = AF_INET; | |
pLocalAddress->sin_port = SwapBytes16 ( pUdp4->ConfigData.StationPort ); | |
CopyMem ( &pLocalAddress->sin_addr, | |
&pUdp4->ConfigData.StationAddress.Addr[0], | |
sizeof ( pLocalAddress->sin_addr )); | |
DBG_EXIT ( ); | |
} | |
/** | |
Set the local port address. | |
This routine sets the local port address. | |
This support routine is called by ::EslSocketPortAllocate. | |
@param [in] pPort Address of an ESL_PORT 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] bBindTest TRUE = run bind testing | |
@retval EFI_SUCCESS The operation was successful | |
**/ | |
EFI_STATUS | |
EslUdp4LocalAddressSet ( | |
IN ESL_PORT * pPort, | |
IN CONST struct sockaddr * pSockAddr, | |
IN BOOLEAN bBindTest | |
) | |
{ | |
EFI_UDP4_CONFIG_DATA * pConfig; | |
CONST struct sockaddr_in * pIpAddress; | |
CONST UINT8 * pIpv4Address; | |
EFI_STATUS Status; | |
DBG_ENTER ( ); | |
// | |
// Validate the address | |
// | |
pIpAddress = (struct sockaddr_in *)pSockAddr; | |
if ( INADDR_BROADCAST == pIpAddress->sin_addr.s_addr ) { | |
// | |
// The local address must not be the broadcast address | |
// | |
Status = EFI_INVALID_PARAMETER; | |
pPort->pSocket->errno = EADDRNOTAVAIL; | |
} | |
else { | |
// | |
// Set the local address | |
// | |
pIpAddress = (struct sockaddr_in *)pSockAddr; | |
pIpv4Address = (UINT8 *)&pIpAddress->sin_addr.s_addr; | |
pConfig = &pPort->Context.Udp4.ConfigData; | |
pConfig->StationAddress.Addr[0] = pIpv4Address[0]; | |
pConfig->StationAddress.Addr[1] = pIpv4Address[1]; | |
pConfig->StationAddress.Addr[2] = pIpv4Address[2]; | |
pConfig->StationAddress.Addr[3] = pIpv4Address[3]; | |
// | |
// Determine if the default address is used | |
// | |
pConfig->UseDefaultAddress = (BOOLEAN)( 0 == pIpAddress->sin_addr.s_addr ); | |
// | |
// Set the subnet mask | |
// | |
if ( pConfig->UseDefaultAddress ) { | |
pConfig->SubnetMask.Addr[0] = 0; | |
pConfig->SubnetMask.Addr[1] = 0; | |
pConfig->SubnetMask.Addr[2] = 0; | |
pConfig->SubnetMask.Addr[3] = 0; | |
} | |
else { | |
pConfig->SubnetMask.Addr[0] = 0xff; | |
pConfig->SubnetMask.Addr[1] = ( 128 <= pConfig->StationAddress.Addr[0]) ? 0xff : 0; | |
pConfig->SubnetMask.Addr[2] = ( 192 <= pConfig->StationAddress.Addr[0]) ? 0xff : 0; | |
pConfig->SubnetMask.Addr[3] = ( 224 <= pConfig->StationAddress.Addr[0]) ? 0xff : 0; | |
} | |
// | |
// Validate the IP address | |
// | |
pConfig->StationPort = 0; | |
Status = bBindTest ? EslSocketBindTest ( pPort, EADDRNOTAVAIL ) | |
: EFI_SUCCESS; | |
if ( !EFI_ERROR ( Status )) { | |
// | |
// Set the port number | |
// | |
pConfig->StationPort = SwapBytes16 ( pIpAddress->sin_port ); | |
// | |
// Display the local address | |
// | |
DEBUG (( DEBUG_BIND, | |
"0x%08x: Port, Local UDP4 Address: %d.%d.%d.%d:%d\r\n", | |
pPort, | |
pConfig->StationAddress.Addr[0], | |
pConfig->StationAddress.Addr[1], | |
pConfig->StationAddress.Addr[2], | |
pConfig->StationAddress.Addr[3], | |
pConfig->StationPort )); | |
} | |
} | |
// | |
// Return the operation status | |
// | |
DBG_EXIT_STATUS ( Status ); | |
return Status; | |
} | |
/** | |
Free a receive packet | |
This routine performs the network specific operations necessary | |
to free a receive packet. | |
This routine is called by ::EslSocketPortCloseTxDone to free a | |
receive packet. | |
@param [in] pPacket Address of an ::ESL_PACKET structure. | |
@param [in, out] pRxBytes Address of the count of RX bytes | |
**/ | |
VOID | |
EslUdp4PacketFree ( | |
IN ESL_PACKET * pPacket, | |
IN OUT size_t * pRxBytes | |
) | |
{ | |
EFI_UDP4_RECEIVE_DATA * pRxData; | |
DBG_ENTER ( ); | |
// | |
// Account for the receive bytes | |
// | |
pRxData = pPacket->Op.Udp4Rx.pRxData; | |
*pRxBytes -= pRxData->DataLength; | |
// | |
// Disconnect the buffer from the packet | |
// | |
pPacket->Op.Udp4Rx.pRxData = NULL; | |
// | |
// Return the buffer to the UDP4 driver | |
// | |
gBS->SignalEvent ( pRxData->RecycleSignal ); | |
DBG_EXIT ( ); | |
} | |
/** | |
Initialize the network specific portions of an ::ESL_PORT structure. | |
This routine initializes the network specific portions of an | |
::ESL_PORT structure for use by the socket. | |
This support routine is called by ::EslSocketPortAllocate | |
to connect the socket with the underlying network adapter | |
running the UDPv4 protocol. | |
@param [in] pPort Address of an ESL_PORT structure | |
@param [in] DebugFlags Flags for debug messages | |
@retval EFI_SUCCESS - Socket successfully created | |
**/ | |
EFI_STATUS | |
EslUdp4PortAllocate ( | |
IN ESL_PORT * pPort, | |
IN UINTN DebugFlags | |
) | |
{ | |
EFI_UDP4_CONFIG_DATA * pConfig; | |
ESL_SOCKET * pSocket; | |
EFI_STATUS Status; | |
DBG_ENTER ( ); | |
// | |
// Initialize the port | |
// | |
pSocket = pPort->pSocket; | |
pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Udp4Tx.TxData ); | |
pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Udp4Tx.Event ); | |
pSocket->TxTokenOffset = OFFSET_OF ( EFI_UDP4_COMPLETION_TOKEN, Packet.TxData ); | |
// | |
// Save the cancel, receive and transmit addresses | |
// | |
pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.UDPv4->Configure; | |
pPort->pfnRxCancel = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Cancel; | |
pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.UDPv4->Poll; | |
pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Receive; | |
pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Transmit; | |
// | |
// Set the configuration flags | |
// | |
pConfig = &pPort->Context.Udp4.ConfigData; | |
pConfig->TimeToLive = 255; | |
pConfig->AcceptAnyPort = FALSE; | |
pConfig->AcceptBroadcast = FALSE; | |
pConfig->AcceptPromiscuous = FALSE; | |
pConfig->AllowDuplicatePort = TRUE; | |
pConfig->DoNotFragment = FALSE; | |
Status = EFI_SUCCESS; | |
// | |
// Return the operation status | |
// | |
DBG_EXIT_STATUS ( Status ); | |
return Status; | |
} | |
/** | |
Receive data from a network connection. | |
This routine attempts to return buffered data to the caller. The | |
data is removed from the urgent queue if the message flag MSG_OOB | |
is specified, otherwise data is removed from the normal queue. | |
See the \ref ReceiveEngine section. | |
This routine is called by ::EslSocketReceive to handle the network | |
specific receive operation to support SOCK_DGRAM sockets. | |
@param [in] pPort Address of an ::ESL_PORT structure. | |
@param [in] pPacket Address of an ::ESL_PACKET structure. | |
@param [in] pbConsumePacket Address of a BOOLEAN indicating if the packet is to be consumed | |
@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 [out] pSkipBytes Address to receive the number of bytes skipped | |
@return Returns the address of the next free byte in the buffer. | |
**/ | |
UINT8 * | |
EslUdp4Receive ( | |
IN ESL_PORT * pPort, | |
IN ESL_PACKET * pPacket, | |
IN BOOLEAN * pbConsumePacket, | |
IN size_t BufferLength, | |
IN UINT8 * pBuffer, | |
OUT size_t * pDataLength, | |
OUT struct sockaddr * pAddress, | |
OUT size_t * pSkipBytes | |
) | |
{ | |
size_t DataBytes; | |
struct sockaddr_in * pRemoteAddress; | |
EFI_UDP4_RECEIVE_DATA * pRxData; | |
DBG_ENTER ( ); | |
pRxData = pPacket->Op.Udp4Rx.pRxData; | |
// | |
// Return the remote system address if requested | |
// | |
if ( NULL != pAddress ) { | |
// | |
// Build the remote address | |
// | |
DEBUG (( DEBUG_RX, | |
"Getting packet remote address: %d.%d.%d.%d:%d\r\n", | |
pRxData->UdpSession.SourceAddress.Addr[0], | |
pRxData->UdpSession.SourceAddress.Addr[1], | |
pRxData->UdpSession.SourceAddress.Addr[2], | |
pRxData->UdpSession.SourceAddress.Addr[3], | |
pRxData->UdpSession.SourcePort )); | |
pRemoteAddress = (struct sockaddr_in *)pAddress; | |
CopyMem ( &pRemoteAddress->sin_addr, | |
&pRxData->UdpSession.SourceAddress.Addr[0], | |
sizeof ( pRemoteAddress->sin_addr )); | |
pRemoteAddress->sin_port = SwapBytes16 ( pRxData->UdpSession.SourcePort ); | |
} | |
// | |
// Copy the received data | |
// | |
pBuffer = EslSocketCopyFragmentedBuffer ( pRxData->FragmentCount, | |
(EFI_IP4_FRAGMENT_DATA *)&pRxData->FragmentTable[0], | |
BufferLength, | |
pBuffer, | |
&DataBytes ); | |
// | |
// Determine if the data is being read | |
// | |
if ( *pbConsumePacket ) { | |
// | |
// Display for the bytes consumed | |
// | |
DEBUG (( DEBUG_RX, | |
"0x%08x: Port account for 0x%08x bytes\r\n", | |
pPort, | |
DataBytes )); | |
// | |
// Account for any discarded data | |
// | |
*pSkipBytes = pRxData->DataLength - DataBytes; | |
} | |
// | |
// Return the data length and the buffer address | |
// | |
*pDataLength = DataBytes; | |
DBG_EXIT_HEX ( pBuffer ); | |
return pBuffer; | |
} | |
/** | |
Get the remote socket address | |
This routine returns the address of the remote connection point | |
associated with the SOCK_DGRAM socket. | |
This routine is called by ::EslSocketGetPeerAddress to detemine | |
the UDPv4 address and port number associated with the network adapter. | |
@param [in] pPort Address of an ::ESL_PORT structure. | |
@param [out] pAddress Network address to receive the remote system address | |
**/ | |
VOID | |
EslUdp4RemoteAddressGet ( | |
IN ESL_PORT * pPort, | |
OUT struct sockaddr * pAddress | |
) | |
{ | |
struct sockaddr_in * pRemoteAddress; | |
ESL_UDP4_CONTEXT * pUdp4; | |
DBG_ENTER ( ); | |
// | |
// Return the remote address | |
// | |
pUdp4 = &pPort->Context.Udp4; | |
pRemoteAddress = (struct sockaddr_in *)pAddress; | |
pRemoteAddress->sin_family = AF_INET; | |
pRemoteAddress->sin_port = SwapBytes16 ( pUdp4->ConfigData.RemotePort ); | |
CopyMem ( &pRemoteAddress->sin_addr, | |
&pUdp4->ConfigData.RemoteAddress.Addr[0], | |
sizeof ( pRemoteAddress->sin_addr )); | |
DBG_EXIT ( ); | |
} | |
/** | |
Set the remote address | |
This routine sets the remote address in the port. | |
This routine is called by ::EslSocketConnect to specify the | |
remote network address. | |
@param [in] pPort Address of an ::ESL_PORT structure. | |
@param [in] pSockAddr Network address of the remote system. | |
@param [in] SockAddrLength Length in bytes of the network address. | |
@retval EFI_SUCCESS The operation was successful | |
**/ | |
EFI_STATUS | |
EslUdp4RemoteAddressSet ( | |
IN ESL_PORT * pPort, | |
IN CONST struct sockaddr * pSockAddr, | |
IN socklen_t SockAddrLength | |
) | |
{ | |
CONST struct sockaddr_in * pRemoteAddress; | |
ESL_UDP4_CONTEXT * pUdp4; | |
EFI_STATUS Status; | |
DBG_ENTER ( ); | |
// | |
// Set the remote address | |
// | |
pUdp4 = &pPort->Context.Udp4; | |
pRemoteAddress = (struct sockaddr_in *)pSockAddr; | |
pUdp4->ConfigData.RemoteAddress.Addr[0] = (UINT8)( pRemoteAddress->sin_addr.s_addr ); | |
pUdp4->ConfigData.RemoteAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 ); | |
pUdp4->ConfigData.RemoteAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 ); | |
pUdp4->ConfigData.RemoteAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 ); | |
pUdp4->ConfigData.RemotePort = SwapBytes16 ( pRemoteAddress->sin_port ); | |
pPort->pSocket->bAddressSet = TRUE; | |
Status = EFI_SUCCESS; | |
// | |
// Return the operation status | |
// | |
DBG_EXIT_STATUS ( Status ); | |
return Status; | |
} | |
/** | |
Process the receive completion | |
This routine keeps the UDPv4 driver's buffer and queues it in | |
in FIFO order to the data queue. The UDP4 driver's buffer will | |
be returned by either ::EslUdp4Receive or ::EslSocketPortCloseTxDone. | |
See the \ref ReceiveEngine section. | |
This routine is called by the UDPv4 driver when data is | |
received. | |
@param [in] Event The receive completion event | |
@param [in] pIo Address of an ::ESL_IO_MGMT structure | |
**/ | |
VOID | |
EslUdp4RxComplete ( | |
IN EFI_EVENT Event, | |
IN ESL_IO_MGMT * pIo | |
) | |
{ | |
size_t LengthInBytes; | |
ESL_PACKET * pPacket; | |
EFI_UDP4_RECEIVE_DATA * pRxData; | |
EFI_STATUS Status; | |
DBG_ENTER ( ); | |
// | |
// Get the operation status. | |
// | |
Status = pIo->Token.Udp4Rx.Status; | |
// | |
// Get the packet length | |
// | |
pRxData = pIo->Token.Udp4Rx.Packet.RxData; | |
LengthInBytes = pRxData->DataLength; | |
// | |
// +--------------------+ +-----------------------+ | |
// | ESL_IO_MGMT | | Data Buffer | | |
// | | | (Driver owned) | | |
// | +---------------+ +-----------------------+ | |
// | | Token | ^ | |
// | | Rx Event | | | |
// | | | +-----------------------+ | |
// | | RxData --> | EFI_UDP4_RECEIVE_DATA | | |
// +----+---------------+ | (Driver owned) | | |
// +-----------------------+ | |
// +--------------------+ ^ | |
// | ESL_PACKET | . | |
// | | . | |
// | +---------------+ . | |
// | | pRxData --> NULL ....... | |
// +----+---------------+ | |
// | |
// | |
// Save the data in the packet | |
// | |
pPacket = pIo->pPacket; | |
pPacket->Op.Udp4Rx.pRxData = pRxData; | |
// | |
// Complete this request | |
// | |
EslSocketRxComplete ( pIo, Status, LengthInBytes, FALSE ); | |
DBG_EXIT ( ); | |
} | |
/** | |
Determine if the socket is configured. | |
This routine uses the flag ESL_SOCKET::bConfigured to determine | |
if the network layer's configuration routine has been called. | |
This routine calls the bind and configuration routines if they | |
were not already called. After the port is configured, the | |
\ref ReceiveEngine is started. | |
This routine is called by EslSocketIsConfigured to verify | |
that the socket is configured. | |
@param [in] pSocket Address of an ::ESL_SOCKET structure | |
@retval EFI_SUCCESS - The port is connected | |
@retval EFI_NOT_STARTED - The port is not connected | |
**/ | |
EFI_STATUS | |
EslUdp4SocketIsConfigured ( | |
IN ESL_SOCKET * pSocket | |
) | |
{ | |
EFI_UDP4_CONFIG_DATA * pConfigData; | |
ESL_PORT * pPort; | |
ESL_PORT * pNextPort; | |
ESL_UDP4_CONTEXT * pUdp4; | |
EFI_UDP4_PROTOCOL * pUdp4Protocol; | |
EFI_STATUS Status; | |
struct sockaddr_in LocalAddress; | |
DBG_ENTER ( ); | |
// | |
// Assume success | |
// | |
Status = EFI_SUCCESS; | |
// | |
// Configure the port if necessary | |
// | |
if ( !pSocket->bConfigured ) { | |
// | |
// Fill in the port list if necessary | |
// | |
pSocket->errno = ENETDOWN; | |
if ( NULL == pSocket->pPortList ) { | |
LocalAddress.sin_len = sizeof ( LocalAddress ); | |
LocalAddress.sin_family = AF_INET; | |
LocalAddress.sin_addr.s_addr = 0; | |
LocalAddress.sin_port = 0; | |
Status = EslSocketBind ( &pSocket->SocketProtocol, | |
(struct sockaddr *)&LocalAddress, | |
LocalAddress.sin_len, | |
&pSocket->errno ); | |
} | |
// | |
// Walk the port list | |
// | |
pPort = pSocket->pPortList; | |
while ( NULL != pPort ) { | |
// | |
// Attempt to configure the port | |
// | |
pNextPort = pPort->pLinkSocket; | |
pUdp4 = &pPort->Context.Udp4; | |
pUdp4Protocol = pPort->pProtocol.UDPv4; | |
pConfigData = &pUdp4->ConfigData; | |
DEBUG (( DEBUG_TX, | |
"0x%08x: pPort Configuring for %d.%d.%d.%d:%d --> %d.%d.%d.%d:%d\r\n", | |
pPort, | |
pConfigData->StationAddress.Addr[0], | |
pConfigData->StationAddress.Addr[1], | |
pConfigData->StationAddress.Addr[2], | |
pConfigData->StationAddress.Addr[3], | |
pConfigData->StationPort, | |
pConfigData->RemoteAddress.Addr[0], | |
pConfigData->RemoteAddress.Addr[1], | |
pConfigData->RemoteAddress.Addr[2], | |
pConfigData->RemoteAddress.Addr[3], | |
pConfigData->RemotePort )); | |
Status = pUdp4Protocol->Configure ( pUdp4Protocol, | |
pConfigData ); | |
if ( !EFI_ERROR ( Status )) { | |
// | |
// Update the configuration data | |
// | |
Status = pUdp4Protocol->GetModeData ( pUdp4Protocol, | |
pConfigData, | |
NULL, | |
NULL, | |
NULL ); | |
} | |
if ( EFI_ERROR ( Status )) { | |
if ( !pSocket->bConfigured ) { | |
DEBUG (( DEBUG_LISTEN, | |
"ERROR - Failed to configure the Udp4 port, Status: %r\r\n", | |
Status )); | |
switch ( Status ) { | |
case EFI_ACCESS_DENIED: | |
pSocket->errno = EACCES; | |
break; | |
default: | |
case EFI_DEVICE_ERROR: | |
pSocket->errno = EIO; | |
break; | |
case EFI_INVALID_PARAMETER: | |
pSocket->errno = EADDRNOTAVAIL; | |
break; | |
case EFI_NO_MAPPING: | |
pSocket->errno = EAFNOSUPPORT; | |
break; | |
case EFI_OUT_OF_RESOURCES: | |
pSocket->errno = ENOBUFS; | |
break; | |
case EFI_UNSUPPORTED: | |
pSocket->errno = EOPNOTSUPP; | |
break; | |
} | |
} | |
} | |
else { | |
DEBUG (( DEBUG_TX, | |
"0x%08x: pPort Configured for %d.%d.%d.%d:%d --> %d.%d.%d.%d:%d\r\n", | |
pPort, | |
pConfigData->StationAddress.Addr[0], | |
pConfigData->StationAddress.Addr[1], | |
pConfigData->StationAddress.Addr[2], | |
pConfigData->StationAddress.Addr[3], | |
pConfigData->StationPort, | |
pConfigData->RemoteAddress.Addr[0], | |
pConfigData->RemoteAddress.Addr[1], | |
pConfigData->RemoteAddress.Addr[2], | |
pConfigData->RemoteAddress.Addr[3], | |
pConfigData->RemotePort )); | |
pPort->bConfigured = TRUE; | |
pSocket->bConfigured = TRUE; | |
// | |
// Start the first read on the port | |
// | |
EslSocketRxStart ( pPort ); | |
// | |
// The socket is connected | |
// | |
pSocket->State = SOCKET_STATE_CONNECTED; | |
pSocket->errno = 0; | |
} | |
// | |
// Set the next port | |
// | |
pPort = pNextPort; | |
} | |
} | |
// | |
// Determine the socket configuration status | |
// | |
Status = pSocket->bConfigured ? EFI_SUCCESS : EFI_NOT_STARTED; | |
// | |
// Return the port connected state. | |
// | |
DBG_EXIT_STATUS ( Status ); | |
return Status; | |
} | |
/** | |
Buffer data for transmission over a network connection. | |
This routine buffers data for the transmit engine in the normal | |
data queue. When the \ref TransmitEngine has resources, this | |
routine will start the transmission of the next buffer on the | |
network connection. | |
This routine is called by ::EslSocketTransmit to buffer | |
data for transmission. The data is copied into a local buffer | |
freeing the application buffer for reuse upon return. When | |
necessary, this routine starts the transmit engine that | |
performs the data transmission on the network connection. The | |
transmit engine transmits the data a packet at a time over the | |
network connection. | |
Transmission errors are returned during the next transmission or | |
during the close operation. Only buffering errors are returned | |
during the current transmission attempt. | |
@param [in] pSocket Address of an ::ESL_SOCKET 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 [in] pAddress Network address of the remote system address | |
@param [in] AddressLength Length of the remote network address structure | |
@retval EFI_SUCCESS - Socket data successfully buffered | |
**/ | |
EFI_STATUS | |
EslUdp4TxBuffer ( | |
IN ESL_SOCKET * pSocket, | |
IN int Flags, | |
IN size_t BufferLength, | |
IN CONST UINT8 * pBuffer, | |
OUT size_t * pDataLength, | |
IN const struct sockaddr * pAddress, | |
IN socklen_t AddressLength | |
) | |
{ | |
ESL_PACKET * pPacket; | |
ESL_PACKET * pPreviousPacket; | |
ESL_PORT * pPort; | |
const struct sockaddr_in * pRemoteAddress; | |
ESL_UDP4_CONTEXT * pUdp4; | |
size_t * pTxBytes; | |
ESL_UDP4_TX_DATA * pTxData; | |
EFI_STATUS Status; | |
EFI_TPL TplPrevious; | |
DBG_ENTER ( ); | |
// | |
// Assume failure | |
// | |
Status = EFI_UNSUPPORTED; | |
pSocket->errno = ENOTCONN; | |
*pDataLength = 0; | |
// | |
// Verify that the socket is connected | |
// | |
if ( SOCKET_STATE_CONNECTED == pSocket->State ) { | |
// | |
// Verify that there is enough room to buffer another | |
// transmit operation | |
// | |
pTxBytes = &pSocket->TxBytes; | |
if ( pSocket->MaxTxBuf > *pTxBytes ) { | |
// | |
// Locate the port | |
// | |
pPort = pSocket->pPortList; | |
while ( NULL != pPort ) { | |
// | |
// Determine the queue head | |
// | |
pUdp4 = &pPort->Context.Udp4; | |
// | |
// Attempt to allocate the packet | |
// | |
Status = EslSocketPacketAllocate ( &pPacket, | |
sizeof ( pPacket->Op.Udp4Tx ) | |
- sizeof ( pPacket->Op.Udp4Tx.Buffer ) | |
+ BufferLength, | |
0, | |
DEBUG_TX ); | |
if ( !EFI_ERROR ( Status )) { | |
// | |
// Initialize the transmit operation | |
// | |
pTxData = &pPacket->Op.Udp4Tx; | |
pTxData->TxData.GatewayAddress = NULL; | |
pTxData->TxData.UdpSessionData = NULL; | |
pTxData->TxData.DataLength = (UINT32) BufferLength; | |
pTxData->TxData.FragmentCount = 1; | |
pTxData->TxData.FragmentTable[0].FragmentLength = (UINT32) BufferLength; | |
pTxData->TxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Udp4Tx.Buffer[0]; | |
pTxData->RetransmitCount = 0; | |
// | |
// Set the remote system address if necessary | |
// | |
pTxData->TxData.UdpSessionData = NULL; | |
if ( NULL != pAddress ) { | |
pRemoteAddress = (const struct sockaddr_in *)pAddress; | |
pTxData->Session.SourceAddress.Addr[0] = pUdp4->ConfigData.StationAddress.Addr[0]; | |
pTxData->Session.SourceAddress.Addr[1] = pUdp4->ConfigData.StationAddress.Addr[1]; | |
pTxData->Session.SourceAddress.Addr[2] = pUdp4->ConfigData.StationAddress.Addr[2]; | |
pTxData->Session.SourceAddress.Addr[3] = pUdp4->ConfigData.StationAddress.Addr[3]; | |
pTxData->Session.SourcePort = 0; | |
pTxData->Session.DestinationAddress.Addr[0] = (UINT8)pRemoteAddress->sin_addr.s_addr; | |
pTxData->Session.DestinationAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 ); | |
pTxData->Session.DestinationAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 ); | |
pTxData->Session.DestinationAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 ); | |
pTxData->Session.DestinationPort = SwapBytes16 ( pRemoteAddress->sin_port ); | |
// | |
// Use the remote system address when sending this packet | |
// | |
pTxData->TxData.UdpSessionData = &pTxData->Session; | |
} | |
// | |
// Copy the data into the buffer | |
// | |
CopyMem ( &pPacket->Op.Udp4Tx.Buffer[0], | |
pBuffer, | |
BufferLength ); | |
// | |
// Synchronize with the socket layer | |
// | |
RAISE_TPL ( TplPrevious, TPL_SOCKETS ); | |
// | |
// Display the request | |
// | |
DEBUG (( DEBUG_TX, | |
"Send %d bytes from 0x%08x to %d.%d.%d.%d:%d\r\n", | |
BufferLength, | |
pBuffer, | |
pTxData->Session.DestinationAddress.Addr[0], | |
pTxData->Session.DestinationAddress.Addr[1], | |
pTxData->Session.DestinationAddress.Addr[2], | |
pTxData->Session.DestinationAddress.Addr[3], | |
pTxData->Session.DestinationPort )); | |
// | |
// Queue the data for transmission | |
// | |
pPacket->pNext = NULL; | |
pPreviousPacket = pSocket->pTxPacketListTail; | |
if ( NULL == pPreviousPacket ) { | |
pSocket->pTxPacketListHead = pPacket; | |
} | |
else { | |
pPreviousPacket->pNext = pPacket; | |
} | |
pSocket->pTxPacketListTail = pPacket; | |
DEBUG (( DEBUG_TX, | |
"0x%08x: Packet on transmit list\r\n", | |
pPacket )); | |
// | |
// Account for the buffered data | |
// | |
*pTxBytes += BufferLength; | |
*pDataLength = BufferLength; | |
// | |
// Start the transmit engine if it is idle | |
// | |
if ( NULL != pPort->pTxFree ) { | |
pPacket = pSocket->pTxPacketListHead; | |
EslSocketTxStart ( pPort, | |
&pSocket->pTxPacketListHead, | |
&pSocket->pTxPacketListTail, | |
&pPort->pTxActive, | |
&pPort->pTxFree ); | |
// | |
// Ignore any transmit error | |
// | |
if ( EFI_ERROR ( pSocket->TxError )) { | |
DEBUG (( DEBUG_TX, | |
"0x%08x: Transmit error, Packet: 0x%08x, Status: %r\r\n", | |
pPort, | |
pPacket, | |
pSocket->TxError )); | |
} | |
pSocket->TxError = EFI_SUCCESS; | |
} | |
// | |
// Release the socket layer synchronization | |
// | |
RESTORE_TPL ( TplPrevious ); | |
} | |
else { | |
// | |
// Packet allocation failed | |
// | |
pSocket->errno = ENOMEM; | |
break; | |
} | |
// | |
// Set the next port | |
// | |
pPort = pPort->pLinkSocket; | |
} | |
} | |
else { | |
// | |
// Not enough buffer space available | |
// | |
pSocket->errno = EAGAIN; | |
Status = EFI_NOT_READY; | |
} | |
} | |
// | |
// Return the operation status | |
// | |
DBG_EXIT_STATUS ( Status ); | |
return Status; | |
} | |
/** | |
Process the transmit completion | |
This routine use ::EslSocketTxComplete to perform the transmit | |
completion processing for data packets. | |
This routine is called by the UDPv4 network layer when a data | |
transmit request completes. | |
@param [in] Event The normal transmit completion event | |
@param [in] pIo Address of an ::ESL_IO_MGMT structure | |
**/ | |
VOID | |
EslUdp4TxComplete ( | |
IN EFI_EVENT Event, | |
IN ESL_IO_MGMT * pIo | |
) | |
{ | |
UINT32 LengthInBytes; | |
ESL_PORT * pPort; | |
ESL_PACKET * pPacket; | |
ESL_SOCKET * pSocket; | |
EFI_STATUS Status; | |
DBG_ENTER ( ); | |
// | |
// Locate the active transmit packet | |
// | |
pPacket = pIo->pPacket; | |
pPort = pIo->pPort; | |
pSocket = pPort->pSocket; | |
// | |
// Get the transmit length and status | |
// | |
LengthInBytes = pPacket->Op.Udp4Tx.TxData.DataLength; | |
pSocket->TxBytes -= LengthInBytes; | |
Status = pIo->Token.Udp4Tx.Status; | |
// | |
// Ignore the transmit error | |
// | |
if ( EFI_ERROR ( Status )) { | |
DEBUG (( DEBUG_TX, | |
"0x%08x: Transmit completion error, Packet: 0x%08x, Status: %r\r\n", | |
pPort, | |
pPacket, | |
Status )); | |
Status = EFI_SUCCESS; | |
} | |
// | |
// Complete the transmit operation | |
// | |
EslSocketTxComplete ( pIo, | |
LengthInBytes, | |
Status, | |
"UDP ", | |
&pSocket->pTxPacketListHead, | |
&pSocket->pTxPacketListTail, | |
&pPort->pTxActive, | |
&pPort->pTxFree ); | |
DBG_EXIT ( ); | |
} | |
/** | |
Verify the adapter's IP address | |
This support routine is called by EslSocketBindTest. | |
@param [in] pPort Address of an ::ESL_PORT structure. | |
@param [in] pConfigData Address of the configuration data | |
@retval EFI_SUCCESS - The IP address is valid | |
@retval EFI_NOT_STARTED - The IP address is invalid | |
**/ | |
EFI_STATUS | |
EslUdp4VerifyLocalIpAddress ( | |
IN ESL_PORT * pPort, | |
IN EFI_UDP4_CONFIG_DATA * pConfigData | |
) | |
{ | |
UINTN DataSize; | |
EFI_IP4_IPCONFIG_DATA * pIpConfigData; | |
EFI_IP4_CONFIG_PROTOCOL * pIpConfigProtocol; | |
ESL_SERVICE * pService; | |
EFI_STATUS Status; | |
DBG_ENTER ( ); | |
// | |
// Use break instead of goto | |
// | |
pIpConfigData = NULL; | |
for ( ; ; ) { | |
// | |
// Determine if the IP address is specified | |
// | |
DEBUG (( DEBUG_BIND, | |
"UseDefaultAddress: %s\r\n", | |
pConfigData->UseDefaultAddress ? L"TRUE" : L"FALSE" )); | |
DEBUG (( DEBUG_BIND, | |
"Requested IP address: %d.%d.%d.%d\r\n", | |
pConfigData->StationAddress.Addr [ 0 ], | |
pConfigData->StationAddress.Addr [ 1 ], | |
pConfigData->StationAddress.Addr [ 2 ], | |
pConfigData->StationAddress.Addr [ 3 ])); | |
if ( pConfigData->UseDefaultAddress | |
|| (( 0 == pConfigData->StationAddress.Addr [ 0 ]) | |
&& ( 0 == pConfigData->StationAddress.Addr [ 1 ]) | |
&& ( 0 == pConfigData->StationAddress.Addr [ 2 ]) | |
&& ( 0 == pConfigData->StationAddress.Addr [ 3 ]))) | |
{ | |
Status = EFI_SUCCESS; | |
break; | |
} | |
// | |
// Open the configuration protocol | |
// | |
pService = pPort->pService; | |
Status = gBS->OpenProtocol ( pService->Controller, | |
&gEfiIp4ConfigProtocolGuid, | |
(VOID **)&pIpConfigProtocol, | |
NULL, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL ); | |
if ( EFI_ERROR ( Status )) { | |
DEBUG (( DEBUG_ERROR, | |
"ERROR - IP Configuration Protocol not available, Status: %r\r\n", | |
Status )); | |
break; | |
} | |
// | |
// Get the IP configuration data size | |
// | |
DataSize = 0; | |
Status = pIpConfigProtocol->GetData ( pIpConfigProtocol, | |
&DataSize, | |
NULL ); | |
if ( EFI_BUFFER_TOO_SMALL != Status ) { | |
DEBUG (( DEBUG_ERROR, | |
"ERROR - Failed to get IP Configuration data size, Status: %r\r\n", | |
Status )); | |
break; | |
} | |
// | |
// Allocate the configuration data buffer | |
// | |
pIpConfigData = AllocatePool ( DataSize ); | |
if ( NULL == pIpConfigData ) { | |
DEBUG (( DEBUG_ERROR, | |
"ERROR - Not enough memory to allocate IP Configuration data!\r\n" )); | |
Status = EFI_OUT_OF_RESOURCES; | |
break; | |
} | |
// | |
// Get the IP configuration | |
// | |
Status = pIpConfigProtocol->GetData ( pIpConfigProtocol, | |
&DataSize, | |
pIpConfigData ); | |
if ( EFI_ERROR ( Status )) { | |
DEBUG (( DEBUG_ERROR, | |
"ERROR - Failed to return IP Configuration data, Status: %r\r\n", | |
Status )); | |
break; | |
} | |
// | |
// Display the current configuration | |
// | |
DEBUG (( DEBUG_BIND, | |
"Actual adapter IP address: %d.%d.%d.%d\r\n", | |
pIpConfigData->StationAddress.Addr [ 0 ], | |
pIpConfigData->StationAddress.Addr [ 1 ], | |
pIpConfigData->StationAddress.Addr [ 2 ], | |
pIpConfigData->StationAddress.Addr [ 3 ])); | |
// | |
// Assume the port is not configured | |
// | |
Status = EFI_SUCCESS; | |
if (( pConfigData->StationAddress.Addr [ 0 ] == pIpConfigData->StationAddress.Addr [ 0 ]) | |
&& ( pConfigData->StationAddress.Addr [ 1 ] == pIpConfigData->StationAddress.Addr [ 1 ]) | |
&& ( pConfigData->StationAddress.Addr [ 2 ] == pIpConfigData->StationAddress.Addr [ 2 ]) | |
&& ( pConfigData->StationAddress.Addr [ 3 ] == pIpConfigData->StationAddress.Addr [ 3 ])) { | |
break; | |
} | |
// | |
// The IP address did not match | |
// | |
Status = EFI_NOT_STARTED; | |
break; | |
} | |
// | |
// Free the buffer if necessary | |
// | |
if ( NULL != pIpConfigData ) { | |
FreePool ( pIpConfigData ); | |
} | |
// | |
// Return the IP address status | |
// | |
DBG_EXIT_STATUS ( Status ); | |
return Status; | |
} | |
/** | |
Interface between the socket layer and the network specific | |
code that supports SOCK_DGRAM sockets over UDPv4. | |
**/ | |
CONST ESL_PROTOCOL_API cEslUdp4Api = { | |
"UDPv4", | |
IPPROTO_UDP, | |
OFFSET_OF ( ESL_PORT, Context.Udp4.ConfigData ), | |
OFFSET_OF ( ESL_LAYER, pUdp4List ), | |
OFFSET_OF ( struct sockaddr_in, sin_zero ), | |
sizeof ( struct sockaddr_in ), | |
AF_INET, | |
sizeof (((ESL_PACKET *)0 )->Op.Udp4Rx ), | |
sizeof (((ESL_PACKET *)0 )->Op.Udp4Rx ), | |
OFFSET_OF ( ESL_IO_MGMT, Token.Udp4Rx.Packet.RxData ), | |
FALSE, | |
EADDRINUSE, | |
NULL, // Accept | |
NULL, // ConnectPoll | |
NULL, // ConnectStart | |
EslUdp4SocketIsConfigured, | |
EslUdp4LocalAddressGet, | |
EslUdp4LocalAddressSet, | |
NULL, // Listen | |
NULL, // OptionGet | |
NULL, // OptionSet | |
EslUdp4PacketFree, | |
EslUdp4PortAllocate, | |
NULL, // PortClose, | |
NULL, // PortCloseOp | |
TRUE, | |
EslUdp4Receive, | |
EslUdp4RemoteAddressGet, | |
EslUdp4RemoteAddressSet, | |
EslUdp4RxComplete, | |
NULL, // RxStart | |
EslUdp4TxBuffer, | |
EslUdp4TxComplete, | |
NULL, // TxOobComplete | |
(PFN_API_VERIFY_LOCAL_IP_ADDRESS)EslUdp4VerifyLocalIpAddress | |
}; |