mirror of https://github.com/acidanthera/audk.git
2336 lines
69 KiB
C
2336 lines
69 KiB
C
/** @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 );
|
|
pPort->pSocket->bAddressSet = TRUE;
|
|
|
|
//
|
|
// 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 );
|
|
|
|
//
|
|
// Set the next buffer address
|
|
//
|
|
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
|
|
};
|