summaryrefslogtreecommitdiff
path: root/StdLib/EfiSocketLib/Tcp6.c
diff options
context:
space:
mode:
Diffstat (limited to 'StdLib/EfiSocketLib/Tcp6.c')
-rw-r--r--StdLib/EfiSocketLib/Tcp6.c2329
1 files changed, 2329 insertions, 0 deletions
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
+};