From 3bdf9aae5f7f4c5f47fc8f807ae4409dfe3bdd58 Mon Sep 17 00:00:00 2001 From: lpleahy Date: Thu, 9 Feb 2012 19:16:44 +0000 Subject: Merged socket development branch: * Fixed bug report (Duane Voth: Python sockets test application not working) by starting the receive operations when a connection is established! * Increased performance by extending the idle loop into the network stack with the Poll call. * Added support for TCPv6 (SOCK_STREAM) and UDPv6 (SOCK_DGRAM). * Added support for getaddrinfo and getnameinfo calls. * Moved application PCD values into AppPkg Signed-off-by: lpleahy git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13002 6f19259b-4bc3-4df7-8a09-765794883524 --- StdLib/EfiSocketLib/EfiSocketLib.inf | 8 + StdLib/EfiSocketLib/Ip4.c | 1 + StdLib/EfiSocketLib/Socket.c | 103 +- StdLib/EfiSocketLib/Socket.h | 125 ++ StdLib/EfiSocketLib/Tcp4.c | 1 + StdLib/EfiSocketLib/Tcp6.c | 2329 +++++++++++++++++++++++++++++++++ StdLib/EfiSocketLib/Udp4.c | 1 + StdLib/EfiSocketLib/Udp6.c | 1094 ++++++++++++++++ StdLib/EfiSocketLib/UseEfiSocketLib.c | 31 +- 9 files changed, 3684 insertions(+), 9 deletions(-) create mode 100644 StdLib/EfiSocketLib/Tcp6.c create mode 100644 StdLib/EfiSocketLib/Udp6.c (limited to 'StdLib/EfiSocketLib') diff --git a/StdLib/EfiSocketLib/EfiSocketLib.inf b/StdLib/EfiSocketLib/EfiSocketLib.inf index df639527f6..5e6ff595a9 100644 --- a/StdLib/EfiSocketLib/EfiSocketLib.inf +++ b/StdLib/EfiSocketLib/EfiSocketLib.inf @@ -34,7 +34,9 @@ Service.c Socket.c Tcp4.c + Tcp6.c Udp4.c + Udp6.c UseEfiSocketLib.c [Packages] @@ -52,9 +54,15 @@ [Protocols] gEfiIp4ProtocolGuid gEfiIp4ServiceBindingProtocolGuid + gEfiIp6ProtocolGuid + gEfiIp6ServiceBindingProtocolGuid gEfiTcp4ProtocolGuid gEfiTcp4ServiceBindingProtocolGuid + gEfiTcp6ProtocolGuid + gEfiTcp6ServiceBindingProtocolGuid gEfiUdp4ProtocolGuid gEfiUdp4ServiceBindingProtocolGuid + gEfiUdp6ProtocolGuid + gEfiUdp6ServiceBindingProtocolGuid gEfiSocketProtocolGuid gEfiSocketServiceBindingProtocolGuid diff --git a/StdLib/EfiSocketLib/Ip4.c b/StdLib/EfiSocketLib/Ip4.c index 9f885e96bf..f295b4250e 100644 --- a/StdLib/EfiSocketLib/Ip4.c +++ b/StdLib/EfiSocketLib/Ip4.c @@ -383,6 +383,7 @@ EslIp4PortAllocate ( // pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.IPv4->Configure; pPort->pfnRxCancel = (PFN_NET_IO_START)pPort->pProtocol.IPv4->Cancel; + pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.IPv4->Poll; pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.IPv4->Receive; pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.IPv4->Transmit; diff --git a/StdLib/EfiSocketLib/Socket.c b/StdLib/EfiSocketLib/Socket.c index 9c2d2f6844..1805fa629c 100644 --- a/StdLib/EfiSocketLib/Socket.c +++ b/StdLib/EfiSocketLib/Socket.c @@ -481,6 +481,14 @@ CONST ESL_SOCKET_BINDING cEslSocketBinding[] = { 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, @@ -488,6 +496,14 @@ CONST ESL_SOCKET_BINDING cEslSocketBinding[] = { 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 }; @@ -516,11 +532,11 @@ CONST int cEslAfInetApiSize = DIM ( cEslAfInetApi ); **/ CONST ESL_PROTOCOL_API * cEslAfInet6Api[] = { NULL, // 0 - NULL, // SOCK_STREAM - NULL, // SOCK_DGRAM + &cEslTcp6Api, // SOCK_STREAM + &cEslUdp6Api, // SOCK_DGRAM NULL, // SOCK_RAW NULL, // SOCK_RDM - NULL // SOCK_SEQPACKET + &cEslTcp6Api // SOCK_SEQPACKET }; /** @@ -603,6 +619,7 @@ EslSocket ( // 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" )); @@ -1789,6 +1806,11 @@ EslSocketConnect ( if ( EFI_NOT_READY != Status ) { if ( !EFI_ERROR ( Status )) { pSocket->State = SOCKET_STATE_CONNECTED; + + // + // Start the receive operations + // + EslSocketRxStart ( pSocket->pPortList ); } else { pSocket->State = SOCKET_STATE_BOUND; @@ -1980,7 +2002,8 @@ EslSocketGetLocalAddress ( // // Verify the socket state // - if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + if (( SOCKET_STATE_CONNECTED == pSocket->State ) + || ( SOCKET_STATE_LISTENING == pSocket->State )) { // // Verify the API // @@ -3096,7 +3119,7 @@ EslSocketPacketAllocate ( LengthInBytes, (VOID **)&pPacket ); if ( !EFI_ERROR ( Status )) { - DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, + DEBUG (( DebugFlags | DEBUG_POOL, "0x%08x: Allocate pPacket, %d bytes\r\n", pPacket, LengthInBytes )); @@ -3210,6 +3233,7 @@ EslSocketPoll ( short DetectedEvents; ESL_SOCKET * pSocket; EFI_STATUS Status; + EFI_TPL TplPrevious; short ValidEvents; DEBUG (( DEBUG_POLL, "Entering SocketPoll\r\n" )); @@ -3247,6 +3271,22 @@ EslSocketPoll ( 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 // @@ -4366,6 +4406,11 @@ EslSocketReceive ( // Verify that the socket is connected // if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + // + // Poll the network to increase performance + // + EslSocketRxPoll ( pSocket ); + // // Locate the port // @@ -4847,6 +4892,49 @@ EslSocketRxComplete ( } +/** + 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 @@ -5291,6 +5379,11 @@ EslSocketTransmit ( // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + // + // Poll the network to increase performance + // + EslSocketRxPoll ( pSocket ); + // // Attempt to buffer the packet for transmission // diff --git a/StdLib/EfiSocketLib/Socket.h b/StdLib/EfiSocketLib/Socket.h index 50bb2d3efd..30b642000e 100644 --- a/StdLib/EfiSocketLib/Socket.h +++ b/StdLib/EfiSocketLib/Socket.h @@ -137,6 +137,26 @@ typedef struct } ESL_TCP4_TX_DATA; +/** + Receive context for SOCK_STREAM and SOCK_SEQPACKET sockets using TCPv6. +**/ +typedef struct +{ + EFI_TCP6_RECEIVE_DATA RxData; ///< Receive operation description + UINT8 Buffer[ RX_PACKET_DATA ]; ///< Data buffer +} ESL_TCP6_RX_DATA; + + +/** + Transmit context for SOCK_STREAM and SOCK_SEQPACKET sockets using TCPv6. +**/ +typedef struct +{ + EFI_TCP6_TRANSMIT_DATA TxData; ///< Transmit operation description + UINT8 Buffer[ 1 ]; ///< Data buffer +} ESL_TCP6_TX_DATA; + + /** Receive context for SOCK_DGRAM sockets using UDPv4. **/ @@ -159,6 +179,28 @@ typedef struct } ESL_UDP4_TX_DATA; +/** + Receive context for SOCK_DGRAM sockets using UDPv6. +**/ +typedef struct +{ + EFI_UDP6_SESSION_DATA Session; ///< Remote network address + EFI_UDP6_RECEIVE_DATA * pRxData; ///< Receive operation description +} ESL_UDP6_RX_DATA; + + +/** + Transmit context for SOCK_DGRAM sockets using UDPv6. +**/ +typedef struct +{ + EFI_UDP6_SESSION_DATA Session; ///< Remote network address + EFI_UDP6_TRANSMIT_DATA TxData; ///< Transmit operation description + UINTN RetransmitCount; ///< Retransmit to handle ARP negotiation + UINT8 Buffer[ 1 ]; ///< Data buffer +} ESL_UDP6_TX_DATA; + + /** Network specific context for transmit and receive packets. **/ @@ -172,8 +214,12 @@ typedef struct _ESL_PACKET { ESL_IP4_TX_DATA Ip4Tx; ///< Transmit operation description ESL_TCP4_RX_DATA Tcp4Rx; ///< Receive operation description ESL_TCP4_TX_DATA Tcp4Tx; ///< Transmit operation description + ESL_TCP6_RX_DATA Tcp6Rx; ///< Receive operation description + ESL_TCP6_TX_DATA Tcp6Tx; ///< Transmit operation description ESL_UDP4_RX_DATA Udp4Rx; ///< Receive operation description ESL_UDP4_TX_DATA Udp4Tx; ///< Transmit operation description + ESL_UDP6_RX_DATA Udp6Rx; ///< Receive operation description + ESL_UDP6_TX_DATA Udp6Tx; ///< Transmit operation description } Op; ///< Network specific context } GCC_ESL_PACKET; @@ -217,8 +263,12 @@ typedef struct _ESL_IO_MGMT { EFI_IP4_COMPLETION_TOKEN Ip4Tx; ///< IP4 transmit token EFI_TCP4_IO_TOKEN Tcp4Rx; ///< TCP4 receive token EFI_TCP4_IO_TOKEN Tcp4Tx; ///< TCP4 transmit token + EFI_TCP6_IO_TOKEN Tcp6Rx; ///< TCP6 receive token + EFI_TCP6_IO_TOKEN Tcp6Tx; ///< TCP6 transmit token EFI_UDP4_COMPLETION_TOKEN Udp4Rx; ///< UDP4 receive token EFI_UDP4_COMPLETION_TOKEN Udp4Tx; ///< UDP4 transmit token + EFI_UDP6_COMPLETION_TOKEN Udp6Rx; ///< UDP6 receive token + EFI_UDP6_COMPLETION_TOKEN Udp6Tx; ///< UDP6 transmit token } Token; ///< Completion token for the network operation } GCC_IO_MGMT; @@ -256,6 +306,26 @@ typedef struct { EFI_TCP4_CLOSE_TOKEN CloseToken; ///< Close control } ESL_TCP4_CONTEXT; +/** + TCP6 context structure + + The driver uses this structure to manage the TCP6 connections. +**/ +typedef struct { + // + // TCP6 context + // + EFI_TCP6_CONFIG_DATA ConfigData; ///< TCP6 configuration data + EFI_TCP6_OPTION Option; ///< TCP6 port options + + // + // Tokens + // + EFI_TCP6_LISTEN_TOKEN ListenToken; ///< Listen control + EFI_TCP6_CONNECTION_TOKEN ConnectToken; ///< Connection control + EFI_TCP6_CLOSE_TOKEN CloseToken; ///< Close control +} ESL_TCP6_CONTEXT; + /** UDP4 context structure @@ -268,6 +338,18 @@ typedef struct { EFI_UDP4_CONFIG_DATA ConfigData; ///< UDP4 configuration data } ESL_UDP4_CONTEXT; +/** + UDP6 context structure + + The driver uses this structure to manage the UDP6 connections. +**/ +typedef struct { + // + // UDP6 context + // + EFI_UDP6_CONFIG_DATA ConfigData; ///< UDP6 configuration data +} ESL_UDP6_CONTEXT; + /** Configure the network layer. @@ -301,6 +383,21 @@ EFI_STATUS IN VOID * pToken ); +/** + Poll the LAN adapter for receive packets. + + @param [in] pProtocol Protocol structure address + @param [in] pToken Completion token address + + @return Returns EFI_SUCCESS if the operation is successfully + started. +**/ +typedef +EFI_STATUS +(* PFN_NET_POLL) ( + IN VOID * pProtocol + ); + /** Port control structure @@ -353,6 +450,7 @@ typedef struct _ESL_PORT { // Receive data management // PFN_NET_IO_START pfnRxCancel; ///< Cancel a receive on the network + PFN_NET_POLL pfnRxPoll; ///< Poll the LAN adapter for receive packets PFN_NET_IO_START pfnRxStart; ///< Start a receive on the network ESL_IO_MGMT * pRxActive; ///< Active receive operation queue ESL_IO_MGMT * pRxFree; ///< Free structure queue @@ -364,12 +462,16 @@ typedef struct _ESL_PORT { VOID * v; ///< VOID pointer EFI_IP4_PROTOCOL * IPv4; ///< IP4 protocol pointer EFI_TCP4_PROTOCOL * TCPv4; ///< TCP4 protocol pointer + EFI_TCP6_PROTOCOL * TCPv6; ///< TCP6 protocol pointer EFI_UDP4_PROTOCOL * UDPv4; ///< UDP4 protocol pointer + EFI_UDP6_PROTOCOL * UDPv6; ///< UDP6 protocol pointer } pProtocol; ///< Protocol structure address union { ESL_IP4_CONTEXT Ip4; ///< IPv4 management data ESL_TCP4_CONTEXT Tcp4; ///< TCPv4 management data + ESL_TCP6_CONTEXT Tcp6; ///< TCPv6 management data ESL_UDP4_CONTEXT Udp4; ///< UDPv4 management data + ESL_UDP6_CONTEXT Udp6; ///< UDPv6 management data } Context; ///< Network specific context }GCC_ESL_PORT; @@ -975,7 +1077,9 @@ typedef struct { // ESL_SERVICE * pIp4List; ///< List of Ip4 services ESL_SERVICE * pTcp4List; ///< List of Tcp4 services + ESL_SERVICE * pTcp6List; ///< List of Tcp6 services ESL_SERVICE * pUdp4List; ///< List of Udp4 services + ESL_SERVICE * pUdp6List; ///< List of Udp6 services // // Socket management @@ -992,8 +1096,11 @@ typedef struct { extern ESL_LAYER mEslLayer; extern CONST ESL_PROTOCOL_API cEslIp4Api; +extern CONST ESL_PROTOCOL_API cEslIp6Api; extern CONST ESL_PROTOCOL_API cEslTcp4Api; +extern CONST ESL_PROTOCOL_API cEslTcp6Api; extern CONST ESL_PROTOCOL_API cEslUdp4Api; +extern CONST ESL_PROTOCOL_API cEslUdp6Api; extern CONST EFI_SERVICE_BINDING_PROTOCOL mEfiServiceBinding; @@ -1423,6 +1530,24 @@ EslSocketRxComplete ( IN BOOLEAN bUrgent ); +/** + 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 + ); + /** Start a receive operation diff --git a/StdLib/EfiSocketLib/Tcp4.c b/StdLib/EfiSocketLib/Tcp4.c index ec03b96bc1..b3ed45a716 100644 --- a/StdLib/EfiSocketLib/Tcp4.c +++ b/StdLib/EfiSocketLib/Tcp4.c @@ -1307,6 +1307,7 @@ EslTcp4PortAllocate ( // pPort->pfnRxCancel = NULL; since the UEFI implementation returns EFI_UNSUPPORTED // pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.TCPv4->Configure; + pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.TCPv4->Poll; pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv4->Receive; pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv4->Transmit; diff --git a/StdLib/EfiSocketLib/Tcp6.c b/StdLib/EfiSocketLib/Tcp6.c new file mode 100644 index 0000000000..0ee9fb6a64 --- /dev/null +++ b/StdLib/EfiSocketLib/Tcp6.c @@ -0,0 +1,2329 @@ +/** @file + Implement the TCP6 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. + + + \section ConnectionManagement Connection Management + + The ::EslTcp6Listen routine initially places the SOCK_STREAM or + SOCK_SEQPACKET socket into a listen state. When a remote machine + makes a connection to the socket, the TCPv6 network layer calls + ::EslTcp6ListenComplete to complete the connection processing. + EslTcp6ListenComplete manages the connections by placing them in + FIFO order in a queue to be serviced by the application. When the + number of connections exceeds the backlog (ESL_SOCKET::MaxFifoDepth), + the new connection is closed. Eventually, the application indirectly + calls ::EslTcp6Accept to remove the next connection from the queue + and get the associated socket. + +**/ + +#include "Socket.h" + + +/** + Attempt to connect to a remote TCP port + + This routine starts the connection processing for a SOCK_STREAM + or SOCK_SEQPAKCET socket using the TCPv6 network layer. It + configures the local TCPv6 connection point and then attempts to + connect to a remote system. Upon completion, the + ::EslTcp6ConnectComplete routine gets called with the connection + status. + + This routine is called by ::EslSocketConnect to initiate the TCPv6 + network specific connect operations. The connection processing is + initiated by this routine and finished by ::EslTcp6ConnectComplete. + This pair of routines walks through the list of local TCPv6 + connection points until a connection to the remote system is + made. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @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 +EslTcp6ConnectStart ( + IN ESL_SOCKET * pSocket + ); + + +/** + Process the connection attempt + + A system has initiated a connection attempt with a socket in the + listen state. Attempt to complete the connection. + + The TCPv6 layer calls this routine when a connection is made to + the socket in the listen state. See the + \ref ConnectionManagement section. + + @param [in] Event The listen completion event + + @param [in] pPort Address of an ::ESL_PORT structure. + +**/ +VOID +EslTcp6ListenComplete ( + IN EFI_EVENT Event, + IN ESL_PORT * pPort + ); + + +/** + Accept a network connection. + + This routine waits for a network connection to the socket and + returns the remote network address to the caller if requested. + + This routine is called by ::EslSocketAccept to handle the TCPv6 protocol + specific accept operations for SOCK_STREAM and SOCK_SEQPACKET sockets. + See the \ref ConnectionManagement section. + + @param [in] pSocket Address of an ::ESL_SOCKET 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. + + @retval EFI_SUCCESS Remote address is available + @retval Others Remote address not available + + **/ +EFI_STATUS +EslTcp6Accept ( + IN ESL_SOCKET * pSocket, + IN struct sockaddr * pSockAddr, + IN OUT socklen_t * pSockAddrLength + ) +{ + ESL_PORT * pPort; + struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Validate the socket length + // + pRemoteAddress = (struct sockaddr_in6 *) pSockAddr; + if (( NULL == pSockAddrLength ) + || ( sizeof ( *pRemoteAddress ) > *pSockAddrLength )) { + // + // Invalid socket address + // + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + DEBUG (( DEBUG_ACCEPT, + "ERROR - Invalid address length\r\n" )); + } + else { + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Locate the address context + // + pPort = pSocket->pPortList; + pTcp6 = &pPort->Context.Tcp6; + + // + // Fill-in the remote address structure + // + ZeroMem ( pRemoteAddress, sizeof ( *pRemoteAddress )); + pRemoteAddress->sin6_len = sizeof ( *pRemoteAddress ); + pRemoteAddress->sin6_family = AF_INET6; + pRemoteAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.RemotePort ); + CopyMem ( &pRemoteAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the remote connection completion event. + + This routine handles the completion of a connection attempt. It + releases the port (TCPv6 adapter connection) in the case of an + error and start a connection attempt on the next port. If the + connection attempt was successful then this routine releases all + of the other ports. + + This routine is called by the TCPv6 layer when a connect request + completes. It sets the ESL_SOCKET::bConnected flag to notify the + ::EslTcp6ConnectComplete routine that the connection is available. + The flag is set when the connection is established or no more ports + exist in the list. The connection status is passed via + ESL_SOCKET::ConnectStatus. + + @param [in] Event The connect completion event + + @param [in] pPort Address of an ::ESL_PORT structure. + +**/ +VOID +EslTcp6ConnectComplete ( + IN EFI_EVENT Event, + IN ESL_PORT * pPort + ) +{ + BOOLEAN bRemoveFirstPort; + BOOLEAN bRemovePorts; + ESL_PORT * pNextPort; + ESL_SOCKET * pSocket; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the TCP context + // + pSocket = pPort->pSocket; + pTcp6 = &pPort->Context.Tcp6; + + // + // Get the connection status + // + bRemoveFirstPort = FALSE; + bRemovePorts = FALSE; + Status = pTcp6->ConnectToken.CompletionToken.Status; + pSocket->ConnectStatus = Status; + if ( !EFI_ERROR ( Status )) { + // + // The connection was successful + // + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port connected to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort )); + + // + // Remove the rest of the ports + // + bRemovePorts = TRUE; + } + else { + // + // The connection failed + // + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port connection to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d failed, Status: %r\r\n", + pPort, + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort, + Status )); + + // + // Close the current port + // + Status = EslSocketPortClose ( pPort ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port closed\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to close port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } + + // + // Try to connect using the next port + // + Status = EslTcp6ConnectStart ( pSocket ); + if ( EFI_NOT_READY != Status ) { + pSocket->ConnectStatus = Status; + bRemoveFirstPort = TRUE; + } + } + + // + // Remove the ports if necessary + // + if ( bRemoveFirstPort || bRemovePorts ) { + // + // Remove the first port if necessary + // + pPort = pSocket->pPortList; + if (( !bRemoveFirstPort ) && ( NULL != pPort )) { + pPort = pPort->pLinkSocket; + } + + // + // Remove the rest of the list + // + while ( NULL != pPort ) { + pNextPort = pPort->pLinkSocket; + EslSocketPortClose ( pPort ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port closed\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to close port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } + pPort = pNextPort; + } + + // + // Notify the poll routine + // + pSocket->bConnected = TRUE; + } + + DBG_EXIT ( ); +} + + +/** + Poll for completion of the connection attempt. + + This routine polls the ESL_SOCKET::bConnected flag to determine + when the connection attempt is complete. + + This routine is called from ::EslSocketConnect to determine when + the connection is complete. The ESL_SOCKET::bConnected flag is + set by ::EslTcp6ConnectComplete when the TCPv6 layer establishes + a connection or runs out of local network adapters. This routine + gets the connection status from ESL_SOCKET::ConnectStatus. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @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 +EslTcp6ConnectPoll ( + IN ESL_SOCKET * pSocket + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if the connection is complete + // + if ( !pSocket->bConnected ) { + // + // Not connected + // + pSocket->errno = EAGAIN; + Status = EFI_NOT_READY; + } + else { + // + // The connection processing is complete + // + pSocket->bConnected = FALSE; + + // + // Translate the connection status + // + Status = pSocket->ConnectStatus; + switch ( Status ) { + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_ABORTED: + pSocket->errno = ECONNREFUSED; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EINVAL; + break; + + case EFI_NO_MAPPING: + case EFI_NO_RESPONSE: + pSocket->errno = EHOSTUNREACH; + break; + + case EFI_NO_MEDIA: + pSocket->errno = ENETDOWN; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOMEM; + break; + + case EFI_SUCCESS: + pSocket->errno = 0; + pSocket->bConfigured = TRUE; + break; + + case EFI_TIMEOUT: + pSocket->errno = ETIMEDOUT; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = ENOTSUP; + break; + + case 0x80000069: + pSocket->errno = ECONNRESET; + break; + } + } + + // + // Return the initialization status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Attempt to connect to a remote TCP port + + This routine starts the connection processing for a SOCK_STREAM + or SOCK_SEQPAKCET socket using the TCPv6 network layer. It + configures the local TCPv6 connection point and then attempts to + connect to a remote system. Upon completion, the + ::EslTcp6ConnectComplete routine gets called with the connection + status. + + This routine is called by ::EslSocketConnect to initiate the TCPv6 + network specific connect operations. The connection processing is + initiated by this routine and finished by ::EslTcp6ConnectComplete. + This pair of routines walks through the list of local TCPv6 + connection points until a connection to the remote system is + made. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @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 +EslTcp6ConnectStart ( + IN ESL_SOCKET * pSocket + ) +{ + ESL_PORT * pPort; + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if any more local adapters are available + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Configure the port + // + pTcp6 = &pPort->Context.Tcp6; + pTcp6->ConfigData.AccessPoint.ActiveFlag = TRUE; + pTcp6->ConfigData.TrafficClass = 0; + pTcp6->ConfigData.HopLimit = 255; + pTcp6Protocol = pPort->pProtocol.TCPv6; + Status = pTcp6Protocol->Configure ( pTcp6Protocol, + &pTcp6->ConfigData ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to configure the Tcp6 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_CONNECT, + "0x%08x: Port configured\r\n", + pPort )); + pPort->bConfigured = TRUE; + + // + // Attempt the connection to the remote system + // + Status = pTcp6Protocol->Connect ( pTcp6Protocol, + &pTcp6->ConnectToken ); + if ( !EFI_ERROR ( Status )) { + // + // Connection in progress + // + pSocket->errno = EINPROGRESS; + Status = EFI_NOT_READY; + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port attempting connection to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort )); + } + else { + // + // Connection error + // + DEBUG (( DEBUG_CONNECT, + "ERROR - Port 0x%08x not connected, Status: %r\r\n", + pPort, + Status )); + // + // Determine the errno value + // + switch ( Status ) { + default: + pSocket->errno = EIO; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_TIMEOUT: + pSocket->errno = ETIMEDOUT; + break; + + case EFI_NETWORK_UNREACHABLE: + pSocket->errno = ENETDOWN; + break; + + case EFI_HOST_UNREACHABLE: + pSocket->errno = EHOSTUNREACH; + break; + + case EFI_PORT_UNREACHABLE: + case EFI_PROTOCOL_UNREACHABLE: + case EFI_CONNECTION_REFUSED: + pSocket->errno = ECONNREFUSED; + break; + + case EFI_CONNECTION_RESET: + pSocket->errno = ECONNRESET; + break; + } + } + } + } + else { + // + // No more local adapters available + // + pSocket->errno = ENETUNREACH; + Status = EFI_NO_RESPONSE; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Establish the known port to listen for network connections. + + This routine places the port into a state that enables connection + attempts. + + This routine is called by ::EslSocketListen to handle the network + specifics of the listen operation for SOCK_STREAM and SOCK_SEQPACKET + sockets. See the \ref ConnectionManagement section. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @retval EFI_SUCCESS - Socket successfully created + @retval Other - Failed to enable the socket for listen + +**/ +EFI_STATUS +EslTcp6Listen ( + IN ESL_SOCKET * pSocket + ) +{ + ESL_PORT * pNextPort; + ESL_PORT * pPort; + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Use for/break instead of goto + // + for ( ; ; ) { + // + // Assume no ports are available + // + pSocket->errno = EOPNOTSUPP; + Status = EFI_NOT_READY; + + // + // Walk the list of ports + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Assume success + // + pSocket->errno = 0; + + // + // Use for/break insteak of goto + // + for ( ; ; ) { + // + // Create the listen completion event + // + pTcp6 = &pPort->Context.Tcp6; + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcp6ListenComplete, + pPort, + &pTcp6->ListenToken.CompletionToken.Event ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DEBUG_LISTEN, + "ERROR - Failed to create the listen completion event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_POOL, + "0x%08x: Created listen completion event\r\n", + pTcp6->ListenToken.CompletionToken.Event )); + + // + // Configure the port + // + pTcp6Protocol = pPort->pProtocol.TCPv6; + Status = pTcp6Protocol->Configure ( pTcp6Protocol, + &pTcp6->ConfigData ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed to configure the Tcp6 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; + } + break; + } + DEBUG (( DEBUG_LISTEN, + "0x%08x: Port configured\r\n", + pPort )); + pPort->bConfigured = TRUE; + + // + // Start the listen operation on the port + // + Status = pTcp6Protocol->Accept ( pTcp6Protocol, + &pTcp6->ListenToken ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed Tcp6 accept, 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_NOT_STARTED: + pSocket->errno = ENETDOWN; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + } + break; + } + DEBUG (( DEBUG_LISTEN, + "0x%08x: Listen pending on Port\r\n", + pPort )); + + // + // Listen is pending on this port + // + break; + } + + // + // Get the next port + // + pNextPort = pPort->pLinkSocket; + + // + // Close the port upon error + // + if ( EFI_ERROR ( Status )) { + EslSocketPortCloseStart ( pPort, TRUE, DEBUG_LISTEN ); + } + + // + // Set the next port + // + pPort = pNextPort; + } + + // + // Determine if any ports are in the listen state + // + if ( NULL == pSocket->pPortList ) { + // + // No ports in the listen state + // + pSocket->MaxFifoDepth = 0; + + // + // Return the last error detected + // + break; + } + + // + // Mark the socket as configured + // + pSocket->bConfigured = TRUE; + + // + // All done + // + DEBUG (( DEBUG_LISTEN, + "0x%08x: pSocket - Listen pending on socket\r\n", + pSocket )); + break; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the connection attempt + + A system has initiated a connection attempt with a socket in the + listen state. Attempt to complete the connection. + + The TCPv6 layer calls this routine when a connection is made to + the socket in the listen state. See the + \ref ConnectionManagement section. + + @param [in] Event The listen completion event + + @param [in] pPort Address of an ::ESL_PORT structure. + +**/ +VOID +EslTcp6ListenComplete ( + IN EFI_EVENT Event, + IN ESL_PORT * pPort + ) +{ + EFI_HANDLE ChildHandle; + struct sockaddr_in6 LocalAddress; + EFI_TCP6_CONFIG_DATA * pConfigData; + ESL_LAYER * pLayer; + ESL_PORT * pNewPort; + ESL_SOCKET * pNewSocket; + ESL_SOCKET * pSocket; + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + EFI_HANDLE TcpPortHandle; + EFI_STATUS TempStatus; + + DBG_ENTER ( ); + VERIFY_AT_TPL ( TPL_SOCKETS ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Determine if this connection fits into the connection FIFO + // + pSocket = pPort->pSocket; + TcpPortHandle = pPort->Context.Tcp6.ListenToken.NewChildHandle; + if (( SOCKET_STATE_LISTENING == pSocket->State ) + && ( pSocket->MaxFifoDepth > pSocket->FifoDepth )) { + // + // Allocate a socket for this connection + // + ChildHandle = NULL; + pLayer = &mEslLayer; + Status = EslSocketAllocate ( &ChildHandle, + DEBUG_CONNECTION, + &pNewSocket ); + if ( !EFI_ERROR ( Status )) { + // + // Clone the socket parameters + // + pNewSocket->pApi = pSocket->pApi; + pNewSocket->Domain = pSocket->Domain; + pNewSocket->Protocol = pSocket->Protocol; + pNewSocket->Type = pSocket->Type; + + // + // Build the local address + // + pTcp6 = &pPort->Context.Tcp6; + LocalAddress.sin6_len = (uint8_t)pNewSocket->pApi->MinimumAddressLength; + LocalAddress.sin6_family = AF_INET6; + LocalAddress.sin6_port = 0; + CopyMem ( &LocalAddress.sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + &pTcp6->ConfigData.AccessPoint.StationAddress.Addr [ 0 ], + sizeof ( pTcp6->ConfigData.AccessPoint.StationAddress.Addr )); + + // + // Allocate a port for this connection + // Note in this instance Configure may not be called with NULL! + // + Status = EslSocketPortAllocate ( pNewSocket, + pPort->pService, + TcpPortHandle, + (struct sockaddr *)&LocalAddress, + FALSE, + DEBUG_CONNECTION, + &pNewPort ); + if ( !EFI_ERROR ( Status )) { + // + // Restart the listen operation on the port + // + pTcp6Protocol = pPort->pProtocol.TCPv6; + Status = pTcp6Protocol->Accept ( pTcp6Protocol, + &pTcp6->ListenToken ); + + // + // Close the TCP port using SocketClose + // + TcpPortHandle = NULL; + pTcp6 = &pNewPort->Context.Tcp6; + + // + // Check for an accept call error + // + if ( !EFI_ERROR ( Status )) { + // + // Get the port configuration + // + pNewPort->bConfigured = TRUE; + pConfigData = &pTcp6->ConfigData; + pConfigData->ControlOption = &pTcp6->Option; + pTcp6Protocol = pNewPort->pProtocol.TCPv6; + Status = pTcp6Protocol->GetModeData ( pTcp6Protocol, + NULL, + pConfigData, + NULL, + NULL, + NULL ); + if ( !EFI_ERROR ( Status )) { + // + // Add the new socket to the connection FIFO + // + if ( NULL == pSocket->pFifoTail ) { + // + // First connection + // + pSocket->pFifoHead = pNewSocket; + } + else { + // + // Add to end of list. + // + pSocket->pFifoTail->pNextConnection = pNewSocket; + } + pSocket->pFifoTail = pNewSocket; + pSocket->FifoDepth += 1; + + // + // Update the socket state + // + pNewSocket->State = SOCKET_STATE_IN_FIFO; + + // + // Log the connection + // + DEBUG (( DEBUG_CONNECTION | DEBUG_INFO, + "0x%08x: Socket on port [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d connected to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pNewSocket, + pConfigData->AccessPoint.StationAddress.Addr[0], + pConfigData->AccessPoint.StationAddress.Addr[1], + pConfigData->AccessPoint.StationAddress.Addr[2], + pConfigData->AccessPoint.StationAddress.Addr[3], + pConfigData->AccessPoint.StationAddress.Addr[4], + pConfigData->AccessPoint.StationAddress.Addr[5], + pConfigData->AccessPoint.StationAddress.Addr[6], + pConfigData->AccessPoint.StationAddress.Addr[7], + pConfigData->AccessPoint.StationAddress.Addr[8], + pConfigData->AccessPoint.StationAddress.Addr[9], + pConfigData->AccessPoint.StationAddress.Addr[10], + pConfigData->AccessPoint.StationAddress.Addr[11], + pConfigData->AccessPoint.StationAddress.Addr[12], + pConfigData->AccessPoint.StationAddress.Addr[13], + pConfigData->AccessPoint.StationAddress.Addr[14], + pConfigData->AccessPoint.StationAddress.Addr[15], + pConfigData->AccessPoint.StationPort, + pConfigData->AccessPoint.RemoteAddress.Addr[0], + pConfigData->AccessPoint.RemoteAddress.Addr[1], + pConfigData->AccessPoint.RemoteAddress.Addr[2], + pConfigData->AccessPoint.RemoteAddress.Addr[3], + pConfigData->AccessPoint.RemoteAddress.Addr[4], + pConfigData->AccessPoint.RemoteAddress.Addr[5], + pConfigData->AccessPoint.RemoteAddress.Addr[6], + pConfigData->AccessPoint.RemoteAddress.Addr[7], + pConfigData->AccessPoint.RemoteAddress.Addr[8], + pConfigData->AccessPoint.RemoteAddress.Addr[9], + pConfigData->AccessPoint.RemoteAddress.Addr[10], + pConfigData->AccessPoint.RemoteAddress.Addr[11], + pConfigData->AccessPoint.RemoteAddress.Addr[12], + pConfigData->AccessPoint.RemoteAddress.Addr[13], + pConfigData->AccessPoint.RemoteAddress.Addr[14], + pConfigData->AccessPoint.RemoteAddress.Addr[15], + pConfigData->AccessPoint.RemotePort )); + DEBUG (( DEBUG_CONNECTION | DEBUG_INFO, + "0x%08x: Listen socket adding socket 0x%08x to FIFO, depth: %d\r\n", + pSocket, + pNewSocket, + pSocket->FifoDepth )); + + // + // Start the receive operation + // + EslSocketRxStart ( pNewPort ); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_CONNECTION | DEBUG_INFO, + "ERROR - GetModeData failed on port 0x%08x, Status: %r\r\n", + pNewPort, + Status )); + } + } + else { + // + // The listen failed on this port + // + DEBUG (( DEBUG_LISTEN | DEBUG_INFO, + "ERROR - Listen failed on port 0x%08x, Status: %r\r\n", + pPort, + Status )); + + // + // Close the listening port + // + EslSocketPortCloseStart ( pPort, TRUE, DEBUG_LISTEN ); + } + } + + // + // Done with the socket if necessary + // + if ( EFI_ERROR ( Status )) { + TempStatus = EslSocketCloseStart ( &pNewSocket->SocketProtocol, + TRUE, + &pSocket->errno ); + ASSERT ( EFI_SUCCESS == TempStatus ); + } + } + } + else { + DEBUG (( DEBUG_CONNECTION, + "0x%08x: Socket FIFO full, connection refused\r\n", + pSocket )); + + // + // The FIFO is full or the socket is in the wrong state + // + Status = EFI_BUFFER_TOO_SMALL; + } + + // + // Close the connection if necessary + // + if (( EFI_ERROR ( Status )) + && ( NULL == TcpPortHandle )) { + // + // TODO: Finish this code path + // The new connection does not fit into the connection FIFO + // + // Process: + // Call close + // Release the resources + + } + + DBG_EXIT ( ); +} + + +/** + Get the local socket address. + + This routine returns the IPv6 address and TCP port number associated + with the local socket. + + This routine is called by ::EslSocketGetLocalAddress to determine the + network address for the SOCK_STREAM or SOCK_SEQPACKET socket. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pSockAddr Network address to receive the local system address + +**/ +VOID +EslTcp6LocalAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pSockAddr + ) +{ + struct sockaddr_in6 * pLocalAddress; + ESL_TCP6_CONTEXT * pTcp6; + + DBG_ENTER ( ); + + // + // Return the local address + // + pTcp6 = &pPort->Context.Tcp6; + pLocalAddress = (struct sockaddr_in6 *)pSockAddr; + pLocalAddress->sin6_family = AF_INET6; + pLocalAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.StationPort ); + CopyMem ( &pLocalAddress->sin6_addr, + &pTcp6->ConfigData.AccessPoint.StationAddress.Addr[0], + sizeof ( pLocalAddress->sin6_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 IPv6 address + of INADDR_ANY specifies that the connection is made to + all of the network stacks on the platform. Specifying a + specific IPv6 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 +EslTcp6LocalAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest + ) +{ + EFI_TCP6_ACCESS_POINT * pAccessPoint; + CONST struct sockaddr_in6 * pIpAddress; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Validate the address + // + pIpAddress = (struct sockaddr_in6 *)pSockAddr; +// +// TODO: Fix the following check +// +/* + if ( INADDR_BROADCAST == pIpAddress->sin6_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 + // + pAccessPoint = &pPort->Context.Tcp6.ConfigData.AccessPoint; + CopyMem ( &pAccessPoint->StationAddress.Addr[0], + &pIpAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + sizeof ( pIpAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ])); + + // + // Validate the IP address + // + pAccessPoint->StationPort = 0; + Status = bBindTest ? EslSocketBindTest ( pPort, EADDRNOTAVAIL ) + : EFI_SUCCESS; + if ( !EFI_ERROR ( Status )) { + // + // Set the port number + // + pAccessPoint->StationPort = SwapBytes16 ( pIpAddress->sin6_port ); + + // + // Display the local address + // + DEBUG (( DEBUG_BIND, + "0x%08x: Port, Local Tcp6 Address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pAccessPoint->StationAddress.Addr[0], + pAccessPoint->StationAddress.Addr[1], + pAccessPoint->StationAddress.Addr[2], + pAccessPoint->StationAddress.Addr[3], + pAccessPoint->StationAddress.Addr[4], + pAccessPoint->StationAddress.Addr[5], + pAccessPoint->StationAddress.Addr[6], + pAccessPoint->StationAddress.Addr[7], + pAccessPoint->StationAddress.Addr[8], + pAccessPoint->StationAddress.Addr[9], + pAccessPoint->StationAddress.Addr[10], + pAccessPoint->StationAddress.Addr[11], + pAccessPoint->StationAddress.Addr[12], + pAccessPoint->StationAddress.Addr[13], + pAccessPoint->StationAddress.Addr[14], + pAccessPoint->StationAddress.Addr[15], + pAccessPoint->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 +EslTcp6PacketFree ( + IN ESL_PACKET * pPacket, + IN OUT size_t * pRxBytes + ) +{ + DBG_ENTER ( ); + + // + // Account for the receive bytes + // + *pRxBytes -= pPacket->Op.Tcp6Rx.RxData.DataLength; + 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 TCPv6 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 +EslTcp6PortAllocate ( + IN ESL_PORT * pPort, + IN UINTN DebugFlags + ) +{ + EFI_TCP6_ACCESS_POINT * pAccessPoint; + ESL_SOCKET * pSocket; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Use for/break instead of goto + for ( ; ; ) { + // + // Allocate the close event + // + pSocket = pPort->pSocket; + pTcp6 = &pPort->Context.Tcp6; + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslSocketPortCloseComplete, + pPort, + &pTcp6->CloseToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the close event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Created close event\r\n", + pTcp6->CloseToken.CompletionToken.Event )); + + // + // Allocate the connection event + // + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcp6ConnectComplete, + pPort, + &pTcp6->ConnectToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the connect event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Created connect event\r\n", + pTcp6->ConnectToken.CompletionToken.Event )); + + // + // Initialize the port + // + pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Tcp6Tx.TxData ); + pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Tcp6Tx.CompletionToken.Event ); + pSocket->TxTokenOffset = OFFSET_OF ( EFI_TCP6_IO_TOKEN, Packet.TxData ); + + // + // Save the cancel, receive and transmit addresses + // pPort->pfnRxCancel = NULL; since the UEFI implementation returns EFI_UNSUPPORTED + // + pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.TCPv6->Configure; + pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.TCPv6->Poll; + pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv6->Receive; + pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv6->Transmit; + + // + // Set the configuration flags + // + pAccessPoint = &pPort->Context.Tcp6.ConfigData.AccessPoint; + pAccessPoint->ActiveFlag = FALSE; + pTcp6->ConfigData.TrafficClass = 0; + pTcp6->ConfigData.HopLimit = 255; + break; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Close a Tcp6 port. + + This routine releases the network specific resources allocated by + ::EslTcp6PortAllocate. + + This routine is called by ::EslSocketPortClose. + 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 +EslTcp6PortClose ( + IN ESL_PORT * pPort + ) +{ + UINTN DebugFlags; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the port in the socket list + // + Status = EFI_SUCCESS; + DebugFlags = pPort->DebugFlags; + pTcp6 = &pPort->Context.Tcp6; + + // + // Done with the connect event + // + if ( NULL != pTcp6->ConnectToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp6->ConnectToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed connect event\r\n", + pTcp6->ConnectToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the connect event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the close event + // + if ( NULL != pTcp6->CloseToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp6->CloseToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed close event\r\n", + pTcp6->CloseToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the close event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the listen completion event + // + if ( NULL != pTcp6->ListenToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp6->ListenToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed listen completion event\r\n", + pTcp6->ListenToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the listen completion event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Perform the network specific close operation on the port. + + This routine performs a cancel operations on the TCPv6 port to + shutdown the receive operations on the port. + + This routine is called by the ::EslSocketPortCloseTxDone + routine after the port completes all of the transmission. + + @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 +EslTcp6PortCloseOp ( + IN ESL_PORT * pPort + ) +{ + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Close the configured port + // + Status = EFI_SUCCESS; + pTcp6 = &pPort->Context.Tcp6; + pTcp6Protocol = pPort->pProtocol.TCPv6; + pTcp6->CloseToken.AbortOnClose = pPort->bCloseNow; + Status = pTcp6Protocol->Close ( pTcp6Protocol, + &pTcp6->CloseToken ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port close started\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_ERROR | pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "ERROR - Close failed on port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } + + // + // 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_STREAM and SOCK_SEQPACKET + 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 * +EslTcp6Receive ( + 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 DataLength; + struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + + DBG_ENTER ( ); + + // + // Return the remote system address if requested + // + if ( NULL != pAddress ) { + // + // Build the remote address + // + pTcp6 = &pPort->Context.Tcp6; + DEBUG (( DEBUG_RX, + "Getting packet remote address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort )); + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + CopyMem ( &pRemoteAddress->sin6_addr, + &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_addr )); + pRemoteAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.RemotePort ); + } + + // + // Determine the amount of received data + // + DataLength = pPacket->ValidBytes; + if ( BufferLength < DataLength ) { + DataLength = BufferLength; + } + + // + // Move the data into the buffer + // + DEBUG (( DEBUG_RX, + "0x%08x: Port copy packet 0x%08x data into 0x%08x, 0x%08x bytes\r\n", + pPort, + pPacket, + pBuffer, + DataLength )); + CopyMem ( pBuffer, pPacket->pBuffer, DataLength ); + + // + // Determine if the data is being read + // + if ( *pbConsumePacket ) { + // + // Account for the bytes consumed + // + pPacket->pBuffer += DataLength; + pPacket->ValidBytes -= DataLength; + DEBUG (( DEBUG_RX, + "0x%08x: Port account for 0x%08x bytes\r\n", + pPort, + DataLength )); + + // + // Determine if the entire packet was consumed + // + if (( 0 == pPacket->ValidBytes ) + || ( SOCK_STREAM != pPort->pSocket->Type )) { + // + // All done with this packet + // Account for any discarded data + // + *pSkipBytes = pPacket->ValidBytes; + } + else + { + // + // More data to consume later + // + *pbConsumePacket = FALSE; + } + } + + // + // Return the data length and the buffer address + // + *pDataLength = DataLength; + 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_STREAM or SOCK_SEQPACKET socket. + + This routine is called by ::EslSocketGetPeerAddress to detemine + the TCPv6 address and por 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 +EslTcp6RemoteAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress + ) +{ + struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + + DBG_ENTER ( ); + + // + // Return the remote address + // + pTcp6 = &pPort->Context.Tcp6; + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + pRemoteAddress->sin6_family = AF_INET6; + pRemoteAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.RemotePort ); + CopyMem ( &pRemoteAddress->sin6_addr, + &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_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 +EslTcp6RemoteAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ) +{ + CONST struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the remote address + // + pTcp6 = &pPort->Context.Tcp6; + pRemoteAddress = (struct sockaddr_in6 *)pSockAddr; + CopyMem ( &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr [ 0 ], + &pRemoteAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + sizeof ( pRemoteAddress->sin6_addr.__u6_addr.__u6_addr8 )); + pTcp6->ConfigData.AccessPoint.RemotePort = SwapBytes16 ( pRemoteAddress->sin6_port ); + Status = EFI_SUCCESS; + +// +// TODO: Fix the following check +// +/* + if ( INADDR_BROADCAST == pRemoteAddress->sin6_addr.s_addr ) { + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid remote address\r\n" )); + Status = EFI_INVALID_PARAMETER; + pPort->pSocket->errno = EAFNOSUPPORT; + } +*/ + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + 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 by the TCPv6 driver when some data is + received. + + Buffer the data that was just received. + + @param [in] Event The receive completion event + + @param [in] pIo Address of an ::ESL_IO_MGMT structure + +**/ +VOID +EslTcp6RxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + BOOLEAN bUrgent; + size_t LengthInBytes; + ESL_PACKET * pPacket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Get the operation status. + // + Status = pIo->Token.Tcp6Rx.CompletionToken.Status; + + // + // +--------------------+ +---------------------------+ + // | ESL_IO_MGMT | | ESL_PACKET | + // | | | | + // | +---------------+ +-----------------------+ | + // | | Token | | EFI_Tcp6_RECEIVE_DATA | | + // | | RxData --> | | | + // | | | +-----------------------+---+ + // | | Event | | Data Buffer | + // +----+---------------+ | | + // | | + // +---------------------------+ + // + // + // Duplicate the buffer address and length for use by the + // buffer handling code in EslTcp6Receive. These fields are + // used when a partial read is done of the data from the + // packet. + // + pPacket = pIo->pPacket; + pPacket->pBuffer = pPacket->Op.Tcp6Rx.RxData.FragmentTable[0].FragmentBuffer; + LengthInBytes = pPacket->Op.Tcp6Rx.RxData.DataLength; + pPacket->ValidBytes = LengthInBytes; + + // + // Get the data type so that it may be linked to the + // correct receive buffer list on the ESL_SOCKET structure + // + bUrgent = pPacket->Op.Tcp6Rx.RxData.UrgentFlag; + + // + // Complete this request + // + EslSocketRxComplete ( pIo, Status, LengthInBytes, bUrgent ); + DBG_EXIT ( ); +} + + +/** + Start a receive operation + + This routine posts a receive buffer to the TCPv6 driver. + See the \ref ReceiveEngine section. + + This support routine is called by EslSocketRxStart. + + @param [in] pPort Address of an ::ESL_PORT structure. + @param [in] pIo Address of an ::ESL_IO_MGMT structure. + + **/ +VOID +EslTcp6RxStart ( + IN ESL_PORT * pPort, + IN ESL_IO_MGMT * pIo + ) +{ + ESL_PACKET * pPacket; + + DBG_ENTER ( ); + + // + // Initialize the buffer for receive + // + pPacket = pIo->pPacket; + pIo->Token.Tcp6Rx.Packet.RxData = &pPacket->Op.Tcp6Rx.RxData; + pPacket->Op.Tcp6Rx.RxData.DataLength = sizeof ( pPacket->Op.Tcp6Rx.Buffer ); + pPacket->Op.Tcp6Rx.RxData.FragmentCount = 1; + pPacket->Op.Tcp6Rx.RxData.FragmentTable[0].FragmentLength = pPacket->Op.Tcp6Rx.RxData.DataLength; + pPacket->Op.Tcp6Rx.RxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Tcp6Rx.Buffer[0]; + + 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 is called by EslSocketIsConfigured to verify + that the socket has been 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 + EslTcp6SocketIsConfigured ( + IN ESL_SOCKET * pSocket + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // 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 one of two + queues, one for urgent (out-of-band) data and the other for normal + data. The urgent data is provided to TCP as soon as it is available, + allowing the TCP layer to schedule transmission of the urgent data + between packets of normal data. + + This routine is called by ::EslSocketTransmit to buffer + data for transmission. When the \ref TransmitEngine has resources, + this routine will start the transmission of the next buffer on + 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 +EslTcp6TxBuffer ( + 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 + ) +{ + BOOLEAN bUrgent; + BOOLEAN bUrgentQueue; + ESL_PACKET * pPacket; + ESL_IO_MGMT ** ppActive; + ESL_IO_MGMT ** ppFree; + ESL_PORT * pPort; + ESL_PACKET ** ppQueueHead; + ESL_PACKET ** ppQueueTail; + ESL_PACKET * pPreviousPacket; + ESL_TCP6_CONTEXT * pTcp6; + size_t * pTxBytes; + EFI_TCP6_TRANSMIT_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 ) { + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine the queue head + // + pTcp6 = &pPort->Context.Tcp6; + bUrgent = (BOOLEAN)( 0 != ( Flags & MSG_OOB )); + bUrgentQueue = bUrgent + && ( !pSocket->bOobInLine ) + && pSocket->pApi->bOobSupported; + if ( bUrgentQueue ) { + ppQueueHead = &pSocket->pTxOobPacketListHead; + ppQueueTail = &pSocket->pTxOobPacketListTail; + ppActive = &pPort->pTxOobActive; + ppFree = &pPort->pTxOobFree; + pTxBytes = &pSocket->TxOobBytes; + } + else { + ppQueueHead = &pSocket->pTxPacketListHead; + ppQueueTail = &pSocket->pTxPacketListTail; + ppActive = &pPort->pTxActive; + ppFree = &pPort->pTxFree; + pTxBytes = &pSocket->TxBytes; + } + + // + // Verify that there is enough room to buffer another + // transmit operation + // + if ( pSocket->MaxTxBuf > *pTxBytes ) { + if ( pPort->bTxFlowControl ) { + DEBUG (( DEBUG_TX, + "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\r\n0x%08x: pPort, TX flow control released, Max bytes: %d > %d bufferred bytes\r\n", + pPort, + pSocket->MaxTxBuf, + *pTxBytes )); + pPort->bTxFlowControl = FALSE; + } + + // + // Attempt to allocate the packet + // + Status = EslSocketPacketAllocate ( &pPacket, + sizeof ( pPacket->Op.Tcp6Tx ) + - sizeof ( pPacket->Op.Tcp6Tx.Buffer ) + + BufferLength, + 0, + DEBUG_TX ); + if ( !EFI_ERROR ( Status )) { + // + // Initialize the transmit operation + // + pTxData = &pPacket->Op.Tcp6Tx.TxData; + pTxData->Push = TRUE || bUrgent; + pTxData->Urgent = bUrgent; + pTxData->DataLength = (UINT32) BufferLength; + pTxData->FragmentCount = 1; + pTxData->FragmentTable[0].FragmentLength = (UINT32) BufferLength; + pTxData->FragmentTable[0].FragmentBuffer = &pPacket->Op.Tcp6Tx.Buffer[0]; + + // + // Copy the data into the buffer + // + CopyMem ( &pPacket->Op.Tcp6Tx.Buffer[0], + pBuffer, + BufferLength ); + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Stop transmission after an error + // + if ( !EFI_ERROR ( pSocket->TxError )) { + // + // Display the request + // + DEBUG (( DEBUG_TX, + "Send %d %s bytes from 0x%08x\r\n", + BufferLength, + bUrgent ? L"urgent" : L"normal", + pBuffer )); + + // + // Queue the data for transmission + // + pPacket->pNext = NULL; + pPreviousPacket = *ppQueueTail; + if ( NULL == pPreviousPacket ) { + *ppQueueHead = pPacket; + } + else { + pPreviousPacket->pNext = pPacket; + } + *ppQueueTail = pPacket; + DEBUG (( DEBUG_TX, + "0x%08x: Packet on %s transmit list\r\n", + pPacket, + bUrgentQueue ? L"urgent" : L"normal" )); + + // + // Account for the buffered data + // + *pTxBytes += BufferLength; + *pDataLength = BufferLength; + + // + // Start the transmit engine if it is idle + // + if ( NULL != *ppFree ) { + EslSocketTxStart ( pPort, + ppQueueHead, + ppQueueTail, + ppActive, + ppFree ); + } + } + else { + // + // Previous transmit error + // Stop transmission + // + Status = pSocket->TxError; + pSocket->errno = EIO; + + // + // Free the packet + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + // + // Packet allocation failed + // + pSocket->errno = ENOMEM; + } + } + else { + if ( !pPort->bTxFlowControl ) { + DEBUG (( DEBUG_TX, + "0x%08x: pPort, TX flow control applied, Max bytes %d <= %d bufferred bytes\r\nTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\r\n", + pPort, + pSocket->MaxTxBuf, + *pTxBytes )); + pPort->bTxFlowControl = TRUE; + } + // + // Not enough buffer space available + // + pSocket->errno = EAGAIN; + Status = EFI_NOT_READY; + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the normal data transmit completion + + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for normal data. + + This routine is called by the TCPv6 network layer when a + normal data transmit request completes. + + @param [in] Event The normal transmit completion event + + @param [in] pIo The ESL_IO_MGMT structure address + +**/ +VOID +EslTcp6TxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + UINT32 LengthInBytes; + ESL_PACKET * pPacket; + ESL_PORT * pPort; + 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.Tcp6Tx.TxData.DataLength; + pSocket->TxBytes -= LengthInBytes; + Status = pIo->Token.Tcp6Tx.CompletionToken.Status; + + // + // Complete the transmit operation + // + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "Normal ", + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); + DBG_EXIT ( ); +} + + +/** + Process the urgent data transmit completion + + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for urgent data. + + This routine is called by the TCPv6 network layer when a + urgent data transmit request completes. + + @param [in] Event The urgent transmit completion event + + @param [in] pIo The ESL_IO_MGMT structure address + +**/ +VOID +EslTcp6TxOobComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + UINT32 LengthInBytes; + ESL_PACKET * pPacket; + ESL_PORT * pPort; + 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.Tcp6Tx.TxData.DataLength; + pSocket->TxOobBytes -= LengthInBytes; + Status = pIo->Token.Tcp6Tx.CompletionToken.Status; + + // + // Complete the transmit operation + // + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "Urgent ", + &pSocket->pTxOobPacketListHead, + &pSocket->pTxOobPacketListTail, + &pPort->pTxOobActive, + &pPort->pTxOobFree ); + DBG_EXIT ( ); +} + + +/** + Interface between the socket layer and the network specific + code that supports SOCK_STREAM and SOCK_SEQPACKET sockets + over TCPv6. +**/ +CONST ESL_PROTOCOL_API cEslTcp6Api = { + "TCPv6", + IPPROTO_TCP, + OFFSET_OF ( ESL_PORT, Context.Tcp6.ConfigData ), + OFFSET_OF ( ESL_LAYER, pTcp6List ), + sizeof ( struct sockaddr_in6 ), + sizeof ( struct sockaddr_in6 ), + AF_INET6, + sizeof (((ESL_PACKET *)0 )->Op.Tcp6Rx ), + OFFSET_OF ( ESL_PACKET, Op.Tcp6Rx.Buffer ) - OFFSET_OF ( ESL_PACKET, Op ), + OFFSET_OF ( ESL_IO_MGMT, Token.Tcp6Rx.Packet.RxData ), + TRUE, + EADDRINUSE, + EslTcp6Accept, + EslTcp6ConnectPoll, + EslTcp6ConnectStart, + EslTcp6SocketIsConfigured, + EslTcp6LocalAddressGet, + EslTcp6LocalAddressSet, + EslTcp6Listen, + NULL, // OptionGet + NULL, // OptionSet + EslTcp6PacketFree, + EslTcp6PortAllocate, + EslTcp6PortClose, + EslTcp6PortCloseOp, + FALSE, + EslTcp6Receive, + EslTcp6RemoteAddressGet, + EslTcp6RemoteAddressSet, + EslTcp6RxComplete, + EslTcp6RxStart, + EslTcp6TxBuffer, + EslTcp6TxComplete, + EslTcp6TxOobComplete +}; diff --git a/StdLib/EfiSocketLib/Udp4.c b/StdLib/EfiSocketLib/Udp4.c index e1500d3f9f..6625c078d0 100644 --- a/StdLib/EfiSocketLib/Udp4.c +++ b/StdLib/EfiSocketLib/Udp4.c @@ -253,6 +253,7 @@ EslUdp4PortAllocate ( // 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; diff --git a/StdLib/EfiSocketLib/Udp6.c b/StdLib/EfiSocketLib/Udp6.c new file mode 100644 index 0000000000..187f0ad641 --- /dev/null +++ b/StdLib/EfiSocketLib/Udp6.c @@ -0,0 +1,1094 @@ +/** @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 IPv6 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 +EslUdp6LocalAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pSockAddr + ) +{ + struct sockaddr_in6 * pLocalAddress; + ESL_UDP6_CONTEXT * pUdp6; + + DBG_ENTER ( ); + + // + // Return the local address + // + pUdp6 = &pPort->Context.Udp6; + pLocalAddress = (struct sockaddr_in6 *)pSockAddr; + pLocalAddress->sin6_family = AF_INET6; + pLocalAddress->sin6_port = SwapBytes16 ( pUdp6->ConfigData.StationPort ); + CopyMem ( &pLocalAddress->sin6_addr, + &pUdp6->ConfigData.StationAddress.Addr[0], + sizeof ( pLocalAddress->sin6_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 IPv6 address + of INADDR_ANY specifies that the connection is made to + all of the network stacks on the platform. Specifying a + specific IPv6 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 +EslUdp6LocalAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest + ) +{ + EFI_UDP6_CONFIG_DATA * pConfig; + CONST struct sockaddr_in6 * pIpAddress; + CONST UINT8 * pIPv6Address; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the local address + // + pIpAddress = (struct sockaddr_in6 *)pSockAddr; + pIPv6Address = (UINT8 *)&pIpAddress->sin6_addr; + pConfig = &pPort->Context.Udp6.ConfigData; + CopyMem ( &pConfig->StationAddress, + pIPv6Address, + sizeof ( pConfig->StationAddress )); + + // + // 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->sin6_port ); + + // + // Display the local address + // + DEBUG (( DEBUG_BIND, + "0x%08x: Port, Local UDP6 Address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pConfig->StationAddress.Addr[0], + pConfig->StationAddress.Addr[1], + pConfig->StationAddress.Addr[2], + pConfig->StationAddress.Addr[3], + pConfig->StationAddress.Addr[4], + pConfig->StationAddress.Addr[5], + pConfig->StationAddress.Addr[6], + pConfig->StationAddress.Addr[7], + pConfig->StationAddress.Addr[8], + pConfig->StationAddress.Addr[9], + pConfig->StationAddress.Addr[10], + pConfig->StationAddress.Addr[11], + pConfig->StationAddress.Addr[12], + pConfig->StationAddress.Addr[13], + pConfig->StationAddress.Addr[14], + pConfig->StationAddress.Addr[15], + 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 +EslUdp6PacketFree ( + IN ESL_PACKET * pPacket, + IN OUT size_t * pRxBytes + ) +{ + EFI_UDP6_RECEIVE_DATA * pRxData; + + DBG_ENTER ( ); + + // + // Account for the receive bytes + // + pRxData = pPacket->Op.Udp6Rx.pRxData; + *pRxBytes -= pRxData->DataLength; + + // + // Disconnect the buffer from the packet + // + pPacket->Op.Udp6Rx.pRxData = NULL; + + // + // Return the buffer to the UDP6 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 +EslUdp6PortAllocate ( + IN ESL_PORT * pPort, + IN UINTN DebugFlags + ) +{ + EFI_UDP6_CONFIG_DATA * pConfig; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Initialize the port + // + pSocket = pPort->pSocket; + pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Udp6Tx.TxData ); + pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Udp6Tx.Event ); + pSocket->TxTokenOffset = OFFSET_OF ( EFI_UDP6_COMPLETION_TOKEN, Packet.TxData ); + + // + // Save the cancel, receive and transmit addresses + // + pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.UDPv6->Configure; + pPort->pfnRxCancel = (PFN_NET_IO_START)pPort->pProtocol.UDPv6->Cancel; + pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.UDPv6->Poll; + pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv6->Receive; + pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv6->Transmit; + + // + // Do not drop packets + // + pConfig = &pPort->Context.Udp6.ConfigData; + pConfig->ReceiveTimeout = 0; + pConfig->ReceiveTimeout = pConfig->ReceiveTimeout; + + // + // Set the configuration flags + // + pConfig->AllowDuplicatePort = TRUE; + pConfig->AcceptAnyPort = FALSE; + pConfig->AcceptPromiscuous = FALSE; + pConfig->HopLimit = 255; + pConfig->TrafficClass = 0; + + 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 * +EslUdp6Receive ( + 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_in6 * pRemoteAddress; + EFI_UDP6_RECEIVE_DATA * pRxData; + + DBG_ENTER ( ); + + pRxData = pPacket->Op.Udp6Rx.pRxData; + // + // Return the remote system address if requested + // + if ( NULL != pAddress ) { + // + // Build the remote address + // + DEBUG (( DEBUG_RX, + "Getting packet remote address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%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.SourceAddress.Addr[4], + pRxData->UdpSession.SourceAddress.Addr[5], + pRxData->UdpSession.SourceAddress.Addr[6], + pRxData->UdpSession.SourceAddress.Addr[7], + pRxData->UdpSession.SourceAddress.Addr[8], + pRxData->UdpSession.SourceAddress.Addr[9], + pRxData->UdpSession.SourceAddress.Addr[10], + pRxData->UdpSession.SourceAddress.Addr[11], + pRxData->UdpSession.SourceAddress.Addr[12], + pRxData->UdpSession.SourceAddress.Addr[13], + pRxData->UdpSession.SourceAddress.Addr[14], + pRxData->UdpSession.SourceAddress.Addr[15], + pRxData->UdpSession.SourcePort )); + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + CopyMem ( &pRemoteAddress->sin6_addr, + &pRxData->UdpSession.SourceAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_addr )); + pRemoteAddress->sin6_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 +EslUdp6RemoteAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress + ) +{ + struct sockaddr_in6 * pRemoteAddress; + ESL_UDP6_CONTEXT * pUdp6; + + DBG_ENTER ( ); + + // + // Return the remote address + // + pUdp6 = &pPort->Context.Udp6; + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + pRemoteAddress->sin6_family = AF_INET6; + pRemoteAddress->sin6_port = SwapBytes16 ( pUdp6->ConfigData.RemotePort ); + CopyMem ( &pRemoteAddress->sin6_addr, + &pUdp6->ConfigData.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_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 +EslUdp6RemoteAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ) +{ + CONST struct sockaddr_in6 * pRemoteAddress; + ESL_UDP6_CONTEXT * pUdp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the remote address + // + pUdp6 = &pPort->Context.Udp6; + pRemoteAddress = (struct sockaddr_in6 *)pSockAddr; + CopyMem ( &pUdp6->ConfigData.RemoteAddress, + &pRemoteAddress->sin6_addr, + sizeof ( pUdp6->ConfigData.RemoteAddress )); + pUdp6->ConfigData.RemotePort = SwapBytes16 ( pRemoteAddress->sin6_port ); + 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 UDP6 driver's buffer will + be returned by either ::EslUdp6Receive 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 +EslUdp6RxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + size_t LengthInBytes; + ESL_PACKET * pPacket; + EFI_UDP6_RECEIVE_DATA * pRxData; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Get the operation status. + // + Status = pIo->Token.Udp6Rx.Status; + + // + // Get the packet length + // + pRxData = pIo->Token.Udp6Rx.Packet.RxData; + LengthInBytes = pRxData->DataLength; + + // + // +--------------------+ +-----------------------+ + // | ESL_IO_MGMT | | Data Buffer | + // | | | (Driver owned) | + // | +---------------+ +-----------------------+ + // | | Token | ^ + // | | Rx Event | | + // | | | +-----------------------+ + // | | RxData --> | EFI_UDP6_RECEIVE_DATA | + // +----+---------------+ | (Driver owned) | + // +-----------------------+ + // +--------------------+ ^ + // | ESL_PACKET | . + // | | . + // | +---------------+ . + // | | pRxData --> NULL ....... + // +----+---------------+ + // + // + // Save the data in the packet + // + pPacket = pIo->pPacket; + pPacket->Op.Udp6Rx.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 + EslUdp6SocketIsConfigured ( + IN ESL_SOCKET * pSocket + ) +{ + EFI_UDP6_CONFIG_DATA * pConfigData; + ESL_PORT * pPort; + ESL_PORT * pNextPort; + ESL_UDP6_CONTEXT * pUdp6; + EFI_UDP6_PROTOCOL * pUdp6Protocol; + EFI_STATUS Status; + struct sockaddr_in6 LocalAddress; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Configure the port if necessary + // + if ( !pSocket->bConfigured ) { + // + // Fill in the port list if necessary + // + if ( NULL == pSocket->pPortList ) { + ZeroMem ( &LocalAddress, sizeof ( LocalAddress )); + LocalAddress.sin6_len = sizeof ( LocalAddress ); + LocalAddress.sin6_family = AF_INET6; + Status = EslSocketBind ( &pSocket->SocketProtocol, + (struct sockaddr *)&LocalAddress, + LocalAddress.sin6_len, + &pSocket->errno ); + } + + // + // Walk the port list + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Attempt to configure the port + // + pNextPort = pPort->pLinkSocket; + pUdp6 = &pPort->Context.Udp6; + pUdp6Protocol = pPort->pProtocol.UDPv6; + pConfigData = &pUdp6->ConfigData; + DEBUG (( DEBUG_TX, + "0x%08x: pPort Configuring for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d --> [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pConfigData->StationAddress.Addr[0], + pConfigData->StationAddress.Addr[1], + pConfigData->StationAddress.Addr[2], + pConfigData->StationAddress.Addr[3], + pConfigData->StationAddress.Addr[4], + pConfigData->StationAddress.Addr[5], + pConfigData->StationAddress.Addr[6], + pConfigData->StationAddress.Addr[7], + pConfigData->StationAddress.Addr[8], + pConfigData->StationAddress.Addr[9], + pConfigData->StationAddress.Addr[10], + pConfigData->StationAddress.Addr[11], + pConfigData->StationAddress.Addr[12], + pConfigData->StationAddress.Addr[13], + pConfigData->StationAddress.Addr[14], + pConfigData->StationAddress.Addr[15], + pConfigData->StationPort, + pConfigData->RemoteAddress.Addr[0], + pConfigData->RemoteAddress.Addr[1], + pConfigData->RemoteAddress.Addr[2], + pConfigData->RemoteAddress.Addr[3], + pConfigData->RemoteAddress.Addr[4], + pConfigData->RemoteAddress.Addr[5], + pConfigData->RemoteAddress.Addr[6], + pConfigData->RemoteAddress.Addr[7], + pConfigData->RemoteAddress.Addr[8], + pConfigData->RemoteAddress.Addr[9], + pConfigData->RemoteAddress.Addr[10], + pConfigData->RemoteAddress.Addr[11], + pConfigData->RemoteAddress.Addr[12], + pConfigData->RemoteAddress.Addr[13], + pConfigData->RemoteAddress.Addr[14], + pConfigData->RemoteAddress.Addr[15], + pConfigData->RemotePort )); + Status = pUdp6Protocol->Configure ( pUdp6Protocol, + pConfigData ); + if ( !EFI_ERROR ( Status )) { + // + // Update the configuration data + // + Status = pUdp6Protocol->GetModeData ( pUdp6Protocol, + pConfigData, + NULL, + NULL, + NULL ); + } + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed to configure the Udp6 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 [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d --> [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pConfigData->StationAddress.Addr[0], + pConfigData->StationAddress.Addr[1], + pConfigData->StationAddress.Addr[2], + pConfigData->StationAddress.Addr[3], + pConfigData->StationAddress.Addr[4], + pConfigData->StationAddress.Addr[5], + pConfigData->StationAddress.Addr[6], + pConfigData->StationAddress.Addr[7], + pConfigData->StationAddress.Addr[8], + pConfigData->StationAddress.Addr[9], + pConfigData->StationAddress.Addr[10], + pConfigData->StationAddress.Addr[11], + pConfigData->StationAddress.Addr[12], + pConfigData->StationAddress.Addr[13], + pConfigData->StationAddress.Addr[14], + pConfigData->StationAddress.Addr[15], + pConfigData->StationPort, + pConfigData->RemoteAddress.Addr[0], + pConfigData->RemoteAddress.Addr[1], + pConfigData->RemoteAddress.Addr[2], + pConfigData->RemoteAddress.Addr[3], + pConfigData->RemoteAddress.Addr[4], + pConfigData->RemoteAddress.Addr[5], + pConfigData->RemoteAddress.Addr[6], + pConfigData->RemoteAddress.Addr[7], + pConfigData->RemoteAddress.Addr[8], + pConfigData->RemoteAddress.Addr[9], + pConfigData->RemoteAddress.Addr[10], + pConfigData->RemoteAddress.Addr[11], + pConfigData->RemoteAddress.Addr[12], + pConfigData->RemoteAddress.Addr[13], + pConfigData->RemoteAddress.Addr[14], + pConfigData->RemoteAddress.Addr[15], + pConfigData->RemotePort )); + pPort->bConfigured = TRUE; + + // + // Start the first read on the port + // + EslSocketRxStart ( pPort ); + + // + // The socket is connected + // + pSocket->State = SOCKET_STATE_CONNECTED; + } + + // + // Set the next port + // + pPort = pNextPort; + } + + // + // Determine the configuration status + // + if ( NULL != pSocket->pPortList ) { + pSocket->bConfigured = TRUE; + } + } + + // + // Determine the socket configuration status + // + if ( !EFI_ERROR ( 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 +EslUdp6TxBuffer ( + 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_in6 * pRemoteAddress; + ESL_UDP6_CONTEXT * pUdp6; + size_t * pTxBytes; + ESL_UDP6_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 ) { + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine the queue head + // + pUdp6 = &pPort->Context.Udp6; + pTxBytes = &pSocket->TxBytes; + + // + // Verify that there is enough room to buffer another + // transmit operation + // + if ( pSocket->MaxTxBuf > *pTxBytes ) { + // + // Attempt to allocate the packet + // + Status = EslSocketPacketAllocate ( &pPacket, + sizeof ( pPacket->Op.Udp6Tx ) + - sizeof ( pPacket->Op.Udp6Tx.Buffer ) + + BufferLength, + 0, + DEBUG_TX ); + if ( !EFI_ERROR ( Status )) { + // + // Initialize the transmit operation + // + pTxData = &pPacket->Op.Udp6Tx; + 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.Udp6Tx.Buffer[0]; + + // + // Set the remote system address if necessary + // + pTxData->TxData.UdpSessionData = NULL; + if ( NULL != pAddress ) { + pRemoteAddress = (const struct sockaddr_in6 *)pAddress; + CopyMem ( &pTxData->Session.SourceAddress, + &pUdp6->ConfigData.StationAddress, + sizeof ( pTxData->Session.SourceAddress )); + pTxData->Session.SourcePort = 0; + CopyMem ( &pTxData->Session.DestinationAddress, + &pRemoteAddress->sin6_addr, + sizeof ( pTxData->Session.DestinationAddress )); + pTxData->Session.DestinationPort = SwapBytes16 ( pRemoteAddress->sin6_port ); + + // + // Use the remote system address when sending this packet + // + pTxData->TxData.UdpSessionData = &pTxData->Session; + } + + // + // Copy the data into the buffer + // + CopyMem ( &pPacket->Op.Udp6Tx.Buffer[0], + pBuffer, + BufferLength ); + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Stop transmission after an error + // + if ( !EFI_ERROR ( pSocket->TxError )) { + // + // Display the request + // + DEBUG (( DEBUG_TX, + "Send %d %s bytes from 0x%08x\r\n", + BufferLength, + pBuffer )); + + // + // 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 ) { + EslSocketTxStart ( pPort, + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); + } + } + else { + // + // Previous transmit error + // Stop transmission + // + Status = pSocket->TxError; + pSocket->errno = EIO; + + // + // Free the packet + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + // + // Packet allocation failed + // + pSocket->errno = ENOMEM; + } + } + 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 +EslUdp6TxComplete ( + 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.Udp6Tx.TxData.DataLength; + pSocket->TxBytes -= LengthInBytes; + Status = pIo->Token.Udp6Tx.Status; + + // + // Complete the transmit operation + // + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "UDP ", + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); + DBG_EXIT ( ); +} + + +/** + Interface between the socket layer and the network specific + code that supports SOCK_DGRAM sockets over UDPv4. +**/ +CONST ESL_PROTOCOL_API cEslUdp6Api = { + "UDPv6", + IPPROTO_UDP, + OFFSET_OF ( ESL_PORT, Context.Udp6.ConfigData ), + OFFSET_OF ( ESL_LAYER, pUdp6List ), + sizeof ( struct sockaddr_in6 ), + sizeof ( struct sockaddr_in6 ), + AF_INET6, + sizeof (((ESL_PACKET *)0 )->Op.Udp6Rx ), + sizeof (((ESL_PACKET *)0 )->Op.Udp6Rx ), + OFFSET_OF ( ESL_IO_MGMT, Token.Udp6Rx.Packet.RxData ), + FALSE, + EADDRINUSE, + NULL, // Accept + NULL, // ConnectPoll + NULL, // ConnectStart + EslUdp6SocketIsConfigured, + EslUdp6LocalAddressGet, + EslUdp6LocalAddressSet, + NULL, // Listen + NULL, // OptionGet + NULL, // OptionSet + EslUdp6PacketFree, + EslUdp6PortAllocate, + NULL, // PortClose, + NULL, // PortCloseOp + TRUE, + EslUdp6Receive, + EslUdp6RemoteAddressGet, + EslUdp6RemoteAddressSet, + EslUdp6RxComplete, + NULL, // RxStart + EslUdp6TxBuffer, + EslUdp6TxComplete, + NULL // TxOobComplete +}; diff --git a/StdLib/EfiSocketLib/UseEfiSocketLib.c b/StdLib/EfiSocketLib/UseEfiSocketLib.c index 1c122af650..ed72e8e763 100644 --- a/StdLib/EfiSocketLib/UseEfiSocketLib.c +++ b/StdLib/EfiSocketLib/UseEfiSocketLib.c @@ -28,6 +28,14 @@ CONST EFI_GUID mEslIp4ServiceGuid = { }; +/** + Tag GUID - IPv6 in use by an application using EfiSocketLib +**/ +CONST EFI_GUID mEslIp6ServiceGuid = { + 0xc51b2761, 0xc476, 0x45fe, { 0xbe, 0x61, 0xba, 0x4b, 0xcc, 0x32, 0xf2, 0x34 } +}; + + /** Tag GUID - TCPv4 in use by an application using EfiSocketLib **/ @@ -36,6 +44,14 @@ CONST EFI_GUID mEslTcp4ServiceGuid = { }; +/** + Tag GUID - TCPv6 in use by an application using EfiSocketLib +**/ +CONST EFI_GUID mEslTcp6ServiceGuid = { + 0x279858a4, 0x4e9e, 0x4e53, { 0x93, 0x22, 0xf2, 0x54, 0xe0, 0x7e, 0xef, 0xd4 } +}; + + /** Tag GUID - UDPv4 in use by an application using EfiSocketLib **/ @@ -44,6 +60,14 @@ CONST EFI_GUID mEslUdp4ServiceGuid = { }; +/** + Tag GUID - UDPv6 in use by an application using EfiSocketLib +**/ +CONST EFI_GUID mEslUdp6ServiceGuid = { + 0xaa4af677, 0x6efe, 0x477c, { 0x96, 0x68, 0xe8, 0x13, 0x9d, 0x2, 0xfd, 0x9b } +}; + + /** Connect to the EFI socket library @@ -252,10 +276,8 @@ EslServiceNetworkDisconnect ( NULL, &HandleCount, &pHandles ); - if ( EFI_ERROR ( Status )) { - break; - } - if ( NULL != pHandles ) { + if (( !EFI_ERROR ( Status )) + && ( NULL != pHandles )) { // // Attempt to disconnect from this network adapter // @@ -277,6 +299,7 @@ EslServiceNetworkDisconnect ( // Set the next network protocol // pSocketBinding += 1; + Status = EFI_SUCCESS; } // -- cgit v1.2.3