summaryrefslogtreecommitdiff
path: root/StdLib/EfiSocketLib/Tcp4.c
diff options
context:
space:
mode:
authordarylm503 <darylm503@6f19259b-4bc3-4df7-8a09-765794883524>2011-07-30 00:30:44 +0000
committerdarylm503 <darylm503@6f19259b-4bc3-4df7-8a09-765794883524>2011-07-30 00:30:44 +0000
commitd7ce700605e1af0e455e31ec11f19ff21d26b525 (patch)
tree243b582ac3350e8c6ce6ca96fff13805318fd65c /StdLib/EfiSocketLib/Tcp4.c
parentf766dd76fde231ecd4f2e9faf99293e90902cebb (diff)
downloadedk2-platforms-d7ce700605e1af0e455e31ec11f19ff21d26b525.tar.xz
Add Socket Libraries.
Add Posix functions for porting compatibility. Fix compliance issues with ISO/IEC 9899:199409 New Functions: setenv(), fparseln(), GetFileNameFromPath(), rename(), realpath(), setprogname(), getprogname(), strlcat(), strlcpy(), strsep(), setitimer(), getitimer(), timegm(), getopt(), basename(), mkstemp(), ffs(), vsnprintf(), snprintf(), getpass(), usleep(), select(), writev(), strcasecmp(), getcwd(), chdir(), tcgetpgrp(), getpgrp(), gettimeofday(), bcopy(), git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12061 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'StdLib/EfiSocketLib/Tcp4.c')
-rw-r--r--StdLib/EfiSocketLib/Tcp4.c3415
1 files changed, 3415 insertions, 0 deletions
diff --git a/StdLib/EfiSocketLib/Tcp4.c b/StdLib/EfiSocketLib/Tcp4.c
new file mode 100644
index 0000000000..2840dd7e0e
--- /dev/null
+++ b/StdLib/EfiSocketLib/Tcp4.c
@@ -0,0 +1,3415 @@
+/** @file
+ Implement the TCP4 driver support for the socket layer.
+
+ Copyright (c) 2011, Intel Corporation
+ All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Socket.h"
+
+
+/**
+ Accept a network connection.
+
+ The SocketAccept routine waits for a network connection to the socket.
+ It is able to return the remote network address to the caller if
+ requested.
+
+ @param [in] pSocket Address of the 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
+EslTcpAccept4 (
+ IN DT_SOCKET * pSocket,
+ IN struct sockaddr * pSockAddr,
+ IN OUT socklen_t * pSockAddrLength
+ )
+{
+ DT_PORT * pPort;
+ struct sockaddr_in * pRemoteAddress;
+ DT_TCP4_CONTEXT * pTcp4;
+ UINT32 RemoteAddress;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Validate the socket length
+ //
+ pRemoteAddress = (struct sockaddr_in *) 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;
+ pTcp4 = &pPort->Context.Tcp4;
+
+ //
+ // Fill-in the remote address structure
+ //
+ ZeroMem ( pRemoteAddress, sizeof ( *pRemoteAddress ));
+ pRemoteAddress->sin_len = sizeof ( *pRemoteAddress );
+ pRemoteAddress->sin_family = AF_INET;
+ pRemoteAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.RemotePort );
+ RemoteAddress = pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3];
+ RemoteAddress <<= 8;
+ RemoteAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2];
+ RemoteAddress <<= 8;
+ RemoteAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1];
+ RemoteAddress <<= 8;
+ RemoteAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0];
+ pRemoteAddress->sin_addr.s_addr = RemoteAddress;
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Bind a name to a socket.
+
+ The ::TcpBind4 routine connects a name to a TCP4 stack on the local machine.
+
+ @param [in] pSocket Address of the socket structure.
+
+ @param [in] pSockAddr Address of a sockaddr structure that contains the
+ connection point on the local machine. An IPv4 address
+ of INADDR_ANY specifies that the connection is made to
+ all of the network stacks on the platform. Specifying a
+ specific IPv4 address restricts the connection to the
+ network stack supporting that address. Specifying zero
+ for the port causes the network layer to assign a port
+ number from the dynamic range. Specifying a specific
+ port number causes the network layer to use that port.
+
+ @param [in] SockAddrLen Specifies the length in bytes of the sockaddr structure.
+
+ @retval EFI_SUCCESS - Socket successfully created
+
+ **/
+EFI_STATUS
+EslTcpBind4 (
+ IN DT_SOCKET * pSocket,
+ IN const struct sockaddr * pSockAddr,
+ IN socklen_t SockAddrLength
+ )
+{
+ EFI_HANDLE ChildHandle;
+ DT_LAYER * pLayer;
+ DT_PORT * pPort;
+ DT_SERVICE * pService;
+ CONST struct sockaddr_in * pIp4Address;
+ EFI_SERVICE_BINDING_PROTOCOL * pTcp4Service;
+ EFI_STATUS Status;
+ EFI_STATUS TempStatus;
+
+ DBG_ENTER ( );
+
+ //
+ // Verify the socket layer synchronization
+ //
+ VERIFY_TPL ( TPL_SOCKETS );
+
+ //
+ // Assume success
+ //
+ pSocket->errno = 0;
+ Status = EFI_SUCCESS;
+
+ //
+ // Validate the address length
+ //
+ pIp4Address = (CONST struct sockaddr_in *) pSockAddr;
+ if ( SockAddrLength >= ( sizeof ( *pIp4Address )
+ - sizeof ( pIp4Address->sin_zero ))) {
+
+ //
+ // Walk the list of services
+ //
+ pLayer = &mEslLayer;
+ pService = pLayer->pTcp4List;
+ while ( NULL != pService ) {
+ //
+ // Create the TCP port
+ //
+ pTcp4Service = pService->pInterface;
+ ChildHandle = NULL;
+ Status = pTcp4Service->CreateChild ( pTcp4Service,
+ &ChildHandle );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_BIND | DEBUG_POOL,
+ "0x%08x: Tcp4 port handle created\r\n",
+ ChildHandle ));
+
+ //
+ // Open the port
+ //
+ Status = EslTcpPortAllocate4 ( pSocket,
+ pService,
+ ChildHandle,
+ (UINT8 *) &pIp4Address->sin_addr.s_addr,
+ SwapBytes16 ( pIp4Address->sin_port ),
+ DEBUG_BIND,
+ &pPort );
+ }
+ else {
+ DEBUG (( DEBUG_BIND | DEBUG_POOL,
+ "ERROR - Failed to open Tcp4 port handle, Status: %r\r\n",
+ Status ));
+ ChildHandle = NULL;
+ }
+
+ //
+ // Close the port if necessary
+ //
+ if (( EFI_ERROR ( Status )) && ( NULL != ChildHandle )) {
+ TempStatus = pTcp4Service->DestroyChild ( pTcp4Service,
+ ChildHandle );
+ if ( !EFI_ERROR ( TempStatus )) {
+ DEBUG (( DEBUG_BIND | DEBUG_POOL,
+ "0x%08x: Tcp4 port handle destroyed\r\n",
+ ChildHandle ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_BIND | DEBUG_POOL,
+ "ERROR - Failed to destroy the Tcp4 port handle 0x%08x, Status: %r\r\n",
+ ChildHandle,
+ TempStatus ));
+ ASSERT ( EFI_SUCCESS == TempStatus );
+ }
+ }
+
+ //
+ // Set the next service
+ //
+ pService = pService->pNext;
+ }
+
+ //
+ // Verify that at least one network connection was found
+ //
+ if ( NULL == pSocket->pPortList ) {
+ DEBUG (( DEBUG_BIND | DEBUG_POOL | DEBUG_INIT,
+ "Socket address %d.%d.%d.%d (0x%08x) is not available!\r\n",
+ ( pIp4Address->sin_addr.s_addr >> 24 ) & 0xff,
+ ( pIp4Address->sin_addr.s_addr >> 16 ) & 0xff,
+ ( pIp4Address->sin_addr.s_addr >> 8 ) & 0xff,
+ pIp4Address->sin_addr.s_addr & 0xff,
+ pIp4Address->sin_addr.s_addr ));
+ pSocket->errno = EADDRNOTAVAIL;
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+ else {
+ DEBUG (( DEBUG_BIND,
+ "ERROR - Invalid TCP4 address length: %d\r\n",
+ SockAddrLength ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EINVAL;
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Attempt to connect to a remote TCP port
+
+ @param [in] pSocket Address of the 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
+EslTcpConnectAttempt4 (
+ IN DT_SOCKET * pSocket
+ )
+{
+ DT_PORT * pPort;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_TCP4_PROTOCOL * pTcp4Protocol;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Determine if any more local adapters are available
+ //
+ pPort = pSocket->pPortList;
+ if ( NULL != pPort ) {
+ //
+ // Configure the port
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ pTcp4->ConfigData.AccessPoint.ActiveFlag = TRUE;
+ pTcp4->ConfigData.TimeToLive = 255;
+ pTcp4Protocol = pTcp4->pProtocol;
+ Status = pTcp4Protocol->Configure ( pTcp4Protocol,
+ &pTcp4->ConfigData );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_CONNECT,
+ "ERROR - Failed to configure the Tcp4 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 ));
+ pTcp4->bConfigured = TRUE;
+
+ //
+ // Attempt the connection to the remote system
+ //
+ Status = pTcp4Protocol->Connect ( pTcp4Protocol,
+ &pTcp4->ConnectToken );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Connection in progress
+ //
+ pSocket->errno = EINPROGRESS;
+ Status = EFI_NOT_READY;
+ DEBUG (( DEBUG_CONNECT,
+ "0x%08x: Port attempting connection to %d.%d.%d.%d:%d\r\n",
+ pPort,
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0],
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1],
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2],
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3],
+ pTcp4->ConfigData.AccessPoint.RemotePort ));
+ }
+ else {
+ //
+ // Connection error
+ //
+ pSocket->errno = EINVAL;
+ DEBUG (( DEBUG_CONNECT,
+ "ERROR - Port 0x%08x not connected, Status: %r\r\n",
+ pPort,
+ Status ));
+ }
+ }
+ }
+ else {
+ //
+ // No more local adapters available
+ //
+ pSocket->errno = ENETUNREACH;
+ Status = EFI_NO_RESPONSE;
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Process the remote connection attempt
+
+ A connection attempt to a remote system has just completed when
+ this routine is invoked. Release the port in the case of an
+ error and start a connection attempt on the next port. If the
+ connection attempt was successful, then release all of the other
+ ports.
+
+ @param Event The connect completion event
+
+ @param pPort The DT_PORT structure address
+
+**/
+VOID
+EslTcpConnectComplete4 (
+ IN EFI_EVENT Event,
+ IN DT_PORT * pPort
+ )
+{
+ BOOLEAN bRemoveFirstPort;
+ BOOLEAN bRemovePorts;
+ DT_PORT * pNextPort;
+ DT_SOCKET * pSocket;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Locate the TCP context
+ //
+ pSocket = pPort->pSocket;
+ pTcp4 = &pPort->Context.Tcp4;
+
+ //
+ // Get the connection status
+ //
+ bRemoveFirstPort = FALSE;
+ bRemovePorts = FALSE;
+ Status = pTcp4->ConnectToken.CompletionToken.Status;
+ pSocket->ConnectStatus = Status;
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // The connection was successful
+ //
+ DEBUG (( DEBUG_CONNECT,
+ "0x%08x: Port connected to %d.%d.%d.%d:%d\r\n",
+ pPort,
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [0],
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [1],
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [2],
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [3],
+ pTcp4->ConfigData.AccessPoint.RemotePort ));
+
+ //
+ // Remove the rest of the ports
+ //
+ bRemovePorts = TRUE;
+ }
+ else {
+ //
+ // The connection failed
+ //
+ DEBUG (( DEBUG_CONNECT,
+ "0x%08x: Port connection to %d.%d.%d.%d:%d failed, Status: %r\r\n",
+ pPort,
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [0],
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [1],
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [2],
+ pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr [3],
+ pTcp4->ConfigData.AccessPoint.RemotePort,
+ Status ));
+
+ //
+ // Close the current port
+ //
+ Status = EslTcpPortClose4 ( 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 = EslTcpConnectAttempt4 ( 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;
+ EslTcpPortClose4 ( 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.
+
+ The ::TcpConnectPoll4 routine determines when the connection
+ attempt transitions from being in process to being complete.
+
+ @param [in] pSocket Address of the 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
+EslTcpConnectPoll4 (
+ IN DT_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;
+}
+
+
+/**
+ Connect to a remote system via the network.
+
+ The ::TcpConnectStart4= routine starts the connection processing
+ for a TCP4 port.
+
+ @param [in] pSocket Address of the socket structure.
+
+ @param [in] pSockAddr Network address of the remote system.
+
+ @param [in] SockAddrLength Length in bytes of the network address.
+
+ @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
+EslTcpConnectStart4 (
+ IN DT_SOCKET * pSocket,
+ IN const struct sockaddr * pSockAddr,
+ IN socklen_t SockAddrLength
+ )
+{
+ struct sockaddr_in LocalAddress;
+ DT_PORT * pPort;
+ DT_TCP4_CONTEXT * pTcp4;
+ CONST struct sockaddr_in * pIp4Address;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Validate the address length
+ //
+ Status = EFI_SUCCESS;
+ pIp4Address = (CONST struct sockaddr_in *) pSockAddr;
+ if ( SockAddrLength >= ( sizeof ( *pIp4Address )
+ - sizeof ( pIp4Address->sin_zero ))) {
+ //
+ // Determine if BIND was already called
+ //
+ if ( NULL == pSocket->pPortList ) {
+ //
+ // Allow any local port
+ //
+ ZeroMem ( &LocalAddress, sizeof ( LocalAddress ));
+ LocalAddress.sin_len = sizeof ( LocalAddress );
+ LocalAddress.sin_family = AF_INET;
+ Status = EslSocketBind ( &pSocket->SocketProtocol,
+ (struct sockaddr *)&LocalAddress,
+ LocalAddress.sin_len,
+ &pSocket->errno );
+ }
+ if ( NULL != pSocket->pPortList ) {
+ //
+ // Walk the list of ports
+ //
+ pPort = pSocket->pPortList;
+ while ( NULL != pPort ) {
+ //
+ // Set the remote address
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ *(UINT32 *)&pTcp4->ConfigData.AccessPoint.RemoteAddress = pIp4Address->sin_addr.s_addr;
+ pTcp4->ConfigData.AccessPoint.RemotePort = SwapBytes16 ( pIp4Address->sin_port );
+
+ //
+ // Set the next port
+ //
+ pPort = pPort->pLinkSocket;
+ }
+
+ //
+ // Attempt a connection using the first adapter
+ //
+ Status = EslTcpConnectAttempt4 ( pSocket );
+ }
+ }
+ else {
+ DEBUG (( DEBUG_CONNECT,
+ "ERROR - Invalid TCP4 address length: %d\r\n",
+ SockAddrLength ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EINVAL;
+ }
+
+ //
+ // Return the initialization status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Initialize the TCP4 service.
+
+ This routine initializes the TCP4 service after its service binding
+ protocol was located on a controller.
+
+ @param [in] pService DT_SERVICE structure address
+
+ @retval EFI_SUCCESS The service was properly initialized
+ @retval other A failure occurred during the service initialization
+
+**/
+EFI_STATUS
+EFIAPI
+EslTcpInitialize4 (
+ IN DT_SERVICE * pService
+ )
+{
+ DT_LAYER * pLayer;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Identify the service
+ //
+ pService->NetworkType = NETWORK_TYPE_TCP4;
+
+ //
+ // Connect this service to the service list
+ //
+ pLayer = &mEslLayer;
+ pService->pNext = pLayer->pTcp4List;
+ pLayer->pTcp4List = pService;
+
+ //
+ // Assume the list is empty
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Return the initialization status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Get the local socket address
+
+ @param [in] pSocket Address of the socket structure.
+
+ @param [out] pAddress Network address to receive the local system address
+
+ @param [in,out] pAddressLength Length of the local network address structure
+
+ @retval EFI_SUCCESS - Address available
+ @retval Other - Failed to get the address
+
+**/
+EFI_STATUS
+EslTcpGetLocalAddress4 (
+ IN DT_SOCKET * pSocket,
+ OUT struct sockaddr * pAddress,
+ IN OUT socklen_t * pAddressLength
+ )
+{
+ socklen_t LengthInBytes;
+ DT_PORT * pPort;
+ struct sockaddr_in * pLocalAddress;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Verify the socket layer synchronization
+ //
+ VERIFY_TPL ( TPL_SOCKETS );
+
+ //
+ // Verify that there is just a single connection
+ //
+ pPort = pSocket->pPortList;
+ if (( NULL != pPort ) && ( NULL == pPort->pLinkSocket )) {
+ //
+ // Verify the address length
+ //
+ LengthInBytes = sizeof ( struct sockaddr_in );
+ if ( LengthInBytes <= * pAddressLength ) {
+ //
+ // Return the local address
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ pLocalAddress = (struct sockaddr_in *)pAddress;
+ ZeroMem ( pLocalAddress, LengthInBytes );
+ pLocalAddress->sin_family = AF_INET;
+ pLocalAddress->sin_len = (uint8_t)LengthInBytes;
+ pLocalAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.StationPort );
+ CopyMem ( &pLocalAddress->sin_addr,
+ &pTcp4->ConfigData.AccessPoint.StationAddress.Addr[0],
+ sizeof ( pLocalAddress->sin_addr ));
+ pSocket->errno = 0;
+ Status = EFI_SUCCESS;
+ }
+ else {
+ pSocket->errno = EINVAL;
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+ else {
+ pSocket->errno = ENOTCONN;
+ Status = EFI_NOT_STARTED;
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Get the remote socket address
+
+ @param [in] pSocket Address of the socket structure.
+
+ @param [out] pAddress Network address to receive the remote system address
+
+ @param [in,out] pAddressLength Length of the remote network address structure
+
+ @retval EFI_SUCCESS - Address available
+ @retval Other - Failed to get the address
+
+**/
+EFI_STATUS
+EslTcpGetRemoteAddress4 (
+ IN DT_SOCKET * pSocket,
+ OUT struct sockaddr * pAddress,
+ IN OUT socklen_t * pAddressLength
+ )
+{
+ socklen_t LengthInBytes;
+ DT_PORT * pPort;
+ struct sockaddr_in * pRemoteAddress;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Verify the socket layer synchronization
+ //
+ VERIFY_TPL ( TPL_SOCKETS );
+
+ //
+ // Verify that there is just a single connection
+ //
+ pPort = pSocket->pPortList;
+ if (( NULL != pPort ) && ( NULL == pPort->pLinkSocket )) {
+ //
+ // Verify the address length
+ //
+ LengthInBytes = sizeof ( struct sockaddr_in );
+ if ( LengthInBytes <= * pAddressLength ) {
+ //
+ // Return the local address
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ pRemoteAddress = (struct sockaddr_in *)pAddress;
+ ZeroMem ( pRemoteAddress, LengthInBytes );
+ pRemoteAddress->sin_family = AF_INET;
+ pRemoteAddress->sin_len = (uint8_t)LengthInBytes;
+ pRemoteAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.RemotePort );
+ CopyMem ( &pRemoteAddress->sin_addr,
+ &pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0],
+ sizeof ( pRemoteAddress->sin_addr ));
+ pSocket->errno = 0;
+ Status = EFI_SUCCESS;
+ }
+ else {
+ pSocket->errno = EINVAL;
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+ else {
+ pSocket->errno = ENOTCONN;
+ Status = EFI_NOT_STARTED;
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Establish the known port to listen for network connections.
+
+ The ::Tcp4Listen routine places the port into a state that enables connection
+ attempts. Connections are placed into FIFO order in a queue to be serviced
+ by the application. The application calls the ::Tcp4Accept routine to remove
+ the next connection from the queue and get the associated socket. The
+ <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html">POSIX</a>
+ documentation for the listen routine is available online for reference.
+
+ @param [in] pSocket Address of the socket structure.
+
+ @retval EFI_SUCCESS - Socket successfully created
+ @retval Other - Failed to enable the socket for listen
+
+**/
+EFI_STATUS
+EslTcpListen4 (
+ IN DT_SOCKET * pSocket
+ )
+{
+ DT_PORT * pNextPort;
+ DT_PORT * pPort;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_TCP4_PROTOCOL * pTcp4Protocol;
+ 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
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,
+ TPL_SOCKETS,
+ (EFI_EVENT_NOTIFY)EslTcpListenComplete4,
+ pPort,
+ &pTcp4->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",
+ pTcp4->ListenToken.CompletionToken.Event ));
+
+ //
+ // Configure the port
+ //
+ pTcp4Protocol = pTcp4->pProtocol;
+ Status = pTcp4Protocol->Configure ( pTcp4Protocol,
+ &pTcp4->ConfigData );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_LISTEN,
+ "ERROR - Failed to configure the Tcp4 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 ));
+ pTcp4->bConfigured = TRUE;
+
+ //
+ // Start the listen operation on the port
+ //
+ Status = pTcp4Protocol->Accept ( pTcp4Protocol,
+ &pTcp4->ListenToken );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_LISTEN,
+ "ERROR - Failed Tcp4 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 ))
+ {
+ EslTcpPortCloseStart4 ( 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.
+
+ @param Event The listen completion event
+
+ @param pPort The DT_PORT structure address
+
+**/
+VOID
+EslTcpListenComplete4 (
+ IN EFI_EVENT Event,
+ IN DT_PORT * pPort
+ )
+{
+ EFI_HANDLE ChildHandle;
+ EFI_TCP4_CONFIG_DATA * pConfigData;
+ DT_LAYER * pLayer;
+ DT_PORT * pNewPort;
+ DT_SOCKET * pNewSocket;
+ DT_SOCKET * pSocket;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_TCP4_PROTOCOL * pTcp4Protocol;
+ EFI_STATUS Status;
+ EFI_HANDLE TcpPortHandle;
+ EFI_STATUS TempStatus;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Determine if this connection fits into the connection FIFO
+ //
+ pSocket = pPort->pSocket;
+ TcpPortHandle = pPort->Context.Tcp4.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->Domain = pSocket->Domain;
+ pNewSocket->Protocol = pSocket->Protocol;
+ pNewSocket->Type = pSocket->Type;
+
+ //
+ // Allocate a port for this connection
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ Status = EslTcpPortAllocate4 ( pNewSocket,
+ pPort->pService,
+ TcpPortHandle,
+ &pTcp4->ConfigData.AccessPoint.StationAddress.Addr[0],
+ 0,
+ DEBUG_CONNECTION,
+ &pNewPort );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Restart the listen operation on the port
+ //
+ pTcp4Protocol = pTcp4->pProtocol;
+ Status = pTcp4Protocol->Accept ( pTcp4Protocol,
+ &pTcp4->ListenToken );
+
+ //
+ // Close the TCP port using SocketClose
+ //
+ TcpPortHandle = NULL;
+ pTcp4 = &pNewPort->Context.Tcp4;
+ pTcp4->bConfigured = TRUE;
+
+ //
+ // Check for an accept call error
+ //
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Get the port configuration
+ //
+ pConfigData = &pTcp4->ConfigData;
+ pConfigData->ControlOption = &pTcp4->Option;
+ pTcp4Protocol = pTcp4->pProtocol;
+ Status = pTcp4Protocol->GetModeData ( pTcp4Protocol,
+ 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 %d.%d.%d.%d:%d connected to %d.%d.%d.%d:%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.StationPort,
+ pConfigData->AccessPoint.RemoteAddress.Addr[0],
+ pConfigData->AccessPoint.RemoteAddress.Addr[1],
+ pConfigData->AccessPoint.RemoteAddress.Addr[2],
+ pConfigData->AccessPoint.RemoteAddress.Addr[3],
+ 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
+ //
+ EslTcpRxStart4 ( 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
+ //
+ EslTcpPortCloseStart4 ( 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 ( );
+}
+
+
+/**
+ Allocate and initialize a DT_PORT structure.
+
+ @param [in] pSocket Address of the socket structure.
+ @param [in] pService Address of the DT_SERVICE structure.
+ @param [in] ChildHandle TCP4 child handle
+ @param [in] pIpAddress Buffer containing IP4 network address of the local host
+ @param [in] PortNumber Tcp4 port number
+ @param [in] DebugFlags Flags for debug messages
+ @param [out] ppPort Buffer to receive new DT_PORT structure address
+
+ @retval EFI_SUCCESS - Socket successfully created
+
+ **/
+EFI_STATUS
+EslTcpPortAllocate4 (
+ IN DT_SOCKET * pSocket,
+ IN DT_SERVICE * pService,
+ IN EFI_HANDLE ChildHandle,
+ IN CONST UINT8 * pIpAddress,
+ IN UINT16 PortNumber,
+ IN UINTN DebugFlags,
+ OUT DT_PORT ** ppPort
+ )
+{
+ UINTN LengthInBytes;
+ EFI_TCP4_ACCESS_POINT * pAccessPoint;
+ DT_LAYER * pLayer;
+ DT_PORT * pPort;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Use for/break instead of goto
+ for ( ; ; ) {
+ //
+ // Allocate a port structure
+ //
+ pLayer = &mEslLayer;
+ LengthInBytes = sizeof ( *pPort );
+ Status = gBS->AllocatePool ( EfiRuntimeServicesData,
+ LengthInBytes,
+ (VOID **)&pPort );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL | DEBUG_INIT,
+ "ERROR - Failed to allocate the port structure, Status: %r\r\n",
+ Status ));
+ pSocket->errno = ENOMEM;
+ pPort = NULL;
+ break;
+ }
+ DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT,
+ "0x%08x: Allocate pPort, %d bytes\r\n",
+ pPort,
+ LengthInBytes ));
+
+ //
+ // Initialize the port
+ //
+ ZeroMem ( pPort, LengthInBytes );
+ pPort->Signature = PORT_SIGNATURE;
+ pPort->pService = pService;
+ pPort->pSocket = pSocket;
+ pPort->pfnCloseStart = EslTcpPortCloseStart4;
+ pPort->DebugFlags = DebugFlags;
+
+ //
+ // Allocate the receive event
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,
+ TPL_SOCKETS,
+ (EFI_EVENT_NOTIFY)EslTcpRxComplete4,
+ pPort,
+ &pTcp4->RxToken.CompletionToken.Event);
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DebugFlags,
+ "ERROR - Failed to create the receive event, Status: %r\r\n",
+ Status ));
+ pSocket->errno = ENOMEM;
+ break;
+ }
+ DEBUG (( DEBUG_RX | DEBUG_POOL,
+ "0x%08x: Created receive event\r\n",
+ pTcp4->RxToken.CompletionToken.Event ));
+
+ //
+ // Allocate the urgent transmit event
+ //
+ Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,
+ TPL_SOCKETS,
+ (EFI_EVENT_NOTIFY)EslTcpTxOobComplete4,
+ pPort,
+ &pTcp4->TxOobToken.CompletionToken.Event);
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DebugFlags,
+ "ERROR - Failed to create the urgent transmit event, Status: %r\r\n",
+ Status ));
+ pSocket->errno = ENOMEM;
+ break;
+ }
+ DEBUG (( DEBUG_CLOSE | DEBUG_POOL,
+ "0x%08x: Created urgent transmit event\r\n",
+ pTcp4->TxOobToken.CompletionToken.Event ));
+
+ //
+ // Allocate the normal transmit event
+ //
+ Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,
+ TPL_SOCKETS,
+ (EFI_EVENT_NOTIFY)EslTcpTxComplete4,
+ pPort,
+ &pTcp4->TxToken.CompletionToken.Event);
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DebugFlags,
+ "ERROR - Failed to create the normal transmit event, Status: %r\r\n",
+ Status ));
+ pSocket->errno = ENOMEM;
+ break;
+ }
+ DEBUG (( DEBUG_CLOSE | DEBUG_POOL,
+ "0x%08x: Created normal transmit event\r\n",
+ pTcp4->TxToken.CompletionToken.Event ));
+
+ //
+ // Allocate the close event
+ //
+ Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,
+ TPL_SOCKETS,
+ (EFI_EVENT_NOTIFY)EslTcpPortCloseComplete4,
+ pPort,
+ &pTcp4->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",
+ pTcp4->CloseToken.CompletionToken.Event ));
+
+ //
+ // Allocate the connection event
+ //
+ Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,
+ TPL_SOCKETS,
+ (EFI_EVENT_NOTIFY)EslTcpConnectComplete4,
+ pPort,
+ &pTcp4->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",
+ pTcp4->ConnectToken.CompletionToken.Event ));
+
+ //
+ // Open the port protocol
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ (VOID **) &pTcp4->pProtocol,
+ pLayer->ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DebugFlags,
+ "ERROR - Failed to open gEfiTcp4ProtocolGuid on controller 0x%08x\r\n",
+ pTcp4->Handle ));
+ pSocket->errno = EEXIST;
+ break;
+ }
+ DEBUG (( DebugFlags,
+ "0x%08x: gEfiTcp4ProtocolGuid opened on controller 0x%08x\r\n",
+ pTcp4->pProtocol,
+ ChildHandle ));
+
+ //
+ // Set the port address
+ //
+ pTcp4->Handle = ChildHandle;
+ pAccessPoint = &pPort->Context.Tcp4.ConfigData.AccessPoint;
+ pAccessPoint->StationPort = PortNumber;
+ if (( 0 == pIpAddress[0])
+ && ( 0 == pIpAddress[1])
+ && ( 0 == pIpAddress[2])
+ && ( 0 == pIpAddress[3])) {
+ pAccessPoint->UseDefaultAddress = TRUE;
+ }
+ else {
+ pAccessPoint->StationAddress.Addr[0] = pIpAddress[0];
+ pAccessPoint->StationAddress.Addr[1] = pIpAddress[1];
+ pAccessPoint->StationAddress.Addr[2] = pIpAddress[2];
+ pAccessPoint->StationAddress.Addr[3] = pIpAddress[3];
+ pAccessPoint->SubnetMask.Addr[0] = 0xff;
+ pAccessPoint->SubnetMask.Addr[1] = 0xff;
+ pAccessPoint->SubnetMask.Addr[2] = 0xff;
+ pAccessPoint->SubnetMask.Addr[3] = 0xff;
+ }
+ pAccessPoint->ActiveFlag = FALSE;
+ pTcp4->ConfigData.TimeToLive = 255;
+
+ //
+ // Verify the socket layer synchronization
+ //
+ VERIFY_TPL ( TPL_SOCKETS );
+
+ //
+ // Add this port to the socket
+ //
+ pPort->pLinkSocket = pSocket->pPortList;
+ pSocket->pPortList = pPort;
+ DEBUG (( DebugFlags,
+ "0x%08x: Socket adding port: 0x%08x\r\n",
+ pSocket,
+ pPort ));
+
+ //
+ // Add this port to the service
+ //
+ pPort->pLinkService = pService->pPortList;
+ pService->pPortList = pPort;
+
+ //
+ // Return the port
+ //
+ *ppPort = pPort;
+ break;
+ }
+
+ //
+ // Clean up after the error if necessary
+ //
+ if (( EFI_ERROR ( Status )) && ( NULL != pPort )) {
+ //
+ // Close the port
+ //
+ EslTcpPortClose4 ( pPort );
+ }
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Close a TCP4 port.
+
+ This routine releases the resources allocated by
+ ::TcpPortAllocate4().
+
+ @param [in] pPort Address of the port structure.
+
+ @retval EFI_SUCCESS The port is closed
+ @retval other Port close error
+
+**/
+EFI_STATUS
+EslTcpPortClose4 (
+ IN DT_PORT * pPort
+ )
+{
+ UINTN DebugFlags;
+ DT_LAYER * pLayer;
+ DT_PACKET * pPacket;
+ DT_PORT * pPreviousPort;
+ DT_SERVICE * pService;
+ DT_SOCKET * pSocket;
+ EFI_SERVICE_BINDING_PROTOCOL * pTcp4Service;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Verify the socket layer synchronization
+ //
+ VERIFY_TPL ( TPL_SOCKETS );
+
+ //
+ // Locate the port in the socket list
+ //
+ Status = EFI_SUCCESS;
+ pLayer = &mEslLayer;
+ DebugFlags = pPort->DebugFlags;
+ pSocket = pPort->pSocket;
+ pPreviousPort = pSocket->pPortList;
+ if ( pPreviousPort == pPort ) {
+ //
+ // Remove this port from the head of the socket list
+ //
+ pSocket->pPortList = pPort->pLinkSocket;
+ }
+ else {
+ //
+ // Locate the port in the middle of the socket list
+ //
+ while (( NULL != pPreviousPort )
+ && ( pPreviousPort->pLinkSocket != pPort )) {
+ pPreviousPort = pPreviousPort->pLinkSocket;
+ }
+ if ( NULL != pPreviousPort ) {
+ //
+ // Remove the port from the middle of the socket list
+ //
+ pPreviousPort->pLinkSocket = pPort->pLinkSocket;
+ }
+ }
+
+ //
+ // Locate the port in the service list
+ //
+ pService = pPort->pService;
+ pPreviousPort = pService->pPortList;
+ if ( pPreviousPort == pPort ) {
+ //
+ // Remove this port from the head of the service list
+ //
+ pService->pPortList = pPort->pLinkService;
+ }
+ else {
+ //
+ // Locate the port in the middle of the service list
+ //
+ while (( NULL != pPreviousPort )
+ && ( pPreviousPort->pLinkService != pPort )) {
+ pPreviousPort = pPreviousPort->pLinkService;
+ }
+ if ( NULL != pPreviousPort ) {
+ //
+ // Remove the port from the middle of the service list
+ //
+ pPreviousPort->pLinkService = pPort->pLinkService;
+ }
+ }
+
+ //
+ // Empty the urgent receive queue
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ while ( NULL != pSocket->pRxOobPacketListHead ) {
+ pPacket = pSocket->pRxOobPacketListHead;
+ pSocket->pRxOobPacketListHead = pPacket->pNext;
+ pSocket->RxOobBytes -= pPacket->Op.Tcp4Rx.ValidBytes;
+ EslSocketPacketFree ( pPacket, DEBUG_RX );
+ }
+ pSocket->pRxOobPacketListTail = NULL;
+ ASSERT ( 0 == pSocket->RxOobBytes );
+
+ //
+ // Empty the receive queue
+ //
+ while ( NULL != pSocket->pRxPacketListHead ) {
+ pPacket = pSocket->pRxPacketListHead;
+ pSocket->pRxPacketListHead = pPacket->pNext;
+ pSocket->RxBytes -= pPacket->Op.Tcp4Rx.ValidBytes;
+ EslSocketPacketFree ( pPacket, DEBUG_RX );
+ }
+ pSocket->pRxPacketListTail = NULL;
+ ASSERT ( 0 == pSocket->RxBytes );
+
+ //
+ // Empty the receive free queue
+ //
+ while ( NULL != pSocket->pRxFree ) {
+ pPacket = pSocket->pRxFree;
+ pSocket->pRxFree = pPacket->pNext;
+ EslSocketPacketFree ( pPacket, DEBUG_RX );
+ }
+
+ //
+ // Done with the connect event
+ //
+ if ( NULL != pTcp4->ConnectToken.CompletionToken.Event ) {
+ Status = gBS->CloseEvent ( pTcp4->ConnectToken.CompletionToken.Event );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Closed connect event\r\n",
+ pTcp4->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 != pTcp4->CloseToken.CompletionToken.Event ) {
+ Status = gBS->CloseEvent ( pTcp4->CloseToken.CompletionToken.Event );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Closed close event\r\n",
+ pTcp4->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 != pTcp4->ListenToken.CompletionToken.Event ) {
+ Status = gBS->CloseEvent ( pTcp4->ListenToken.CompletionToken.Event );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Closed listen completion event\r\n",
+ pTcp4->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 );
+ }
+ }
+
+ //
+ // Done with the receive event
+ //
+ if ( NULL != pTcp4->RxToken.CompletionToken.Event ) {
+ Status = gBS->CloseEvent ( pTcp4->RxToken.CompletionToken.Event );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Closed receive event\r\n",
+ pTcp4->RxToken.CompletionToken.Event ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DebugFlags,
+ "ERROR - Failed to close the receive event, Status: %r\r\n",
+ Status ));
+ ASSERT ( EFI_SUCCESS == Status );
+ }
+ }
+
+ //
+ // Done with the normal transmit event
+ //
+ if ( NULL != pTcp4->TxToken.CompletionToken.Event ) {
+ Status = gBS->CloseEvent ( pTcp4->TxToken.CompletionToken.Event );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Closed normal transmit event\r\n",
+ pTcp4->TxToken.CompletionToken.Event ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DebugFlags,
+ "ERROR - Failed to close the normal transmit event, Status: %r\r\n",
+ Status ));
+ ASSERT ( EFI_SUCCESS == Status );
+ }
+ }
+
+ //
+ // Done with the urgent transmit event
+ //
+ if ( NULL != pTcp4->TxOobToken.CompletionToken.Event ) {
+ Status = gBS->CloseEvent ( pTcp4->TxOobToken.CompletionToken.Event );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Closed urgent transmit event\r\n",
+ pTcp4->TxOobToken.CompletionToken.Event ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DebugFlags,
+ "ERROR - Failed to close the urgent transmit event, Status: %r\r\n",
+ Status ));
+ ASSERT ( EFI_SUCCESS == Status );
+ }
+ }
+
+ //
+ // Done with the TCP protocol
+ //
+ pTcp4Service = pService->pInterface;
+ if ( NULL != pTcp4->pProtocol ) {
+ Status = gBS->CloseProtocol ( pTcp4->Handle,
+ &gEfiTcp4ProtocolGuid,
+ pLayer->ImageHandle,
+ NULL );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags,
+ "0x%08x: gEfiTcp4ProtocolGuid closed on controller 0x%08x\r\n",
+ pTcp4->pProtocol,
+ pTcp4->Handle ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DebugFlags,
+ "ERROR - Failed to close gEfiTcp4ProtocolGuid opened on controller 0x%08x, Status: %r\r\n",
+ pTcp4->Handle,
+ Status ));
+ ASSERT ( EFI_SUCCESS == Status );
+ }
+ }
+
+ //
+ // Done with the TCP port
+ //
+ if ( NULL != pTcp4->Handle ) {
+ Status = pTcp4Service->DestroyChild ( pTcp4Service,
+ pTcp4->Handle );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Tcp4 port handle destroyed\r\n",
+ pTcp4->Handle ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL,
+ "ERROR - Failed to destroy the Tcp4 port handle, Status: %r\r\n",
+ Status ));
+ ASSERT ( EFI_SUCCESS == Status );
+ }
+ }
+
+ //
+ // Release the port structure
+ //
+ Status = gBS->FreePool ( pPort );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Free pPort, %d bytes\r\n",
+ pPort,
+ sizeof ( *pPort )));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL,
+ "ERROR - Failed to free pPort: 0x%08x, Status: %r\r\n",
+ pPort,
+ Status ));
+ ASSERT ( EFI_SUCCESS == Status );
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Process the port close completion
+
+ @param Event The close completion event
+
+ @param pPort The DT_PORT structure address
+
+**/
+VOID
+EslTcpPortCloseComplete4 (
+ IN EFI_EVENT Event,
+ IN DT_PORT * pPort
+ )
+{
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Update the port state
+ //
+ pPort->State = PORT_STATE_CLOSE_DONE;
+
+ //
+ // Release the resources once the receive operation completes
+ //
+ Status = EslTcpPortCloseRxDone4 ( pPort );
+ DBG_EXIT_STATUS ( Status );
+}
+
+
+/**
+ Start the close operation on a TCP4 port, state 1.
+
+ Closing a port goes through the following states:
+ 1. Port close starting - Mark the port as closing and wait for transmission to complete
+ 2. Port TX close done - Transmissions complete, close the port and abort the receives
+ 3. Port RX close done - Receive operations complete, close the port
+ 4. Port closed - Release the port resources
+
+ @param [in] pPort Address of the port structure.
+ @param [in] bCloseNow Set TRUE to abort active transfers
+ @param [in] DebugFlags Flags for debug messages
+
+ @retval EFI_SUCCESS The port is closed, not normally returned
+ @retval EFI_NOT_READY The port has started the closing process
+ @retval EFI_ALREADY_STARTED Error, the port is in the wrong state,
+ most likely the routine was called already.
+
+**/
+EFI_STATUS
+EslTcpPortCloseStart4 (
+ IN DT_PORT * pPort,
+ IN BOOLEAN bCloseNow,
+ IN UINTN DebugFlags
+ )
+{
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Verify the socket layer synchronization
+ //
+ VERIFY_TPL ( TPL_SOCKETS );
+
+ //
+ // Mark the port as closing
+ //
+ Status = EFI_ALREADY_STARTED;
+ pSocket = pPort->pSocket;
+ pSocket->errno = EALREADY;
+ if ( PORT_STATE_CLOSE_STARTED > pPort->State ) {
+
+ //
+ // Update the port state
+ //
+ pPort->State = PORT_STATE_CLOSE_STARTED;
+ DEBUG (( DEBUG_CLOSE | DEBUG_INFO,
+ "0x%08x: Port Close State: PORT_STATE_CLOSE_STARTED\r\n",
+ pPort ));
+ pPort->bCloseNow = bCloseNow;
+ pPort->DebugFlags = DebugFlags;
+
+ //
+ // Determine if transmits are complete
+ //
+ Status = EslTcpPortCloseTxDone4 ( pPort );
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Port close state 3
+
+ Continue the close operation after the receive is complete.
+
+ @param [in] pPort Address of the port structure.
+
+ @retval EFI_SUCCESS The port is closed
+ @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
+EslTcpPortCloseRxDone4 (
+ IN DT_PORT * pPort
+ )
+{
+ PORT_STATE PortState;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Verify the socket layer synchronization
+ //
+ VERIFY_TPL ( TPL_SOCKETS );
+
+ //
+ // Verify that the port is closing
+ //
+ Status = EFI_ALREADY_STARTED;
+ PortState = pPort->State;
+ if (( PORT_STATE_CLOSE_TX_DONE == PortState )
+ || ( PORT_STATE_CLOSE_DONE == PortState )) {
+ //
+ // Determine if the receive operation is pending
+ //
+ Status = EFI_NOT_READY;
+ pTcp4 = &pPort->Context.Tcp4;
+ if ( NULL == pTcp4->pReceivePending ) {
+ //
+ // The receive operation is complete
+ // Update the port state
+ //
+ pPort->State = PORT_STATE_CLOSE_RX_DONE;
+ DEBUG (( DEBUG_CLOSE | DEBUG_INFO,
+ "0x%08x: Port Close State: PORT_STATE_CLOSE_RX_DONE\r\n",
+ pPort ));
+
+ //
+ // Determine if the close operation has completed
+ //
+ if ( PORT_STATE_CLOSE_DONE == PortState ) {
+ //
+ // The close operation has completed
+ // Release the port resources
+ //
+ Status = EslTcpPortClose4 ( pPort );
+ }
+ else
+ {
+ DEBUG (( DEBUG_CLOSE | DEBUG_INFO,
+ "0x%08x: Port Close: Close operation still pending!\r\n",
+ pPort ));
+ }
+ }
+ else {
+ DEBUG (( DEBUG_CLOSE | DEBUG_INFO,
+ "0x%08x: Port Close: Receive still pending!\r\n",
+ pPort ));
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Port close state 2
+
+ Continue the close operation after the transmission is complete.
+
+ @param [in] pPort Address of the 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
+EslTcpPortCloseTxDone4 (
+ IN DT_PORT * pPort
+ )
+{
+ DT_SOCKET * pSocket;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_TCP4_PROTOCOL * pTcp4Protocol;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Verify the socket layer synchronization
+ //
+ VERIFY_TPL ( TPL_SOCKETS );
+
+ //
+ // All transmissions are complete or must be stopped
+ // Mark the port as TX complete
+ //
+ Status = EFI_ALREADY_STARTED;
+ if ( PORT_STATE_CLOSE_STARTED == pPort->State ) {
+ //
+ // Verify that the transmissions are complete
+ //
+ pSocket = pPort->pSocket;
+ if ( pPort->bCloseNow
+ || ( EFI_SUCCESS != pSocket->TxError )
+ || (( 0 == pSocket->TxOobBytes )
+ && ( 0 == pSocket->TxBytes ))) {
+ //
+ // Start the close operation on the port
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ pTcp4->CloseToken.AbortOnClose = FALSE;
+ pTcp4Protocol = pTcp4->pProtocol;
+ if ( !pTcp4->bConfigured ) {
+ //
+ // Skip the close operation since the port is not
+ // configured
+ //
+ // Update the port state
+ //
+ pPort->State = PORT_STATE_CLOSE_DONE;
+ DEBUG (( DEBUG_CLOSE | DEBUG_INFO,
+ "0x%08x: Port Close State: PORT_STATE_CLOSE_DONE\r\n",
+ pPort ));
+ Status = EFI_SUCCESS;
+ }
+ else {
+ //
+ // Close the configured port
+ //
+ Status = pTcp4Protocol->Close ( pTcp4Protocol,
+ &pTcp4->CloseToken );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO,
+ "0x%08x: Port close started\r\n",
+ pPort ));
+
+ //
+ // Update the port state
+ //
+ pPort->State = PORT_STATE_CLOSE_TX_DONE;
+ DEBUG (( DEBUG_CLOSE | DEBUG_INFO,
+ "0x%08x: Port Close State: PORT_STATE_CLOSE_TX_DONE\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 ));
+ ASSERT ( EFI_SUCCESS == Status );
+ }
+ }
+
+ //
+ // Determine if the receive operation is pending
+ //
+ if ( !EFI_ERROR ( Status )) {
+ Status = EslTcpPortCloseRxDone4 ( pPort );
+ }
+ }
+ else {
+ //
+ // Transmissions are still active, exit
+ //
+ DEBUG (( DEBUG_CLOSE | DEBUG_INFO,
+ "0x%08x: Port Close: Transmits are still pending!\r\n",
+ pPort ));
+ Status = EFI_NOT_READY;
+ pSocket->errno = EAGAIN;
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Receive data from a network connection.
+
+
+ @param [in] pSocket Address of a DT_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 [out] pAddress Network address to receive the remote system address
+
+ @param [in,out] pAddressLength Length of the remote network address structure
+
+ @retval EFI_SUCCESS - Socket data successfully received
+
+ **/
+EFI_STATUS
+EslTcpReceive4 (
+ IN DT_SOCKET * pSocket,
+ IN INT32 Flags,
+ IN size_t BufferLength,
+ IN UINT8 * pBuffer,
+ OUT size_t * pDataLength,
+ OUT struct sockaddr * pAddress,
+ IN OUT socklen_t * pAddressLength
+ )
+{
+ socklen_t AddressLength;
+ size_t BytesToCopy;
+ in_addr_t IpAddress;
+ size_t LengthInBytes;
+ DT_PACKET * pPacket;
+ DT_PORT * pPort;
+ DT_PACKET ** ppQueueHead;
+ DT_PACKET ** ppQueueTail;
+ struct sockaddr_in * pRemoteAddress;
+ size_t * pRxDataBytes;
+ DT_TCP4_CONTEXT * pTcp4;
+ struct sockaddr_in RemoteAddress;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume failure
+ //
+ Status = EFI_UNSUPPORTED;
+ pSocket->errno = ENOTCONN;
+
+ //
+ // Verify that the socket is connected
+ //
+ if (( SOCKET_STATE_CONNECTED == pSocket->State )
+ || ( PORT_STATE_RX_ERROR == pSocket->State )) {
+ //
+ // Locate the port
+ //
+ pPort = pSocket->pPortList;
+ if ( NULL != pPort ) {
+ //
+ // Determine the queue head
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ if ( 0 != ( Flags & MSG_OOB )) {
+ ppQueueHead = &pSocket->pRxOobPacketListHead;
+ ppQueueTail = &pSocket->pRxOobPacketListTail;
+ pRxDataBytes = &pSocket->RxOobBytes;
+ }
+ else {
+ ppQueueHead = &pSocket->pRxPacketListHead;
+ ppQueueTail = &pSocket->pRxPacketListTail;
+ pRxDataBytes = &pSocket->RxBytes;
+ }
+
+ //
+ // Determine if there is any data on the queue
+ //
+ pPacket = *ppQueueHead;
+ if ( NULL != pPacket ) {
+ //
+ // Validate the return address parameters
+ //
+ if (( NULL == pAddress ) || ( NULL != pAddressLength )) {
+ //
+ // Return the remote system address if requested
+ //
+ if ( NULL != pAddress ) {
+ //
+ // Build the remote address
+ //
+ ZeroMem ( &RemoteAddress, sizeof ( RemoteAddress ));
+ RemoteAddress.sin_len = sizeof ( RemoteAddress );
+ RemoteAddress.sin_family = AF_INET;
+ IpAddress = pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3];
+ IpAddress <<= 8;
+ IpAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2];
+ IpAddress <<= 8;
+ IpAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1];
+ IpAddress <<= 8;
+ IpAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0];
+ RemoteAddress.sin_addr.s_addr = IpAddress;
+ RemoteAddress.sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.RemotePort );
+
+ //
+ // Copy the address
+ //
+ pRemoteAddress = (struct sockaddr_in *)pAddress;
+ AddressLength = sizeof ( *pRemoteAddress );
+ if ( AddressLength > *pAddressLength ) {
+ AddressLength = *pAddressLength;
+ }
+ CopyMem ( pRemoteAddress,
+ &RemoteAddress,
+ AddressLength );
+
+ //
+ // Update the address length
+ //
+ *pAddressLength = AddressLength;
+ }
+
+ //
+ // Copy the received data
+ //
+ LengthInBytes = 0;
+ do {
+ //
+ // Determine the amount of received data
+ //
+ BytesToCopy = pPacket->Op.Tcp4Rx.ValidBytes;
+ if (( BufferLength - LengthInBytes ) < BytesToCopy ) {
+ BytesToCopy = BufferLength - LengthInBytes;
+ }
+ LengthInBytes += BytesToCopy;
+
+ //
+ // 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,
+ BytesToCopy ));
+ CopyMem ( pBuffer, pPacket->Op.Tcp4Rx.pBuffer, BytesToCopy );
+
+ //
+ // Determine if the data is being read
+ //
+ if ( 0 == ( Flags & MSG_PEEK )) {
+ //
+ // Account for the bytes consumed
+ //
+ pPacket->Op.Tcp4Rx.pBuffer += BytesToCopy;
+ pPacket->Op.Tcp4Rx.ValidBytes -= BytesToCopy;
+ *pRxDataBytes -= BytesToCopy;
+ DEBUG (( DEBUG_RX,
+ "0x%08x: Port account for 0x%08x bytes\r\n",
+ pPort,
+ BytesToCopy ));
+
+ //
+ // Determine if the entire packet was consumed
+ //
+ if (( 0 == pPacket->Op.Tcp4Rx.ValidBytes )
+ || ( SOCK_STREAM != pSocket->Type )) {
+ //
+ // All done with this packet
+ // Account for any discarded data
+ //
+ *pRxDataBytes -= pPacket->Op.Tcp4Rx.ValidBytes;
+ if ( 0 != pPacket->Op.Tcp4Rx.ValidBytes ) {
+ DEBUG (( DEBUG_RX,
+ "0x%08x: Port, packet read, skipping over 0x%08x bytes\r\n",
+ pPort,
+ pPacket->Op.Tcp4Rx.ValidBytes ));
+ }
+
+ //
+ // Remove this packet from the queue
+ //
+ *ppQueueHead = pPacket->pNext;
+ if ( NULL == *ppQueueHead ) {
+ *ppQueueTail = NULL;
+ }
+
+ //
+ // Move the packet to the free queue
+ //
+ pPacket->pNext = pSocket->pRxFree;
+ pSocket->pRxFree = pPacket;
+ DEBUG (( DEBUG_RX,
+ "0x%08x: Port freeing packet 0x%08x\r\n",
+ pPort,
+ pPacket ));
+
+ //
+ // Restart this receive operation if necessary
+ //
+ if (( NULL == pTcp4->pReceivePending )
+ && ( MAX_RX_DATA > pSocket->RxBytes )) {
+ EslTcpRxStart4 ( pPort );
+ }
+ }
+ }
+
+ //
+ // Get the next packet
+ //
+ pPacket = *ppQueueHead;
+ } while (( SOCK_STREAM == pSocket->Type )
+ && ( NULL != pPacket )
+ && ( 0 == ( Flags & MSG_PEEK ))
+ && ( BufferLength > LengthInBytes ));
+
+ //
+ // Return the data length
+ //
+ *pDataLength = LengthInBytes;
+
+ //
+ // Successful operation
+ //
+ Status = EFI_SUCCESS;
+ pSocket->errno = 0;
+ }
+ else {
+ //
+ // Bad return address pointer and length
+ //
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EINVAL;
+ }
+ }
+ else {
+ //
+ // The queue is empty
+ // Determine if it is time to return the receive error
+ //
+ if ( EFI_ERROR ( pSocket->RxError )
+ && ( NULL == pSocket->pRxPacketListHead )
+ && ( NULL == pSocket->pRxOobPacketListHead )) {
+ Status = pSocket->RxError;
+ switch ( Status ) {
+ default:
+ pSocket->errno = EIO;
+ break;
+
+ case EFI_CONNECTION_FIN:
+ pSocket->errno = ESHUTDOWN;
+ break;
+
+ case EFI_CONNECTION_REFUSED:
+ pSocket->errno = ECONNREFUSED;
+ break;
+
+ case EFI_CONNECTION_RESET:
+ pSocket->errno = ECONNRESET;
+ break;
+
+ case EFI_HOST_UNREACHABLE:
+ pSocket->errno = EHOSTUNREACH;
+ break;
+
+ case EFI_NETWORK_UNREACHABLE:
+ pSocket->errno = ENETUNREACH;
+ break;
+
+ case EFI_PORT_UNREACHABLE:
+ pSocket->errno = EPROTONOSUPPORT;
+ break;
+
+ case EFI_PROTOCOL_UNREACHABLE:
+ pSocket->errno = ENOPROTOOPT;
+ break;
+ }
+ pSocket->RxError = EFI_SUCCESS;
+ }
+ else {
+ Status = EFI_NOT_READY;
+ pSocket->errno = EAGAIN;
+ }
+ }
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Cancel the receive operations
+
+ @param [in] pSocket Address of a DT_SOCKET structure
+
+ @retval EFI_SUCCESS - The cancel was successful
+
+ **/
+EFI_STATUS
+EslTcpRxCancel4 (
+ IN DT_SOCKET * pSocket
+ )
+{
+ DT_PACKET * pPacket;
+ DT_PORT * pPort;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_TCP4_PROTOCOL * pTcp4Protocol;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume failure
+ //
+ Status = EFI_NOT_FOUND;
+
+ //
+ // Locate the port
+ //
+ pPort = pSocket->pPortList;
+ if ( NULL != pPort ) {
+ //
+ // Determine if a receive is pending
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ pPacket = pTcp4->pReceivePending;
+ if ( NULL != pPacket ) {
+ //
+ // Attempt to cancel the receive operation
+ //
+ pTcp4Protocol = pTcp4->pProtocol;
+ Status = pTcp4Protocol->Cancel ( pTcp4Protocol,
+ &pTcp4->RxToken.CompletionToken );
+ if ( EFI_NOT_FOUND == Status ) {
+ //
+ // The receive is complete
+ //
+ Status = EFI_SUCCESS;
+ }
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Process the receive completion
+
+ Buffer the data that was just received.
+
+ @param Event The receive completion event
+
+ @param pPort The DT_PORT structure address
+
+**/
+VOID
+EslTcpRxComplete4 (
+ IN EFI_EVENT Event,
+ IN DT_PORT * pPort
+ )
+{
+ BOOLEAN bUrgent;
+ size_t LengthInBytes;
+ DT_PACKET * pPacket;
+ DT_PACKET * pPrevious;
+ DT_SOCKET * pSocket;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Mark this receive complete
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ pPacket = pTcp4->pReceivePending;
+ pTcp4->pReceivePending = NULL;
+
+ //
+ // Determine if this receive was successful
+ //
+ pSocket = pPort->pSocket;
+ Status = pTcp4->RxToken.CompletionToken.Status;
+ if (( !EFI_ERROR ( Status )) && ( !pSocket->bRxDisable )) {
+ //
+ // Set the buffer size and address
+ //
+ pPacket->Op.Tcp4Rx.pBuffer = pPacket->Op.Tcp4Rx.RxData.FragmentTable[0].FragmentBuffer;
+ LengthInBytes = pPacket->Op.Tcp4Rx.RxData.DataLength;
+ pPacket->Op.Tcp4Rx.ValidBytes = LengthInBytes;
+ pPacket->pNext = NULL;
+
+ //
+ // Queue this packet
+ //
+ bUrgent = pPacket->Op.Tcp4Rx.RxData.UrgentFlag;
+ if ( bUrgent ) {
+ //
+ // Add packet to the urgent list
+ //
+ pPrevious = pSocket->pRxOobPacketListTail;
+ if ( NULL == pPrevious ) {
+ pSocket->pRxOobPacketListHead = pPacket;
+ }
+ else {
+ pPrevious->pNext = pPacket;
+ }
+ pSocket->pRxOobPacketListTail = pPacket;
+
+ //
+ // Account for the urgent data
+ //
+ pSocket->RxOobBytes += LengthInBytes;
+ }
+ else {
+ //
+ // Add packet to the normal list
+ //
+ pPrevious = pSocket->pRxPacketListTail;
+ if ( NULL == pPrevious ) {
+ pSocket->pRxPacketListHead = pPacket;
+ }
+ else {
+ pPrevious->pNext = pPacket;
+ }
+ pSocket->pRxPacketListTail = pPacket;
+
+ //
+ // Account for the normal data
+ //
+ pSocket->RxBytes += LengthInBytes;
+ }
+
+ //
+ // Log the received data
+ //
+ DEBUG (( DEBUG_RX | DEBUG_INFO,
+ "0x%08x: Packet queued on port 0x%08x with 0x%08x bytes of %s data\r\n",
+ pPacket,
+ pPort,
+ LengthInBytes,
+ bUrgent ? L"urgent" : L"normal" ));
+
+ //
+ // Attempt to restart this receive operation
+ //
+ if ( pSocket->MaxRxBuf > pSocket->RxBytes ) {
+ EslTcpRxStart4 ( pPort );
+ }
+ else {
+ DEBUG (( DEBUG_RX,
+ "0x%08x: Port RX suspended, 0x%08x bytes queued\r\n",
+ pPort,
+ pSocket->RxBytes ));
+ }
+ }
+ else
+ {
+ DEBUG (( DEBUG_RX | DEBUG_INFO,
+ "ERROR - Receiving packet 0x%08x, on port 0x%08x, Status:%r\r\n",
+ pPacket,
+ pPort,
+ Status ));
+
+ //
+ // Receive error, free the packet save the error
+ //
+ EslSocketPacketFree ( pPacket, DEBUG_RX );
+ if ( !EFI_ERROR ( pSocket->RxError )) {
+ pSocket->RxError = Status;
+ }
+
+ //
+ // Update the port state
+ //
+ if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) {
+ EslTcpPortCloseRxDone4 ( pPort );
+ }
+ else {
+ if ( EFI_ERROR ( Status )) {
+ pPort->State = PORT_STATE_RX_ERROR;
+ }
+ }
+ }
+
+ DBG_EXIT ( );
+}
+
+
+/**
+ Start a receive operation
+
+ @param [in] pPort Address of the DT_PORT structure.
+
+ **/
+VOID
+EslTcpRxStart4 (
+ IN DT_PORT * pPort
+ )
+{
+ size_t LengthInBytes;
+ DT_PACKET * pPacket;
+ DT_SOCKET * pSocket;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_TCP4_PROTOCOL * pTcp4Protocol;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Determine if a receive is already pending
+ //
+ Status = EFI_SUCCESS;
+ pPacket = NULL;
+ pSocket = pPort->pSocket;
+ pTcp4 = &pPort->Context.Tcp4;
+ if ( !EFI_ERROR ( pPort->pSocket->RxError )) {
+ if (( NULL == pTcp4->pReceivePending )
+ && ( PORT_STATE_CLOSE_STARTED > pPort->State )) {
+ //
+ // Determine if there are any free packets
+ //
+ pPacket = pSocket->pRxFree;
+ LengthInBytes = sizeof ( pPacket->Op.Tcp4Rx.Buffer );
+ if ( NULL != pPacket ) {
+ //
+ // Remove this packet from the free list
+ //
+ pSocket->pRxFree = pPacket->pNext;
+ DEBUG (( DEBUG_RX,
+ "0x%08x: Port removed packet 0x%08x from free list\r\n",
+ pPort,
+ pPacket ));
+ }
+ else {
+ //
+ // Allocate a packet structure
+ //
+ Status = EslSocketPacketAllocate ( &pPacket,
+ sizeof ( pPacket->Op.Tcp4Rx ),
+ DEBUG_RX );
+ if ( EFI_ERROR ( Status )) {
+ pPacket = NULL;
+ DEBUG (( DEBUG_ERROR | DEBUG_RX,
+ "0x%08x: Port failed to allocate RX packet, Status: %r\r\n",
+ pPort,
+ Status ));
+ }
+ }
+
+ //
+ // Determine if a packet is available
+ //
+ if ( NULL != pPacket ) {
+ //
+ // Initialize the buffer for receive
+ //
+ pTcp4->RxToken.Packet.RxData = &pPacket->Op.Tcp4Rx.RxData;
+ pPacket->Op.Tcp4Rx.RxData.DataLength = (UINT32) LengthInBytes;
+ pPacket->Op.Tcp4Rx.RxData.FragmentCount = 1;
+ pPacket->Op.Tcp4Rx.RxData.FragmentTable [0].FragmentLength = (UINT32) LengthInBytes;
+ pPacket->Op.Tcp4Rx.RxData.FragmentTable [0].FragmentBuffer = &pPacket->Op.Tcp4Rx.Buffer [0];
+ pTcp4->pReceivePending = pPacket;
+
+ //
+ // Start the receive on the packet
+ //
+ pTcp4Protocol = pTcp4->pProtocol;
+ Status = pTcp4Protocol->Receive ( pTcp4Protocol,
+ &pTcp4->RxToken );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_RX | DEBUG_INFO,
+ "0x%08x: Packet receive pending on port 0x%08x\r\n",
+ pPacket,
+ pPort ));
+ }
+ else {
+ DEBUG (( DEBUG_RX | DEBUG_INFO,
+ "ERROR - Failed to post a receive on port 0x%08x, Status: %r\r\n",
+ pPort,
+ Status ));
+ pTcp4->pReceivePending = NULL;
+ if ( !EFI_ERROR ( pSocket->RxError )) {
+ //
+ // Save the error status
+ //
+ pSocket->RxError = Status;
+ }
+ }
+ }
+ }
+ }
+
+ DBG_EXIT ( );
+}
+
+
+/**
+ Shutdown the TCP4 service.
+
+ This routine undoes the work performed by ::TcpInitialize4.
+
+ @param [in] pService DT_SERVICE structure address
+
+**/
+VOID
+EFIAPI
+EslTcpShutdown4 (
+ IN DT_SERVICE * pService
+ )
+{
+ DT_LAYER * pLayer;
+ DT_PORT * pPort;
+ DT_SERVICE * pPreviousService;
+
+ DBG_ENTER ( );
+
+ //
+ // Verify the socket layer synchronization
+ //
+ VERIFY_TPL ( TPL_SOCKETS );
+
+ //
+ // Walk the list of ports
+ //
+ do {
+ pPort = pService->pPortList;
+ if ( NULL != pPort ) {
+ //
+ // Remove the port from the port list
+ //
+ pService->pPortList = pPort->pLinkService;
+
+ //
+ // Close the port
+ // TODO: Fix this
+ //
+// pPort->pfnClosePort ( pPort, DEBUG_LISTEN | DEBUG_CONNECTION );
+ }
+ } while ( NULL != pPort );
+
+ //
+ // Remove the service from the service list
+ //
+ pLayer = &mEslLayer;
+ pPreviousService = pLayer->pTcp4List;
+ if ( pService == pPreviousService ) {
+ //
+ // Remove the service from the beginning of the list
+ //
+ pLayer->pTcp4List = pService->pNext;
+ }
+ else {
+ //
+ // Remove the service from the middle of the list
+ //
+ while ( NULL != pPreviousService ) {
+ if ( pService == pPreviousService->pNext ) {
+ pPreviousService->pNext = pService->pNext;
+ break;
+ }
+ }
+ }
+
+ DBG_EXIT ( );
+}
+
+
+/**
+ Determine if the socket is configured.
+
+
+ @param [in] pSocket Address of a DT_SOCKET structure
+
+ @retval EFI_SUCCESS - The port is connected
+ @retval EFI_NOT_STARTED - The port is not connected
+
+ **/
+ EFI_STATUS
+ EslTcpSocketIsConfigured4 (
+ IN DT_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 is called by the socket layer API to buffer
+ data for transmission. When necessary, this routine will
+ start the transmit engine that performs the data transmission
+ on the network connection.
+
+ The transmit engine uses 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.
+
+ 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 a DT_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.
+
+ @retval EFI_SUCCESS - Socket data successfully buffered
+
+ **/
+EFI_STATUS
+EslTcpTxBuffer4 (
+ IN DT_SOCKET * pSocket,
+ IN int Flags,
+ IN size_t BufferLength,
+ IN CONST UINT8 * pBuffer,
+ OUT size_t * pDataLength
+ )
+{
+ BOOLEAN bUrgent;
+ DT_PACKET * pPacket;
+ DT_PACKET * pPreviousPacket;
+ DT_PACKET ** ppPacket;
+ DT_PACKET ** ppQueueHead;
+ DT_PACKET ** ppQueueTail;
+ DT_PORT * pPort;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_TCP4_IO_TOKEN * pToken;
+ size_t * pTxBytes;
+ EFI_TCP4_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
+ //
+ pTcp4 = &pPort->Context.Tcp4;
+ bUrgent = (BOOLEAN)( 0 != ( Flags & MSG_OOB ));
+ if ( bUrgent ) {
+ ppQueueHead = &pSocket->pTxOobPacketListHead;
+ ppQueueTail = &pSocket->pTxOobPacketListTail;
+ ppPacket = &pTcp4->pTxOobPacket;
+ pToken = &pTcp4->TxOobToken;
+ pTxBytes = &pSocket->TxOobBytes;
+ }
+ else {
+ ppQueueHead = &pSocket->pTxPacketListHead;
+ ppQueueTail = &pSocket->pTxPacketListTail;
+ ppPacket = &pTcp4->pTxPacket;
+ pToken = &pTcp4->TxToken;
+ pTxBytes = &pSocket->TxBytes;
+ }
+
+ //
+ // Verify that there is enough room to buffer another
+ // transmit operation
+ //
+ if ( pSocket->MaxTxBuf > *pTxBytes ) {
+ //
+ // Attempt to allocate the packet
+ //
+ Status = EslSocketPacketAllocate ( &pPacket,
+ sizeof ( pPacket->Op.Tcp4Tx )
+ - sizeof ( pPacket->Op.Tcp4Tx.Buffer )
+ + BufferLength,
+ DEBUG_TX );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Initialize the transmit operation
+ //
+ pTxData = &pPacket->Op.Tcp4Tx.TxData;
+ pTxData->Push = TRUE;
+ pTxData->Urgent = bUrgent;
+ pTxData->DataLength = (UINT32) BufferLength;
+ pTxData->FragmentCount = 1;
+ pTxData->FragmentTable[0].FragmentLength = (UINT32) BufferLength;
+ pTxData->FragmentTable[0].FragmentBuffer = &pPacket->Op.Tcp4Tx.Buffer[0];
+
+ //
+ // Copy the data into the buffer
+ //
+ CopyMem ( &pPacket->Op.Tcp4Tx.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,
+ bUrgent ? L"urgent" : L"normal" ));
+
+ //
+ // Account for the buffered data
+ //
+ *pTxBytes += BufferLength;
+ *pDataLength = BufferLength;
+
+ //
+ // Start the transmit engine if it is idle
+ //
+ if ( NULL == *ppPacket ) {
+ EslTcpTxStart4 ( pSocket->pPortList,
+ pToken,
+ ppQueueHead,
+ ppQueueTail,
+ ppPacket );
+ }
+ }
+ else {
+ //
+ // Previous transmit error
+ // Stop transmission
+ //
+ Status = pSocket->TxError;
+ pSocket->errno = EIO;
+
+ //
+ // Free the packet
+ //
+ EslSocketPacketFree ( pPacket, DEBUG_TX );
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ else {
+ //
+ // Packet allocation failed
+ //
+ pSocket->errno = ENOMEM;
+ }
+ }
+ else {
+ //
+ // Not enough buffer space available
+ //
+ pSocket->errno = EAGAIN;
+ Status = EFI_NOT_READY;
+ }
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Process the normal data transmit completion
+
+ @param Event The normal transmit completion event
+
+ @param pPort The DT_PORT structure address
+
+**/
+VOID
+EslTcpTxComplete4 (
+ IN EFI_EVENT Event,
+ IN DT_PORT * pPort
+ )
+{
+ UINT32 LengthInBytes;
+ DT_PACKET * pCurrentPacket;
+ DT_PACKET * pNextPacket;
+ DT_PACKET * pPacket;
+ DT_SOCKET * pSocket;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Locate the active transmit packet
+ //
+ pSocket = pPort->pSocket;
+ pTcp4 = &pPort->Context.Tcp4;
+ pPacket = pTcp4->pTxPacket;
+
+ //
+ // Mark this packet as complete
+ //
+ pTcp4->pTxPacket = NULL;
+ LengthInBytes = pPacket->Op.Tcp4Tx.TxData.DataLength;
+ pSocket->TxBytes -= LengthInBytes;
+
+ //
+ // Save any transmit error
+ //
+ Status = pTcp4->TxToken.CompletionToken.Status;
+ if ( EFI_ERROR ( Status )) {
+ if ( !EFI_ERROR ( pSocket->TxError )) {
+ pSocket->TxError = Status;
+ }
+ DEBUG (( DEBUG_TX | DEBUG_INFO,
+ "ERROR - Transmit failure for packet 0x%08x, Status: %r\r\n",
+ pPacket,
+ Status ));
+
+ //
+ // Empty the normal transmit list
+ //
+ pCurrentPacket = pPacket;
+ pNextPacket = pSocket->pTxPacketListHead;
+ while ( NULL != pNextPacket ) {
+ pPacket = pNextPacket;
+ pNextPacket = pPacket->pNext;
+ EslSocketPacketFree ( pPacket, DEBUG_TX );
+ }
+ pSocket->pTxPacketListHead = NULL;
+ pSocket->pTxPacketListTail = NULL;
+ pPacket = pCurrentPacket;
+ }
+ else
+ {
+ DEBUG (( DEBUG_TX | DEBUG_INFO,
+ "0x%08x: Packet transmitted %d bytes successfully\r\n",
+ pPacket,
+ LengthInBytes ));
+
+ //
+ // Verify the transmit engine is still running
+ //
+ if ( !pPort->bCloseNow ) {
+ //
+ // Start the next packet transmission
+ //
+ EslTcpTxStart4 ( pPort,
+ &pTcp4->TxToken,
+ &pSocket->pTxPacketListHead,
+ &pSocket->pTxPacketListTail,
+ &pTcp4->pTxPacket );
+ }
+ }
+
+ //
+ // Release this packet
+ //
+ EslSocketPacketFree ( pPacket, DEBUG_TX );
+
+ //
+ // Finish the close operation if necessary
+ //
+ if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) {
+ //
+ // Indicate that the transmit is complete
+ //
+ EslTcpPortCloseTxDone4 ( pPort );
+ }
+ DBG_EXIT ( );
+}
+
+
+/**
+ Process the urgent data transmit completion
+
+ @param Event The urgent transmit completion event
+
+ @param pPort The DT_PORT structure address
+
+**/
+VOID
+EslTcpTxOobComplete4 (
+ IN EFI_EVENT Event,
+ IN DT_PORT * pPort
+ )
+{
+ UINT32 LengthInBytes;
+ DT_PACKET * pCurrentPacket;
+ DT_PACKET * pNextPacket;
+ DT_PACKET * pPacket;
+ DT_SOCKET * pSocket;
+ DT_TCP4_CONTEXT * pTcp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Locate the active transmit packet
+ //
+ pSocket = pPort->pSocket;
+ pTcp4 = &pPort->Context.Tcp4;
+ pPacket = pTcp4->pTxOobPacket;
+
+ //
+ // Mark this packet as complete
+ //
+ pTcp4->pTxOobPacket = NULL;
+ LengthInBytes = pPacket->Op.Tcp4Tx.TxData.DataLength;
+ pSocket->TxOobBytes -= LengthInBytes;
+
+ //
+ // Save any transmit error
+ //
+ Status = pTcp4->TxOobToken.CompletionToken.Status;
+ if ( EFI_ERROR ( Status )) {
+ if ( !EFI_ERROR ( Status )) {
+ pSocket->TxError = Status;
+ }
+ DEBUG (( DEBUG_TX | DEBUG_INFO,
+ "ERROR - Transmit failure for urgent packet 0x%08x, Status: %r\r\n",
+ pPacket,
+ Status ));
+
+
+ //
+ // Empty the OOB transmit list
+ //
+ pCurrentPacket = pPacket;
+ pNextPacket = pSocket->pTxOobPacketListHead;
+ while ( NULL != pNextPacket ) {
+ pPacket = pNextPacket;
+ pNextPacket = pPacket->pNext;
+ EslSocketPacketFree ( pPacket, DEBUG_TX );
+ }
+ pSocket->pTxOobPacketListHead = NULL;
+ pSocket->pTxOobPacketListTail = NULL;
+ pPacket = pCurrentPacket;
+ }
+ else
+ {
+ DEBUG (( DEBUG_TX | DEBUG_INFO,
+ "0x%08x: Urgent packet transmitted %d bytes successfully\r\n",
+ pPacket,
+ LengthInBytes ));
+
+ //
+ // Verify the transmit engine is still running
+ //
+ if ( !pPort->bCloseNow ) {
+ //
+ // Start the next packet transmission
+ //
+ EslTcpTxStart4 ( pPort,
+ &pTcp4->TxOobToken,
+ &pSocket->pTxOobPacketListHead,
+ &pSocket->pTxOobPacketListTail,
+ &pTcp4->pTxOobPacket );
+ }
+ }
+
+ //
+ // Release this packet
+ //
+ EslSocketPacketFree ( pPacket, DEBUG_TX );
+
+ //
+ // Finish the close operation if necessary
+ //
+ if ( PORT_STATE_CLOSE_STARTED <= pPort->State ) {
+ //
+ // Indicate that the transmit is complete
+ //
+ EslTcpPortCloseTxDone4 ( pPort );
+ }
+ DBG_EXIT ( );
+}
+
+
+/**
+ Transmit data using a network connection.
+
+
+ @param [in] pPort Address of a DT_PORT structure
+ @param [in] pToken Address of either the OOB or normal transmit token
+ @param [in] ppQueueHead Transmit queue head address
+ @param [in] ppQueueTail Transmit queue tail address
+ @param [in] ppPacket Active transmit packet address
+
+ **/
+VOID
+EslTcpTxStart4 (
+ IN DT_PORT * pPort,
+ IN EFI_TCP4_IO_TOKEN * pToken,
+ IN DT_PACKET ** ppQueueHead,
+ IN DT_PACKET ** ppQueueTail,
+ IN DT_PACKET ** ppPacket
+ )
+{
+ DT_PACKET * pNextPacket;
+ DT_PACKET * pPacket;
+ DT_SOCKET * pSocket;
+ EFI_TCP4_PROTOCOL * pTcp4Protocol;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Get the packet from the queue head
+ //
+ pPacket = *ppQueueHead;
+ if ( NULL != pPacket ) {
+ //
+ // Remove the packet from the queue
+ //
+ pNextPacket = pPacket->pNext;
+ *ppQueueHead = pNextPacket;
+ if ( NULL == pNextPacket ) {
+ *ppQueueTail = NULL;
+ }
+
+ //
+ // Set the packet as active
+ //
+ *ppPacket = pPacket;
+
+ //
+ // Start the transmit operation
+ //
+ pTcp4Protocol = pPort->Context.Tcp4.pProtocol;
+ pToken->Packet.TxData = &pPacket->Op.Tcp4Tx.TxData;
+ Status = pTcp4Protocol->Transmit ( pTcp4Protocol, pToken );
+ if ( EFI_ERROR ( Status )) {
+ pSocket = pPort->pSocket;
+ if ( EFI_SUCCESS == pSocket->TxError ) {
+ pSocket->TxError = Status;
+ }
+ }
+ }
+
+ DBG_EXIT ( );
+}