diff options
Diffstat (limited to 'StdLib/EfiSocketLib')
-rw-r--r-- | StdLib/EfiSocketLib/EfiSocketLib.inf | 56 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Init.c | 87 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Service.c | 529 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Socket.c | 3091 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Socket.h | 1337 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Tcp4.c | 3415 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Udp4.c | 2408 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/UseEfiSocketLib.c | 242 |
8 files changed, 11165 insertions, 0 deletions
diff --git a/StdLib/EfiSocketLib/EfiSocketLib.inf b/StdLib/EfiSocketLib/EfiSocketLib.inf new file mode 100644 index 0000000000..9a30193f84 --- /dev/null +++ b/StdLib/EfiSocketLib/EfiSocketLib.inf @@ -0,0 +1,56 @@ +#/** @file
+# Component description file for the EFI socket library.
+#
+# This module implements 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.
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = EfiSocketLib
+ FILE_GUID = C33E0B7C-9D0F-41df-BDFD-08F5E4C39EE8
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = EfiSocketLib
+ CONSTRUCTOR = EslConstructor
+ DESTRUCTOR = EslDestructor
+
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ Init.c
+ Service.c
+ Socket.c
+ Tcp4.c
+ Udp4.c
+ UseEfiSocketLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ StdLib/StdLib.dec
+# SocketPkg/SocketPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ UefiBootServicesTableLib
+ UefiLib
+
+[Protocols]
+ gEfiTcp4ProtocolGuid
+ gEfiTcp4ServiceBindingProtocolGuid
+ gEfiUdp4ProtocolGuid
+ gEfiUdp4ServiceBindingProtocolGuid
+ gEfiSocketProtocolGuid
+ gEfiSocketServiceBindingProtocolGuid
diff --git a/StdLib/EfiSocketLib/Init.c b/StdLib/EfiSocketLib/Init.c new file mode 100644 index 0000000000..7a1b89da87 --- /dev/null +++ b/StdLib/EfiSocketLib/Init.c @@ -0,0 +1,87 @@ +/** @file
+ Implement the constructor and destructor for the EFI socket library
+
+ 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>
+
+
+/**
+ EFI Socket Library Constructor
+
+ @retval EFI_SUCCESS The initialization was successful
+
+ **/
+EFI_STATUS
+EFIAPI
+EslConstructor (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Call the image dependent constructor if available
+ //
+ if ( NULL != mpfnEslConstructor ) {
+ Status = mpfnEslConstructor ( );
+ }
+
+ //
+ // Return the constructor status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ EFI Socket Library Destructor
+
+ @retval EFI_SUCCESS The shutdown was successful
+
+ **/
+EFI_STATUS
+EFIAPI
+EslDestructor (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Call the image dependent destructor if available
+ //
+ if ( NULL != mpfnEslDestructor ) {
+ Status = mpfnEslDestructor ( );
+ }
+
+ //
+ // Return the constructor status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
diff --git a/StdLib/EfiSocketLib/Service.c b/StdLib/EfiSocketLib/Service.c new file mode 100644 index 0000000000..49c8884612 --- /dev/null +++ b/StdLib/EfiSocketLib/Service.c @@ -0,0 +1,529 @@ +/** @file
+ Connect to and disconnect from the various network layers
+
+ 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"
+
+EFI_TCP4_PROTOCOL * mpEfiTcpClose4 [ 1024 ];
+
+
+/**
+ Connect to the network service bindings
+
+ Walk the network service protocols on the controller handle and
+ locate any that are not in use. Create service structures to
+ manage the service binding for the socket driver.
+
+ @param [in] BindingHandle Handle for protocol binding.
+ @param [in] Controller Handle of device to work with.
+
+ @retval EFI_SUCCESS This driver is added to Controller.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+EslServiceConnect (
+ IN EFI_HANDLE BindingHandle,
+ IN EFI_HANDLE Controller
+ )
+{
+ BOOLEAN bInUse;
+ UINTN LengthInBytes;
+ CONST DT_SOCKET_BINDING * pEnd;
+ VOID * pJunk;
+ VOID * pInterface;
+ DT_SERVICE * pService;
+ CONST DT_SOCKET_BINDING * pSocketBinding;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume the list is empty
+ //
+ Status = EFI_UNSUPPORTED;
+ bInUse = FALSE;
+
+ //
+ // Walk the list of network connection points
+ //
+ pSocketBinding = &cEslSocketBinding[0];
+ pEnd = &pSocketBinding[ cEslSocketBindingEntries ];
+ while ( pEnd > pSocketBinding ) {
+ //
+ // Determine if the controller supports the network protocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ pSocketBinding->pNetworkBinding,
+ &pInterface,
+ BindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Determine if the socket layer is already connected
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ (EFI_GUID *)pSocketBinding->pTagGuid,
+ &pJunk,
+ BindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if ( EFI_UNSUPPORTED == Status ) {
+ //
+ // Allocate a service structure since the tag is not present
+ //
+ LengthInBytes = sizeof ( *pService );
+ Status = gBS->AllocatePool (
+ EfiRuntimeServicesData,
+ LengthInBytes,
+ (VOID **) &pService
+ );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_POOL | DEBUG_INIT,
+ "0x%08x: Allocate pService, %d bytes\r\n",
+ pService,
+ LengthInBytes ));
+
+ //
+ // Set the structure signature and service binding
+ //
+ ZeroMem ( pService, LengthInBytes );
+ pService->Signature = SERVICE_SIGNATURE;
+ pService->pSocketBinding = pSocketBinding;
+ pService->Controller = Controller;
+ pService->pInterface = pInterface;
+
+ //
+ // Mark the controller in use
+ //
+ if ( !bInUse ) {
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiCallerIdGuid,
+ NULL,
+ NULL
+ );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+ "Installed: gEfiCallerIdGuid on 0x%08x\r\n",
+ Controller ));
+ bInUse = TRUE;
+ }
+ else {
+ if ( EFI_INVALID_PARAMETER == Status ) {
+ Status = EFI_SUCCESS;
+ }
+ }
+ }
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Mark the network service protocol in use
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ pSocketBinding->pTagGuid,
+ pService,
+ NULL
+ );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+ "Installed: %s TagGuid on 0x%08x\r\n",
+ pSocketBinding->pName,
+ Controller ));
+
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Initialize the service
+ //
+ Status = pSocketBinding->pfnInitialize ( pService );
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+
+ //
+ // Determine if the initialization was successful
+ //
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT,
+ "ERROR - Failed to initialize service %s on 0x%08x, Status: %r\r\n",
+ pSocketBinding->pName,
+ Controller,
+ Status ));
+
+ //
+ // Free the network service binding if necessary
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Controller,
+ pSocketBinding->pTagGuid,
+ pService,
+ NULL );
+ DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+ "Removed: %s TagGuid from 0x%08x\r\n",
+ pSocketBinding->pName,
+ Controller ));
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT,
+ "ERROR - Failed to install %s TagGuid on 0x%08x, Status: %r\r\n",
+ pSocketBinding->pName,
+ Controller,
+ Status ));
+ }
+
+ if ( EFI_ERROR ( Status )) {
+ //
+ // The controller is no longer in use
+ //
+ if ( bInUse ) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Controller,
+ &gEfiCallerIdGuid,
+ NULL,
+ NULL );
+ DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+ "Removed: gEfiCallerIdGuid from 0x%08x\r\n",
+ Controller ));
+ }
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_INIT,
+ "ERROR - Failed to install gEfiCallerIdGuid on 0x%08x, Status: %r\r\n",
+ Controller,
+ Status ));
+ }
+
+ //
+ // Release the service if necessary
+ //
+ if ( EFI_ERROR ( Status )) {
+ gBS->FreePool ( pService );
+ DEBUG (( DEBUG_POOL | DEBUG_INIT,
+ "0x%08x: Free pService, %d bytes\r\n",
+ pService,
+ sizeof ( *pService )));
+ pService = NULL;
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_INIT,
+ "ERROR - Failed service allocation, Status: %r\r\n",
+ Status ));
+ }
+ }
+ }
+
+ //
+ // Set the next network protocol
+ //
+ pSocketBinding += 1;
+ }
+
+ //
+ // Display the driver start status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Shutdown the network connections to this controller by removing
+ NetworkInterfaceIdentifier protocol and closing the DevicePath
+ and PciIo protocols on Controller.
+
+ @param [in] BindingHandle Handle for protocol binding.
+ @param [in] Controller Handle of device to stop driver on.
+
+ @retval EFI_SUCCESS This driver is removed Controller.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+ @retval other This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+EslServiceDisconnect (
+ IN EFI_HANDLE BindingHandle,
+ IN EFI_HANDLE Controller
+ )
+{
+ CONST DT_SOCKET_BINDING * pEnd;
+ DT_SERVICE * pService;
+ CONST DT_SOCKET_BINDING * pSocketBinding;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Walk the list of network connection points in reverse order
+ //
+ pEnd = &cEslSocketBinding[0];
+ pSocketBinding = &pEnd[ cEslSocketBindingEntries ];
+ while ( pEnd < pSocketBinding ) {
+ //
+ // Set the next network protocol
+ //
+ pSocketBinding -= 1;
+
+ //
+ // Determine if the driver connected
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ (EFI_GUID *)pSocketBinding->pTagGuid,
+ (VOID **)&pService,
+ BindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if ( !EFI_ERROR ( Status )) {
+
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Shutdown the service
+ //
+ pSocketBinding->pfnShutdown ( pService );
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+
+ //
+ // Break the driver connection
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ Controller,
+ pSocketBinding->pTagGuid,
+ pService,
+ NULL );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_POOL | DEBUG_INIT,
+ "Removed: %s TagGuid from 0x%08x\r\n",
+ pSocketBinding->pName,
+ Controller ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT,
+ "ERROR - Failed to removed %s TagGuid from 0x%08x, Status: %r\r\n",
+ pSocketBinding->pName,
+ Controller,
+ Status ));
+ }
+
+ //
+ // Free the service structure
+ //
+ Status = gBS->FreePool ( pService );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_POOL | DEBUG_INIT,
+ "0x%08x: Free pService, %d bytes\r\n",
+ pService,
+ sizeof ( *pService )));
+ }
+ else {
+ DEBUG (( DEBUG_POOL | DEBUG_INIT,
+ "ERROR - Failed to free pService 0x%08x, Status: %r\r\n",
+ pService,
+ Status ));
+ }
+ pService = NULL;
+ }
+ }
+
+ //
+ // The controller is no longer in use
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Controller,
+ &gEfiCallerIdGuid,
+ NULL,
+ NULL );
+ DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+ "Removed: gEfiCallerIdGuid from 0x%08x\r\n",
+ Controller ));
+
+ //
+ // The driver is disconnected from the network controller
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Display the driver start status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+
+/**
+Install the socket service
+
+@param [in] pImageHandle Address of the image handle
+
+@retval EFI_SUCCESS Service installed successfully
+**/
+EFI_STATUS
+EFIAPI
+EslServiceInstall (
+ IN EFI_HANDLE * pImageHandle
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Install the socket service binding protocol
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ pImageHandle,
+ &gEfiSocketServiceBindingProtocolGuid,
+ &mEslLayer.ServiceBinding,
+ NULL
+ );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+ "Installed: gEfiSocketServiceBindingProtocolGuid on 0x%08x\r\n",
+ *pImageHandle ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT,
+ "ERROR - InstallMultipleProtocolInterfaces failed, Status: %r\r\n",
+ Status ));
+ }
+
+ //
+ // Return the operation status
+ //
+ return Status;
+}
+
+
+/**
+Initialize the service layer
+
+@param [in] ImageHandle Handle for the image.
+
+**/
+VOID
+EFIAPI
+EslServiceLoad (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ DT_LAYER * pLayer;
+
+ //
+ // Save the image handle
+ //
+ pLayer = &mEslLayer;
+ pLayer->Signature = LAYER_SIGNATURE;
+ pLayer->ImageHandle = ImageHandle;
+
+ //
+ // Initialize the TCP4 close
+ //
+ pLayer->TcpCloseMax4 = DIM ( mpEfiTcpClose4 );
+ pLayer->ppTcpClose4 = mpEfiTcpClose4;
+
+ //
+ // Connect the service binding protocol to the image handle
+ //
+ pLayer->ServiceBinding.CreateChild = EslSocketCreateChild;
+ pLayer->ServiceBinding.DestroyChild = EslSocketDestroyChild;
+}
+
+
+/**
+Uninstall the socket service
+
+@param [in] ImageHandle Handle for the image.
+
+@retval EFI_SUCCESS Service installed successfully
+**/
+EFI_STATUS
+EFIAPI
+EslServiceUninstall (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Install the socket service binding protocol
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEfiSocketServiceBindingProtocolGuid,
+ &mEslLayer.ServiceBinding,
+ NULL
+ );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_POOL | DEBUG_INIT,
+ "Removed: gEfiSocketServiceBindingProtocolGuid from 0x%08x\r\n",
+ ImageHandle ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT,
+ "ERROR - Failed to remove gEfiSocketServiceBindingProtocolGuid from 0x%08x, Status: %r\r\n",
+ ImageHandle,
+ Status ));
+ }
+
+ //
+ // Return the operation status
+ //
+ return Status;
+}
+
+
+/**
+ Shutdown the service layer
+
+**/
+VOID
+EFIAPI
+EslServiceUnload (
+ VOID
+ )
+{
+ DT_LAYER * pLayer;
+
+ //
+ // Undo the work by ServiceLoad
+ //
+ pLayer = &mEslLayer;
+ pLayer->ImageHandle = NULL;
+ pLayer->ServiceBinding.CreateChild = NULL;
+ pLayer->ServiceBinding.DestroyChild = NULL;
+}
diff --git a/StdLib/EfiSocketLib/Socket.c b/StdLib/EfiSocketLib/Socket.c new file mode 100644 index 0000000000..bad888cdf5 --- /dev/null +++ b/StdLib/EfiSocketLib/Socket.c @@ -0,0 +1,3091 @@ +/** @file
+ Implement the socket support for the socket layer.
+
+ Socket States:
+ * Bound - pSocket->PortList is not NULL
+ * Listen - AcceptWait event is not NULL
+
+ 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"
+
+
+/**
+ Socket driver connection points
+
+ List the network stack connection points for the socket driver.
+**/
+CONST DT_SOCKET_BINDING cEslSocketBinding [] = {
+ { L"Tcp4",
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ &mEslTcp4ServiceGuid,
+ EslTcpInitialize4,
+ EslTcpShutdown4 },
+ { L"Udp4",
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ &mEslUdp4ServiceGuid,
+ EslUdpInitialize4,
+ EslUdpShutdown4 }
+};
+
+CONST UINTN cEslSocketBindingEntries = DIM ( cEslSocketBinding );
+
+DT_LAYER mEslLayer;
+
+
+/**
+ Initialize an endpoint for network communication.
+
+ The ::Socket routine initializes the communication endpoint by providing
+ the support for the socket library function ::socket. The
+ <a href="http://www.linuxhowtos.org/manpages/2/socket.htm">Linux</a>,
+ <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html">POSIX</a>
+ and <a href="http://msdn.microsoft.com/en-us/library/ms740506(v=VS.85).aspx">Windows</a>
+ documentation for the socket routine are available online for reference.
+
+ @param [in] pSocketProtocol Address of the socket protocol structure.
+ @param [in] domain Select the family of protocols for the client or server
+ application.
+
+ @param [in] type Specifies how to make the network connection. The following values
+ are supported:
+ <ul>
+ <li>
+ SOCK_STREAM - Connect to TCP, provides a byte stream
+ that is manipluated by read, recv, send and write.
+ </li>
+ <li>
+ SOCK_SEQPACKET - Connect to TCP, provides sequenced packet stream
+ that is manipulated by read, recv, send and write.
+ </li>
+ <li>
+ SOCK_DGRAM - Connect to UDP, provides a datagram service that is
+ manipulated by recvfrom and sendto.
+ </li>
+ </ul>
+
+ @param [in] protocol Specifies the lower layer protocol to use. The following
+ values are supported:
+ <ul>
+ <li>IPPROTO_TCP</li> - This value must be combined with SOCK_STREAM.</li>
+ <li>IPPROTO_UDP</li> - This value must be combined with SOCK_DGRAM.</li>
+ </ul>
+
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS - Socket successfully created
+ @retval EFI_INVALID_PARAMETER - Invalid domain value, errno = EAFNOSUPPORT
+ @retval EFI_INVALID_PARAMETER - Invalid type value, errno = EINVAL
+ @retval EFI_INVALID_PARAMETER - Invalid protocol value, errno = EINVAL
+
+ **/
+EFI_STATUS
+EslSocket (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN int domain,
+ IN int type,
+ IN int protocol,
+ IN int * pErrno
+ )
+{
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ int errno;
+
+ DBG_ENTER ( );
+
+ //
+ // Locate the socket
+ //
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+
+ //
+ // Set the default domain if necessary
+ //
+ if ( AF_UNSPEC == domain ) {
+ domain = AF_INET;
+ }
+
+ //
+ // Assume success
+ //
+ errno = 0;
+ Status = EFI_SUCCESS;
+
+ //
+ // Use break instead of goto
+ //
+ for ( ; ; ) {
+ //
+ // Validate the domain value
+ //
+ if (( AF_INET != domain )
+ && ( AF_LOCAL != domain ))
+ {
+ DEBUG (( DEBUG_ERROR | DEBUG_SOCKET,
+ "ERROR - Invalid domain value" ));
+ Status = EFI_INVALID_PARAMETER;
+ errno = EAFNOSUPPORT;
+ break;
+ }
+
+ //
+ // Set the default type if necessary
+ //
+ if ( 0 == type ) {
+ type = SOCK_STREAM;
+ }
+
+ //
+ // Validate the type value
+ //
+ if (( SOCK_STREAM == type )
+ || ( SOCK_SEQPACKET == type )) {
+ //
+ // Set the default protocol if necessary
+ //
+ if ( 0 == protocol ) {
+ protocol = IPPROTO_TCP;
+ }
+ }
+ else if ( SOCK_DGRAM == type ) {
+ //
+ // Set the default protocol if necessary
+ //
+ if ( 0 == protocol ) {
+ protocol = IPPROTO_UDP;
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_SOCKET,
+ "ERROR - Invalid type value" ));
+ Status = EFI_INVALID_PARAMETER;
+ errno = EINVAL;
+ break;
+ }
+
+ //
+ // Validate the protocol value
+ //
+ if (( IPPROTO_TCP != protocol )
+ && ( IPPROTO_UDP != protocol )) {
+ DEBUG (( DEBUG_ERROR | DEBUG_SOCKET,
+ "ERROR - Invalid protocol value" ));
+ Status = EFI_INVALID_PARAMETER;
+ errno = EINVAL;
+ break;
+ }
+
+ //
+ // Save the socket attributes
+ //
+ pSocket->Domain = domain;
+ pSocket->Type = type;
+ pSocket->Protocol = protocol;
+
+ //
+ // Done
+ //
+ break;
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ *pErrno = errno;
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ 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] pSocketProtocol Address of the socket protocol 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.
+
+ @param [out] ppSocketProtocol Address of a buffer to receive the socket protocol
+ instance associated with the new socket.
+
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS New connection successfully created
+ @retval EFI_NOT_READY No connection is available
+
+ **/
+EFI_STATUS
+EslSocketAccept (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN struct sockaddr * pSockAddr,
+ IN OUT socklen_t * pSockAddrLength,
+ IN EFI_SOCKET_PROTOCOL ** ppSocketProtocol,
+ IN int * pErrno
+ )
+{
+ DT_SOCKET * pNewSocket;
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Validate the socket
+ //
+ pSocket = NULL;
+ pNewSocket = NULL;
+ if ( NULL != pSocketProtocol ) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+
+ //
+ // Validate the sockaddr
+ //
+ if (( NULL != pSockAddr )
+ && ( NULL == pSockAddrLength )) {
+ DEBUG (( DEBUG_ACCEPT,
+ "ERROR - pSockAddr is NULL!\r\n" ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EFAULT;
+ }
+ else {
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Verify that the socket is in the listen state
+ //
+ if ( SOCKET_STATE_LISTENING != pSocket->State ) {
+ DEBUG (( DEBUG_ACCEPT,
+ "ERROR - Socket is not listening!\r\n" ));
+ Status = EFI_NOT_STARTED;
+ pSocket->errno = EOPNOTSUPP;
+ }
+ else {
+ //
+ // Determine if a socket is available
+ //
+ if ( 0 == pSocket->FifoDepth ) {
+ //
+ // No connections available
+ // Determine if any ports are available
+ //
+ if ( NULL == pSocket->pPortList ) {
+ //
+ // No ports available
+ //
+ Status = EFI_DEVICE_ERROR;
+ pSocket->errno = EINVAL;
+
+ //
+ // Update the socket state
+ //
+ pSocket->State = SOCKET_STATE_NO_PORTS;
+ }
+ else {
+ //
+ // Ports are available
+ // No connection requests at this time
+ //
+ Status = EFI_NOT_READY;
+ pSocket->errno = EAGAIN;
+ }
+ }
+ else {
+
+ //
+ // Get the remote network address
+ //
+ pNewSocket = pSocket->pFifoHead;
+ ASSERT ( NULL != pNewSocket );
+ switch ( pSocket->Domain ) {
+ default:
+ DEBUG (( DEBUG_ACCEPT,
+ "ERROR - Invalid socket address family: %d\r\n",
+ pSocket->Domain ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_ACCEPT,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ Status = EslTcpAccept4 ( pNewSocket,
+ pSockAddr,
+ pSockAddrLength );
+ break;
+
+ /*
+ case SOCK_DGRAM:
+ Status = UdpAccept4 ( pSocket );
+ break;
+ */
+ }
+ break;
+ }
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Remove the new socket from the list
+ //
+ pSocket->pFifoHead = pNewSocket->pNextConnection;
+ if ( NULL == pSocket->pFifoHead ) {
+ pSocket->pFifoTail = NULL;
+ }
+
+ //
+ // Account for this socket
+ //
+ pSocket->FifoDepth -= 1;
+
+ //
+ // Update the new socket's state
+ //
+ pNewSocket->State = SOCKET_STATE_CONNECTED;
+ pNewSocket->bConfigured = TRUE;
+ DEBUG (( DEBUG_ACCEPT,
+ "0x%08x: Socket connected\r\n",
+ pNewSocket ));
+ }
+ }
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ }
+
+ //
+ // Return the new socket
+ //
+ if (( NULL != ppSocketProtocol )
+ && ( NULL != pNewSocket )) {
+ *ppSocketProtocol = &pNewSocket->SocketProtocol;
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ if ( NULL != pSocket ) {
+ *pErrno = pSocket->errno;
+ }
+ else
+ {
+ Status = EFI_INVALID_PARAMETER;
+ *pErrno = EBADF;
+ }
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Allocate and initialize a DT_SOCKET structure.
+
+ The ::SocketAllocate() function allocates a DT_SOCKET structure
+ and installs a protocol on ChildHandle. If pChildHandle is a
+ pointer to NULL, then a new handle is created and returned in
+ pChildHandle. If pChildHandle is not a pointer to NULL, then
+ the protocol installs on the existing pChildHandle.
+
+ @param [in, out] pChildHandle Pointer to the handle of the child to create.
+ If it is NULL, then a new handle is created.
+ If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI
+ handle.
+ @param [in] DebugFlags Flags for debug messages
+ @param [in, out] ppSocket The buffer to receive the DT_SOCKET structure address.
+
+ @retval EFI_SUCCESS The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+EslSocketAllocate (
+ IN OUT EFI_HANDLE * pChildHandle,
+ IN UINTN DebugFlags,
+ IN OUT DT_SOCKET ** ppSocket
+ )
+{
+ UINTN LengthInBytes;
+ DT_LAYER * pLayer;
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Create a socket structure
+ //
+ LengthInBytes = sizeof ( *pSocket );
+ Status = gBS->AllocatePool (
+ EfiRuntimeServicesData,
+ LengthInBytes,
+ (VOID **) &pSocket
+ );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT,
+ "0x%08x: Allocate pSocket, %d bytes\r\n",
+ pSocket,
+ LengthInBytes ));
+
+ //
+ // Initialize the socket protocol
+ //
+ ZeroMem ( pSocket, LengthInBytes );
+
+ pSocket->Signature = SOCKET_SIGNATURE;
+ pSocket->SocketProtocol.pfnAccept = EslSocketAccept;
+ pSocket->SocketProtocol.pfnBind = EslSocketBind;
+ pSocket->SocketProtocol.pfnClosePoll = EslSocketClosePoll;
+ pSocket->SocketProtocol.pfnCloseStart = EslSocketCloseStart;
+ pSocket->SocketProtocol.pfnConnect = EslSocketConnect;
+ pSocket->SocketProtocol.pfnGetLocal = EslSocketGetLocalAddress;
+ pSocket->SocketProtocol.pfnGetPeer = EslSocketGetPeerAddress;
+ pSocket->SocketProtocol.pfnListen = EslSocketListen;
+ pSocket->SocketProtocol.pfnOptionGet = EslSocketOptionGet;
+ pSocket->SocketProtocol.pfnOptionSet = EslSocketOptionSet;
+ pSocket->SocketProtocol.pfnPoll = EslSocketPoll;
+ pSocket->SocketProtocol.pfnReceive = EslSocketReceive;
+ pSocket->SocketProtocol.pfnSend = EslSocketTransmit;
+ pSocket->SocketProtocol.pfnShutdown = EslSocketShutdown;
+ pSocket->SocketProtocol.pfnSocket = EslSocket;
+
+ pSocket->MaxRxBuf = MAX_RX_DATA;
+ pSocket->MaxTxBuf = MAX_TX_DATA;
+
+ //
+ // Install the socket protocol on the specified handle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ pChildHandle,
+ &gEfiSocketProtocolGuid,
+ &pSocket->SocketProtocol,
+ NULL
+ );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+ "Installed: gEfiSocketProtocolGuid on 0x%08x\r\n",
+ *pChildHandle ));
+ pSocket->SocketProtocol.SocketHandle = *pChildHandle;
+
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Add this socket to the list
+ //
+ pLayer = &mEslLayer;
+ pSocket->pNext = pLayer->pSocketList;
+ pLayer->pSocketList = pSocket;
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+
+ //
+ // Return the socket structure address
+ //
+ *ppSocket = pSocket;
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL | DEBUG_INIT,
+ "ERROR - Failed to install gEfiSocketProtocolGuid on 0x%08x, Status: %r\r\n",
+ *pChildHandle,
+ Status ));
+ }
+
+ //
+ // Release the socket if necessary
+ //
+ if ( EFI_ERROR ( Status )) {
+ gBS->FreePool ( pSocket );
+ DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT,
+ "0x%08x: Free pSocket, %d bytes\r\n",
+ pSocket,
+ sizeof ( *pSocket )));
+ pSocket = NULL;
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL | DEBUG_INIT,
+ "ERROR - Failed socket allocation, Status: %r\r\n",
+ Status ));
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Bind a name to a socket.
+
+ The ::SocketBind routine connects a name to a socket on the local machine. The
+ <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html">POSIX</a>
+ documentation for the bind routine is available online for reference.
+
+ @param [in] pSocketProtocol Address of the socket protocol 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.
+
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS - Socket successfully created
+
+ **/
+EFI_STATUS
+EslSocketBind (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN const struct sockaddr * pSockAddr,
+ IN socklen_t SockAddrLength,
+ OUT int * pErrno
+ )
+{
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Validate the socket
+ //
+ pSocket = NULL;
+ if ( NULL != pSocketProtocol ) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+
+ //
+ // Validate the structure pointer
+ //
+ if ( NULL == pSockAddr ) {
+ DEBUG (( DEBUG_BIND,
+ "ERROR - pSockAddr is NULL!\r\n" ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EFAULT;
+ }
+ else{
+ //
+ // Validate the name length
+ //
+ if (( SockAddrLength < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data )))
+ || ( pSockAddr->sa_len < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data )))) {
+ DEBUG (( DEBUG_BIND,
+ "ERROR - Invalid bind name length: %d, sa_len: %d\r\n",
+ SockAddrLength,
+ pSockAddr->sa_len ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EINVAL;
+ }
+ else {
+ //
+ // Set the socket address length
+ //
+ if ( SockAddrLength > pSockAddr->sa_len ) {
+ SockAddrLength = pSockAddr->sa_len;
+ }
+
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Validate the local address
+ //
+ switch ( pSockAddr->sa_family ) {
+ default:
+ DEBUG (( DEBUG_BIND,
+ "ERROR - Invalid bind address family: %d\r\n",
+ pSockAddr->sa_family ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_BIND,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ Status = EslTcpBind4 ( pSocket,
+ pSockAddr,
+ SockAddrLength );
+ break;
+
+ case SOCK_DGRAM:
+ Status = EslUdpBind4 ( pSocket,
+ pSockAddr,
+ SockAddrLength );
+ break;
+ }
+ break;
+ }
+
+ //
+ // Mark this socket as bound if successful
+ //
+ if ( !EFI_ERROR ( Status )) {
+ pSocket->State = SOCKET_STATE_BOUND;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ if ( NULL != pSocket ) {
+ *pErrno = pSocket->errno;
+ }
+ else
+ {
+ Status = EFI_INVALID_PARAMETER;
+ *pErrno = EBADF;
+ }
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Determine if the socket is closed
+
+ Reverses the operations of the ::SocketAllocate() routine.
+
+ @param [in] pSocketProtocol Address of the socket protocol structure.
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS Socket successfully closed
+ @retval EFI_NOT_READY Close still in progress
+ @retval EFI_ALREADY Close operation already in progress
+ @retval Other Failed to close the socket
+
+**/
+EFI_STATUS
+EslSocketClosePoll (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN int * pErrno
+ )
+{
+ int errno;
+ DT_LAYER * pLayer;
+ DT_SOCKET * pNextSocket;
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ errno = 0;
+ Status = EFI_SUCCESS;
+
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Locate the socket
+ //
+ pLayer = &mEslLayer;
+ pNextSocket = pLayer->pSocketList;
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+ while ( NULL != pNextSocket ) {
+ if ( pNextSocket == pSocket ) {
+ //
+ // Determine if the socket is in the closing state
+ //
+ if ( SOCKET_STATE_CLOSED == pSocket->State ) {
+ //
+ // Walk the list of ports
+ //
+ if ( NULL == pSocket->pPortList ) {
+ //
+ // All the ports are closed
+ // Close the WaitAccept event if necessary
+ //
+ if ( NULL != pSocket->WaitAccept ) {
+ Status = gBS->CloseEvent ( pSocket->WaitAccept );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_SOCKET | DEBUG_CLOSE | DEBUG_POOL,
+ "0x%08x: Closed WaitAccept event\r\n",
+ pSocket->WaitAccept ));
+ //
+ // Return the transmit status
+ //
+ Status = pSocket->TxError;
+ if ( EFI_ERROR ( Status )) {
+ pSocket->errno = EIO;
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_SOCKET | DEBUG_CLOSE | DEBUG_POOL,
+ "ERROR - Failed to close the WaitAccept event, Status: %r\r\n",
+ Status ));
+ ASSERT ( EFI_SUCCESS == Status );
+ }
+ }
+ }
+ else {
+ //
+ // At least one port is still open
+ //
+ Status = EFI_NOT_READY;
+ errno = EAGAIN;
+ }
+ }
+ else {
+ //
+ // SocketCloseStart was not called
+ //
+ Status = EFI_NOT_STARTED;
+ errno = EPERM;
+ }
+ break;
+ }
+
+ //
+ // Set the next socket
+ //
+ pNextSocket = pNextSocket->pNext;
+ }
+
+ //
+ // Handle the error case where the socket was already closed
+ //
+ if ( NULL == pSocket ) {
+ //
+ // Socket not found
+ //
+ Status = EFI_NOT_FOUND;
+ errno = ENOTSOCK;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ *pErrno = errno;
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Start the close operation on the socket
+
+ Start closing the socket by closing all of the ports. Upon
+ completion, the ::SocketPoll() routine finishes closing the
+ socket.
+
+ @param [in] pSocketProtocol Address of the socket protocol structure.
+ @param [in] bCloseNow Boolean to control close behavior
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS Socket successfully closed
+ @retval EFI_NOT_READY Close still in progress
+ @retval EFI_ALREADY Close operation already in progress
+ @retval Other Failed to close the socket
+
+**/
+EFI_STATUS
+EslSocketCloseStart (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN BOOLEAN bCloseNow,
+ IN int * pErrno
+ )
+{
+ int errno;
+ DT_PORT * pNextPort;
+ DT_PORT * pPort;
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+ errno = 0;
+
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Determine if the socket is already closed
+ //
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+ if ( SOCKET_STATE_CLOSED > pSocket->State ) {
+ //
+ // Update the socket state
+ //
+ pSocket->State = SOCKET_STATE_CLOSED;
+
+ //
+ // Walk the list of ports
+ //
+ pPort = pSocket->pPortList;
+ while ( NULL != pPort ) {
+ //
+ // Start closing the ports
+ //
+ pNextPort = pPort->pLinkSocket;
+ Status = pPort->pfnCloseStart ( pPort,
+ bCloseNow,
+ DEBUG_CLOSE | DEBUG_LISTEN | DEBUG_CONNECTION );
+ if (( EFI_SUCCESS != Status )
+ && ( EFI_NOT_READY != Status )) {
+ errno = EIO;
+ break;
+ }
+
+ //
+ // Set the next port
+ //
+ pPort = pNextPort;
+ }
+
+ //
+ // Attempt to finish closing the socket
+ //
+ if ( NULL == pPort ) {
+ Status = EslSocketClosePoll ( pSocketProtocol, &errno );
+ }
+ }
+ else {
+ Status = EFI_ALREADY_STARTED;
+ errno = EALREADY;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ *pErrno = errno;
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Connect to a remote system via the network.
+
+ The ::SocketConnect routine attempts to establish a connection to a
+ socket on the local or remote system using the specified address.
+ The POSIX
+ <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html">connect</a>
+ documentation is available online.
+
+ There are three states associated with a connection:
+ <ul>
+ <li>Not connected</li>
+ <li>Connection in progress</li>
+ <li>Connected</li>
+ </ul>
+ In the "Not connected" state, calls to ::connect start the connection
+ processing and update the state to "Connection in progress". During
+ the "Connection in progress" state, connect polls for connection completion
+ and moves the state to "Connected" after the connection is established.
+ Note that these states are only visible when the file descriptor is marked
+ with O_NONBLOCK. Also, the POLL_WRITE bit is set when the connection
+ completes and may be used by poll or select as an indicator to call
+ connect again.
+
+ @param [in] pSocketProtocol Address of the socket protocol structure.
+
+ @param [in] pSockAddr Network address of the remote system.
+
+ @param [in] SockAddrLength Length in bytes of the network address.
+
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @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
+EslSocketConnect (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN const struct sockaddr * pSockAddr,
+ IN socklen_t SockAddrLength,
+ IN int * pErrno
+ )
+{
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DEBUG (( DEBUG_CONNECT, "Entering SocketConnect\r\n" ));
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Validate the socket
+ //
+ pSocket = NULL;
+ if ( NULL != pSocketProtocol ) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+
+ //
+ // Validate the name length
+ //
+ if (( SockAddrLength < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data )))
+ || ( pSockAddr->sa_len < ( sizeof ( struct sockaddr ) - sizeof ( pSockAddr->sa_data )))) {
+ DEBUG (( DEBUG_CONNECT,
+ "ERROR - Invalid bind name length: %d, sa_len: %d\r\n",
+ SockAddrLength,
+ pSockAddr->sa_len ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EINVAL;
+ }
+ else {
+ //
+ // Assume success
+ //
+ pSocket->errno = 0;
+
+ //
+ // Set the socket address length
+ //
+ if ( SockAddrLength > pSockAddr->sa_len ) {
+ SockAddrLength = pSockAddr->sa_len;
+ }
+
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Validate the socket state
+ //
+ switch ( pSocket->State ) {
+ default:
+ //
+ // Wrong socket state
+ //
+ pSocket->errno = EIO;
+ Status = EFI_DEVICE_ERROR;
+ break;
+
+ case SOCKET_STATE_NOT_CONFIGURED:
+ case SOCKET_STATE_BOUND:
+ //
+ // Validate the local address
+ //
+ switch ( pSockAddr->sa_family ) {
+ default:
+ DEBUG (( DEBUG_CONNECT,
+ "ERROR - Invalid bind address family: %d\r\n",
+ pSockAddr->sa_family ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_CONNECT,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ //
+ // Start the connection processing
+ //
+ Status = EslTcpConnectStart4 ( pSocket,
+ pSockAddr,
+ SockAddrLength );
+
+ //
+ // Set the next state if connecting
+ //
+ if ( EFI_NOT_READY == Status ) {
+ pSocket->State = SOCKET_STATE_CONNECTING;
+ }
+ break;
+
+ case SOCK_DGRAM:
+ Status = EslUdpConnect4 ( pSocket,
+ pSockAddr,
+ SockAddrLength );
+ break;
+ }
+ break;
+ }
+ break;
+
+ case SOCKET_STATE_CONNECTING:
+ //
+ // Validate the local address
+ //
+ switch ( pSockAddr->sa_family ) {
+ default:
+ DEBUG (( DEBUG_CONNECT,
+ "ERROR - Invalid bind address family: %d\r\n",
+ pSockAddr->sa_family ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_CONNECT,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ //
+ // Determine if the connection processing is completed
+ //
+ Status = EslTcpConnectPoll4 ( pSocket );
+
+ //
+ // Set the next state if connected
+ //
+ if ( EFI_NOT_READY != Status ) {
+ if ( !EFI_ERROR ( Status )) {
+ pSocket->State = SOCKET_STATE_CONNECTED;
+ }
+ else {
+ pSocket->State = SOCKET_STATE_BOUND;
+ }
+ }
+ break;
+
+ case SOCK_DGRAM:
+ //
+ // Already connected
+ //
+ pSocket->errno = EISCONN;
+ Status = EFI_ALREADY_STARTED;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case SOCKET_STATE_CONNECTED:
+ //
+ // Already connected
+ //
+ pSocket->errno = EISCONN;
+ Status = EFI_ALREADY_STARTED;
+ break;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ if ( NULL != pSocket ) {
+ *pErrno = pSocket->errno;
+ }
+ else
+ {
+ //
+ // Bad socket protocol
+ //
+ DEBUG (( DEBUG_ERROR | DEBUG_CONNECT,
+ "ERROR - pSocketProtocol invalid!\r\n" ));
+ Status = EFI_INVALID_PARAMETER;
+ *pErrno = EBADF;
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ DEBUG (( DEBUG_CONNECT, "Exiting SocketConnect, Status: %r\r\n", Status ));
+ return Status;
+}
+
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If pChildHandle is a pointer to NULL, then a new handle is created and returned in pChildHandle.
+ If pChildHandle is not a pointer to NULL, then the protocol installs on the existing pChildHandle.
+
+ @param [in] pThis Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param [in] pChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCESS The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+EslSocketCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL * pThis,
+ IN OUT EFI_HANDLE * pChildHandle
+ )
+{
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Create a socket structure
+ //
+ Status = EslSocketAllocate ( pChildHandle,
+ DEBUG_SOCKET,
+ &pSocket );
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param [in] pThis Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param [in] ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCESS The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+EslSocketDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL * pThis,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ DT_LAYER * pLayer;
+ DT_SOCKET * pSocket;
+ DT_SOCKET * pSocketPrevious;
+ EFI_SOCKET_PROTOCOL * pSocketProtocol;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Locate the socket control structure
+ //
+ pLayer = &mEslLayer;
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiSocketProtocolGuid,
+ (VOID **)&pSocketProtocol,
+ pLayer->ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if ( !EFI_ERROR ( Status )) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Walk the socket list
+ //
+ pSocketPrevious = pLayer->pSocketList;
+ if ( NULL != pSocketPrevious ) {
+ if ( pSocket == pSocketPrevious ) {
+ //
+ // Remove the socket from the head of the list
+ //
+ pLayer->pSocketList = pSocket->pNext;
+ }
+ else {
+ //
+ // Find the socket in the middle of the list
+ //
+ while (( NULL != pSocketPrevious )
+ && ( pSocket != pSocketPrevious->pNext )) {
+ //
+ // Set the next socket
+ //
+ pSocketPrevious = pSocketPrevious->pNext;
+ }
+ if ( NULL != pSocketPrevious ) {
+ //
+ // Remove the socket from the middle of the list
+ //
+ pSocketPrevious = pSocket->pNext;
+ }
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL,
+ "ERROR - Socket list is empty!\r\n" ));
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+
+ //
+ // Determine if the socket was found
+ //
+ if ( NULL != pSocketPrevious ) {
+ pSocket->pNext = NULL;
+
+ //
+ // Remove the socket protocol
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiSocketProtocolGuid,
+ &pSocket->SocketProtocol,
+ NULL );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_POOL | DEBUG_INFO,
+ "Removed: gEfiSocketProtocolGuid from 0x%08x\r\n",
+ ChildHandle ));
+
+ //
+ // Free the socket structure
+ //
+ Status = gBS->FreePool ( pSocket );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_POOL,
+ "0x%08x: Free pSocket, %d bytes\r\n",
+ pSocket,
+ sizeof ( *pSocket )));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL,
+ "ERROR - Failed to free pSocket 0x%08x, Status: %r\r\n",
+ pSocket,
+ Status ));
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INFO,
+ "ERROR - Failed to remove gEfiSocketProtocolGuid from 0x%08x, Status: %r\r\n",
+ ChildHandle,
+ Status ));
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_INFO,
+ "ERROR - The socket was not in the socket list!\r\n" ));
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - Failed to open socket protocol on 0x%08x, Status; %r\r\n",
+ ChildHandle,
+ Status ));
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Get the local address.
+
+ @param [in] pSocketProtocol Address of the socket protocol structure.
+
+ @param [out] pAddress Network address to receive the local system address
+
+ @param [in,out] pAddressLength Length of the local network address structure
+
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS - Local address successfully returned
+
+ **/
+EFI_STATUS
+EslSocketGetLocalAddress (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ OUT struct sockaddr * pAddress,
+ IN OUT socklen_t * pAddressLength,
+ IN int * pErrno
+ )
+{
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Validate the socket
+ //
+ pSocket = NULL;
+ if ( NULL != pSocketProtocol ) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+
+ //
+ // Verify the address buffer and length address
+ //
+ if (( NULL != pAddress ) && ( NULL != pAddressLength )) {
+ //
+ // Verify the socket state
+ //
+ if ( SOCKET_STATE_CONNECTED == pSocket->State ) {
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Validate the local address
+ //
+ switch ( pSocket->Domain ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket address family: %d\r\n",
+ pSocket->Domain ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ //
+ // Get the local address
+ //
+ Status = EslTcpGetLocalAddress4 ( pSocket,
+ pAddress,
+ pAddressLength );
+ break;
+
+ case SOCK_DGRAM:
+ //
+ // Get the local address
+ //
+ Status = EslUdpGetLocalAddress4 ( pSocket,
+ pAddress,
+ pAddressLength );
+ break;
+ }
+ break;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ else {
+ pSocket->errno = ENOTCONN;
+ Status = EFI_NOT_STARTED;
+ }
+ }
+ else {
+ pSocket->errno = EINVAL;
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ if ( NULL != pSocket ) {
+ *pErrno = pSocket->errno;
+ }
+ else
+ {
+ Status = EFI_INVALID_PARAMETER;
+ *pErrno = EBADF;
+ }
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Get the peer address.
+
+ @param [in] pSocketProtocol Address of the socket protocol structure.
+
+ @param [out] pAddress Network address to receive the remote system address
+
+ @param [in,out] pAddressLength Length of the remote network address structure
+
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS - Remote address successfully returned
+
+ **/
+EFI_STATUS
+EslSocketGetPeerAddress (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ OUT struct sockaddr * pAddress,
+ IN OUT socklen_t * pAddressLength,
+ IN int * pErrno
+ )
+{
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Validate the socket
+ //
+ pSocket = NULL;
+ if ( NULL != pSocketProtocol ) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+
+ //
+ // Verify the address buffer and length address
+ //
+ if (( NULL != pAddress ) && ( NULL != pAddressLength )) {
+ //
+ // Verify the socket state
+ //
+ if ( SOCKET_STATE_CONNECTED == pSocket->State ) {
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Validate the local address
+ //
+ switch ( pSocket->Domain ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket address family: %d\r\n",
+ pSocket->Domain ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ //
+ // Verify the port state
+ //
+ Status = EslTcpGetRemoteAddress4 ( pSocket,
+ pAddress,
+ pAddressLength );
+ break;
+
+ case SOCK_DGRAM:
+ //
+ // Verify the port state
+ //
+ Status = EslUdpGetRemoteAddress4 ( pSocket,
+ pAddress,
+ pAddressLength );
+ break;
+ }
+ break;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ else {
+ pSocket->errno = ENOTCONN;
+ Status = EFI_NOT_STARTED;
+ }
+ }
+ else {
+ pSocket->errno = EINVAL;
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ if ( NULL != pSocket ) {
+ *pErrno = pSocket->errno;
+ }
+ else
+ {
+ Status = EFI_INVALID_PARAMETER;
+ *pErrno = EBADF;
+ }
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Establish the known port to listen for network connections.
+
+ The ::SocketListen 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 ::SocketAccept 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] pSocketProtocol Address of the socket protocol structure.
+
+ @param [in] Backlog Backlog specifies the maximum FIFO depth for
+ the connections waiting for the application
+ to call accept. Connection attempts received
+ while the queue is full are refused.
+
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS - Socket successfully created
+ @retval Other - Failed to enable the socket for listen
+
+**/
+EFI_STATUS
+EslSocketListen (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN INT32 Backlog,
+ OUT int * pErrno
+ )
+{
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_STATUS TempStatus;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Validate the socket
+ //
+ pSocket = NULL;
+ if ( NULL != pSocketProtocol ) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+
+ //
+ // Assume success
+ //
+ pSocket->Status = EFI_SUCCESS;
+ pSocket->errno = 0;
+
+ //
+ // Verify that the bind operation was successful
+ //
+ if ( SOCKET_STATE_BOUND == pSocket->State ) {
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Create the event for SocketAccept completion
+ //
+ Status = gBS->CreateEvent ( 0,
+ TplPrevious,
+ NULL,
+ NULL,
+ &pSocket->WaitAccept );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_POOL,
+ "0x%08x: Created WaitAccept event\r\n",
+ pSocket->WaitAccept ));
+ //
+ // Set the maximum FIFO depth
+ //
+ if ( 0 >= Backlog ) {
+ Backlog = MAX_PENDING_CONNECTIONS;
+ }
+ else {
+ if ( SOMAXCONN < Backlog ) {
+ Backlog = SOMAXCONN;
+ }
+ else {
+ pSocket->MaxFifoDepth = Backlog;
+ }
+ }
+
+ //
+ // Validate the local address
+ //
+ switch ( pSocket->Domain ) {
+ default:
+ DEBUG (( DEBUG_BIND,
+ "ERROR - Invalid socket address family: %d\r\n",
+ pSocket->Domain ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_BIND,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ Status = EslTcpListen4 ( pSocket );
+ break;
+
+/*
+ case SOCK_DGRAM:
+ Status = UdpListen4 ( pSocket );
+ break;
+*/
+ }
+ break;
+ }
+
+ //
+ // Place the socket in the listen state if successful
+ //
+ if ( !EFI_ERROR ( Status )) {
+ pSocket->State = SOCKET_STATE_LISTENING;
+ }
+ else {
+ //
+ // Not waiting for SocketAccept to complete
+ //
+ TempStatus = gBS->CloseEvent ( pSocket->WaitAccept );
+ if ( !EFI_ERROR ( TempStatus )) {
+ DEBUG (( DEBUG_POOL,
+ "0x%08x: Closed WaitAccept event\r\n",
+ pSocket->WaitAccept ));
+ pSocket->WaitAccept = NULL;
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL,
+ "ERROR - Failed to close WaitAccept event, Status: %r\r\n",
+ TempStatus ));
+ ASSERT ( EFI_SUCCESS == TempStatus );
+ }
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_LISTEN,
+ "ERROR - Failed to create the WaitAccept event, Status: %r\r\n",
+ Status ));
+ pSocket->errno = ENOMEM;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_LISTEN,
+ "ERROR - Bind operation must be performed first!\r\n" ));
+ pSocket->errno = EDESTADDRREQ;
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ if ( NULL != pSocket ) {
+ *pErrno = pSocket->errno;
+ }
+ else
+ {
+ Status = EFI_INVALID_PARAMETER;
+ *pErrno = EBADF;
+ }
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Get the socket options
+
+ Retrieve the socket options one at a time by name. The
+ <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html">POSIX</a>
+ documentation is available online.
+
+ @param [in] pSocketProtocol Address of the socket protocol structure.
+ @param [in] level Option protocol level
+ @param [in] OptionName Name of the option
+ @param [out] pOptionValue Buffer to receive the option value
+ @param [in,out] pOptionLength Length of the buffer in bytes,
+ upon return length of the option value in bytes
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS - Socket data successfully received
+
+ **/
+EFI_STATUS
+EslSocketOptionGet (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN int level,
+ IN int OptionName,
+ OUT void * __restrict pOptionValue,
+ IN OUT socklen_t * __restrict pOptionLength,
+ IN int * pErrno
+ )
+{
+ int errno;
+ socklen_t LengthInBytes;
+ socklen_t MaxBytes;
+ UINT8 * pOptionData;
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume failure
+ //
+ errno = EINVAL;
+ Status = EFI_INVALID_PARAMETER;
+
+ //
+ // Validate the socket
+ //
+ pSocket = NULL;
+ if (( NULL != pSocketProtocol )
+ && ( NULL != pOptionValue )
+ && ( NULL != pOptionLength )) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+ LengthInBytes = 0;
+ MaxBytes = *pOptionLength;
+ pOptionData = NULL;
+ switch ( level ) {
+ default:
+ //
+ // Protocol level not supported
+ //
+ errno = ENOTSUP;
+ Status = EFI_UNSUPPORTED;
+ break;
+
+ case SOL_SOCKET:
+ switch ( OptionName ) {
+ default:
+ //
+ // Option not supported
+ //
+ errno = ENOTSUP;
+ Status = EFI_UNSUPPORTED;
+ break;
+
+ case SO_RCVTIMEO:
+ //
+ // Return the receive timeout
+ //
+ pOptionData = (UINT8 *)&pSocket->RxTimeout;
+ LengthInBytes = sizeof ( pSocket->RxTimeout );
+ break;
+
+ case SO_RCVBUF:
+ //
+ // Return the maximum transmit buffer size
+ //
+ pOptionData = (UINT8 *)&pSocket->MaxRxBuf;
+ LengthInBytes = sizeof ( pSocket->MaxRxBuf );
+ break;
+
+ case SO_SNDBUF:
+ //
+ // Return the maximum transmit buffer size
+ //
+ pOptionData = (UINT8 *)&pSocket->MaxTxBuf;
+ LengthInBytes = sizeof ( pSocket->MaxTxBuf );
+ break;
+
+ case SO_TYPE:
+ //
+ // Return the socket type
+ //
+ pOptionData = (UINT8 *)&pSocket->Type;
+ LengthInBytes = sizeof ( pSocket->Type );
+ break;
+ }
+ break;
+ }
+
+ //
+ // Return the option length
+ //
+ *pOptionLength = LengthInBytes;
+
+ //
+ // Return the option value
+ //
+ if ( NULL != pOptionData ) {
+ //
+ // Silently truncate the value length
+ //
+ if ( LengthInBytes > MaxBytes ) {
+ LengthInBytes = MaxBytes;
+ }
+ CopyMem ( pOptionValue, pOptionData, LengthInBytes );
+ errno = 0;
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ *pErrno = errno;
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Set the socket options
+
+ Adjust the socket options one at a time by name. The
+ <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html">POSIX</a>
+ documentation is available online.
+
+ @param [in] pSocketProtocol Address of the socket protocol structure.
+ @param [in] level Option protocol level
+ @param [in] OptionName Name of the option
+ @param [in] pOptionValue Buffer containing the option value
+ @param [in] OptionLength Length of the buffer in bytes
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS - Socket data successfully received
+
+ **/
+EFI_STATUS
+EslSocketOptionSet (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN int level,
+ IN int OptionName,
+ IN CONST void * pOptionValue,
+ IN socklen_t OptionLength,
+ IN int * pErrno
+ )
+{
+ int errno;
+ socklen_t LengthInBytes;
+ UINT8 * pOptionData;
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume failure
+ //
+ errno = EINVAL;
+ Status = EFI_INVALID_PARAMETER;
+
+ //
+ // Validate the socket
+ //
+ pSocket = NULL;
+ if (( NULL != pSocketProtocol )
+ && ( NULL != pOptionValue )) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+ LengthInBytes = 0;
+ pOptionData = NULL;
+ switch ( level ) {
+ default:
+ //
+ // Protocol level not supported
+ //
+ errno = ENOTSUP;
+ Status = EFI_UNSUPPORTED;
+ break;
+
+ case SOL_SOCKET:
+ switch ( OptionName ) {
+ default:
+ //
+ // Option not supported
+ //
+ errno = ENOTSUP;
+ Status = EFI_UNSUPPORTED;
+ break;
+
+ case SO_RCVTIMEO:
+ //
+ // Return the receive timeout
+ //
+ pOptionData = (UINT8 *)&pSocket->RxTimeout;
+ LengthInBytes = sizeof ( pSocket->RxTimeout );
+ break;
+
+ case SO_RCVBUF:
+ //
+ // Return the maximum transmit buffer size
+ //
+ pOptionData = (UINT8 *)&pSocket->MaxRxBuf;
+ LengthInBytes = sizeof ( pSocket->MaxRxBuf );
+ break;
+
+ case SO_SNDBUF:
+ //
+ // Send buffer size
+ //
+ //
+ // Return the maximum transmit buffer size
+ //
+ pOptionData = (UINT8 *)&pSocket->MaxTxBuf;
+ LengthInBytes = sizeof ( pSocket->MaxTxBuf );
+ break;
+ }
+ break;
+ }
+
+ //
+ // Validate the option length
+ //
+ if ( LengthInBytes <= OptionLength ) {
+ //
+ // Set the option value
+ //
+ if ( NULL != pOptionData ) {
+ CopyMem ( pOptionData, pOptionValue, LengthInBytes );
+ errno = 0;
+ Status = EFI_SUCCESS;
+ }
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ *pErrno = errno;
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Allocate a packet for a receive or transmit operation
+
+ @param [in] ppPacket Address to receive the DT_PACKET structure
+ @param [in] LengthInBytes Length of the packet structure
+ @param [in] DebugFlags Flags for debug messages
+
+ @retval EFI_SUCCESS - The packet was allocated successfully
+
+ **/
+EFI_STATUS
+EslSocketPacketAllocate (
+ IN DT_PACKET ** ppPacket,
+ IN size_t LengthInBytes,
+ IN UINTN DebugFlags
+ )
+{
+ DT_PACKET * pPacket;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Allocate a packet structure
+ //
+ LengthInBytes += sizeof ( *pPacket )
+ - sizeof ( pPacket->Op );
+ Status = gBS->AllocatePool ( EfiRuntimeServicesData,
+ LengthInBytes,
+ (VOID **)&pPacket );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT,
+ "0x%08x: Allocate pPacket, %d bytes\r\n",
+ pPacket,
+ LengthInBytes ));
+ pPacket->PacketSize = LengthInBytes;
+ }
+ else {
+ DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INFO,
+ "ERROR - Packet allocation failed for %d bytes, Status: %r\r\n",
+ LengthInBytes,
+ Status ));
+ pPacket = NULL;
+ }
+
+ //
+ // Return the packet
+ //
+ *ppPacket = pPacket;
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Free a packet used for receive or transmit operation
+
+ @param [in] pPacket Address of the DT_PACKET structure
+ @param [in] DebugFlags Flags for debug messages
+
+ @retval EFI_SUCCESS - The packet was allocated successfully
+
+ **/
+EFI_STATUS
+EslSocketPacketFree (
+ IN DT_PACKET * pPacket,
+ IN UINTN DebugFlags
+ )
+{
+ UINTN LengthInBytes;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Allocate a packet structure
+ //
+ LengthInBytes = pPacket->PacketSize;
+ Status = gBS->FreePool ( pPacket );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Free pPacket, %d bytes\r\n",
+ pPacket,
+ LengthInBytes ));
+ }
+ else {
+ DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INFO,
+ "ERROR - Failed to free packet 0x%08x, Status: %r\r\n",
+ pPacket,
+ Status ));
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Poll a socket for pending activity.
+
+ The SocketPoll routine checks a socket for pending activity associated
+ with the event mask. Activity is returned in the detected event buffer.
+
+ @param [in] pSocketProtocol Address of the socket protocol structure.
+
+ @param [in] Events Events of interest for this socket
+
+ @param [in] pEvents Address to receive the detected events
+
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS - Socket successfully polled
+ @retval EFI_INVALID_PARAMETER - When pEvents is NULL
+
+ **/
+EFI_STATUS
+EslSocketPoll (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN short Events,
+ IN short * pEvents,
+ IN int * pErrno
+ )
+{
+ short DetectedEvents;
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+ short ValidEvents;
+
+ DEBUG (( DEBUG_POLL, "Entering SocketPoll\r\n" ));
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+ DetectedEvents = 0;
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+ pSocket->errno = 0;
+
+ //
+ // Verify the socket state
+ //
+ if ( !pSocket->bConfigured ) {
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Validate the local address
+ //
+ switch ( pSocket->Domain ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket address family: %d\r\n",
+ pSocket->Domain ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ //
+ // Verify the port state
+ //
+ Status = EslTcpSocketIsConfigured4 ( pSocket );
+ break;
+
+ case SOCK_DGRAM:
+ //
+ // Verify the port state
+ //
+ Status = EslUdpSocketIsConfigured4 ( pSocket );
+ break;
+ }
+ break;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Check for invalid events
+ //
+ ValidEvents = POLLIN
+ | POLLPRI
+ | POLLOUT | POLLWRNORM
+ | POLLERR
+ | POLLHUP
+ | POLLNVAL
+ | POLLRDNORM
+ | POLLRDBAND
+ | POLLWRBAND ;
+ if ( 0 != ( Events & ( ~ValidEvents ))) {
+ DetectedEvents |= POLLNVAL;
+ DEBUG (( DEBUG_INFO | DEBUG_POLL,
+ "ERROR - Invalid event mask, Valid Events: 0x%04x, Invalid Events: 0x%04x\r\n",
+ Events & ValidEvents,
+ Events & ( ~ValidEvents )));
+ }
+ else {
+ //
+ // Check for pending connections
+ //
+ if ( 0 != pSocket->FifoDepth ) {
+ //
+ // A connection is waiting for an accept call
+ // See posix connect documentation at
+ // http://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.htm
+ //
+ DetectedEvents |= POLLIN | POLLRDNORM;
+ }
+ if ( pSocket->bConnected ) {
+ //
+ // A connection is present
+ // See posix connect documentation at
+ // http://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.htm
+ //
+ DetectedEvents |= POLLOUT | POLLWRNORM;
+ }
+
+ //
+ // The following bits are set based upon the POSIX poll documentation at
+ // http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html
+ //
+
+ //
+ // Check for urgent receive data
+ //
+ if ( 0 < pSocket->RxOobBytes ) {
+ DetectedEvents |= POLLRDBAND | POLLPRI | POLLIN;
+ }
+
+ //
+ // Check for normal receive data
+ //
+ if (( 0 < pSocket->RxBytes )
+ || ( EFI_SUCCESS != pSocket->RxError )) {
+ DetectedEvents |= POLLRDNORM | POLLIN;
+ }
+
+ //
+ // Handle the receive errors
+ //
+ if (( EFI_SUCCESS != pSocket->RxError )
+ && ( 0 == ( DetectedEvents & POLLIN ))) {
+ DetectedEvents |= POLLERR | POLLIN | POLLRDNORM | POLLRDBAND;
+ }
+
+ //
+ // Check for urgent transmit data buffer space
+ //
+ if (( MAX_TX_DATA > pSocket->TxOobBytes )
+ || ( EFI_SUCCESS != pSocket->TxError )) {
+ DetectedEvents |= POLLWRBAND;
+ }
+
+ //
+ // Check for normal transmit data buffer space
+ //
+ if (( MAX_TX_DATA > pSocket->TxBytes )
+ || ( EFI_SUCCESS != pSocket->TxError )) {
+ DetectedEvents |= POLLWRNORM;
+ }
+
+ //
+ // Handle the transmit error
+ //
+ if ( EFI_ERROR ( pSocket->TxError )) {
+ DetectedEvents |= POLLERR;
+ }
+ }
+ }
+
+ //
+ // Return the detected events
+ //
+ *pEvents = DetectedEvents & ( Events
+ | POLLERR
+ | POLLHUP
+ | POLLNVAL );
+
+ //
+ // Return the operation status
+ //
+ DEBUG (( DEBUG_POLL, "Exiting SocketPoll, Status: %r\r\n", Status ));
+ return Status;
+}
+
+
+/**
+ Receive data from a network connection.
+
+
+ @param [in] pSocketProtocol Address of the socket protocol 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
+
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS - Socket data successfully received
+
+ **/
+EFI_STATUS
+EslSocketReceive (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN INT32 Flags,
+ IN size_t BufferLength,
+ IN UINT8 * pBuffer,
+ OUT size_t * pDataLength,
+ OUT struct sockaddr * pAddress,
+ IN OUT socklen_t * pAddressLength,
+ IN int * pErrno
+ )
+{
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Validate the socket
+ //
+ pSocket = NULL;
+ if ( NULL != pSocketProtocol ) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+
+ //
+ // Verify the socket state
+ //
+ if ( !pSocket->bConfigured ) {
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Validate the local address
+ //
+ switch ( pSocket->Domain ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket address family: %d\r\n",
+ pSocket->Domain ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ //
+ // Verify the port state
+ //
+ Status = EslTcpSocketIsConfigured4 ( pSocket );
+ break;
+
+ case SOCK_DGRAM:
+ //
+ // Verify the port state
+ //
+ Status = EslUdpSocketIsConfigured4 ( pSocket );
+ break;
+ }
+ break;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+
+ //
+ // Set errno if a failure occurs
+ //
+ if ( EFI_ERROR ( Status )) {
+ pSocket->errno = EADDRNOTAVAIL;
+ }
+ }
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Validate the buffer length
+ //
+ if (( NULL == pDataLength )
+ && ( 0 > pDataLength )
+ && ( NULL == pBuffer )) {
+ if ( NULL == pDataLength ) {
+ DEBUG (( DEBUG_RX,
+ "ERROR - pDataLength is NULL!\r\n" ));
+ }
+ else if ( NULL == pBuffer ) {
+ DEBUG (( DEBUG_RX,
+ "ERROR - pBuffer is NULL!\r\n" ));
+ }
+ else {
+ DEBUG (( DEBUG_RX,
+ "ERROR - Data length < 0!\r\n" ));
+ }
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EFAULT;
+ }
+ else{
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Validate the local address
+ //
+ switch ( pSocket->Domain ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket address family: %d\r\n",
+ pSocket->Domain ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ Status = EslTcpReceive4 ( pSocket,
+ Flags,
+ BufferLength,
+ pBuffer,
+ pDataLength,
+ pAddress,
+ pAddressLength );
+ break;
+
+ case SOCK_DGRAM:
+ Status = EslUdpReceive4 ( pSocket,
+ Flags,
+ BufferLength,
+ pBuffer,
+ pDataLength,
+ pAddress,
+ pAddressLength);
+ break;
+ }
+ break;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ if ( NULL != pSocket ) {
+ *pErrno = pSocket->errno;
+ }
+ else
+ {
+ Status = EFI_INVALID_PARAMETER;
+ *pErrno = EBADF;
+ }
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Shutdown the socket receive and transmit operations
+
+ The SocketShutdown routine stops the socket receive and transmit
+ operations.
+
+ @param [in] pSocketProtocol Address of the socket protocol structure.
+
+ @param [in] How Which operations to stop
+
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS - Socket operations successfully shutdown
+
+ **/
+EFI_STATUS
+EslSocketShutdown (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN int How,
+ IN int * pErrno
+ )
+{
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Validate the socket
+ //
+ pSocket = NULL;
+ if ( NULL != pSocketProtocol ) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+
+ //
+ // Verify that the socket is connected
+ //
+ if ( pSocket->bConnected ) {
+ //
+ // Validate the How value
+ //
+ if (( SHUT_RD <= How ) && ( SHUT_RDWR >= How )) {
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Disable the receiver if requested
+ //
+ if (( SHUT_RD == How ) || ( SHUT_RDWR == How )) {
+ pSocket->bRxDisable = TRUE;
+ }
+
+ //
+ // Disable the transmitter if requested
+ //
+ if (( SHUT_WR == How ) || ( SHUT_RDWR == How )) {
+ pSocket->bTxDisable = TRUE;
+ }
+
+ //
+ // Validate the local address
+ //
+ switch ( pSocket->Domain ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket address family: %d\r\n",
+ pSocket->Domain ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ //
+ // Cancel the pending receive operation
+ //
+ Status = EslTcpRxCancel4 ( pSocket );
+ break;
+
+ case SOCK_DGRAM:
+ //
+ // Cancel the pending receive operation
+ //
+ Status = EslUdpRxCancel4 ( pSocket );
+ break;
+ }
+ break;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ else {
+ //
+ // The socket is not connected
+ //
+ pSocket->errno = ENOTCONN;
+ Status = EFI_NOT_STARTED;
+ }
+ }
+ else {
+ //
+ // Invalid How value
+ //
+ pSocket->errno = EINVAL;
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ if ( NULL != pSocket ) {
+ *pErrno = pSocket->errno;
+ }
+ else
+ {
+ Status = EFI_INVALID_PARAMETER;
+ *pErrno = EBADF;
+ }
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Send data using a network connection.
+
+ The SocketTransmit routine queues the data for transmission to the
+ remote network connection.
+
+ @param [in] pSocketProtocol Address of the socket protocol structure.
+
+ @param [in] Flags Message control flags
+
+ @param [in] BufferLength Length of the the buffer
+
+ @param [in] pBuffer Address of a buffer containing the data to send
+
+ @param [in] pDataLength Address to receive the number of data bytes sent
+
+ @param [in] pAddress Network address of the remote system address
+
+ @param [in] AddressLength Length of the remote network address structure
+
+ @param [out] pErrno Address to receive the errno value upon completion.
+
+ @retval EFI_SUCCESS - Socket data successfully queued for transmit
+
+ **/
+EFI_STATUS
+EslSocketTransmit (
+ IN EFI_SOCKET_PROTOCOL * pSocketProtocol,
+ IN int Flags,
+ IN size_t BufferLength,
+ IN CONST UINT8 * pBuffer,
+ OUT size_t * pDataLength,
+ IN const struct sockaddr * pAddress,
+ IN socklen_t AddressLength,
+ IN int * pErrno
+ )
+{
+ DT_SOCKET * pSocket;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Validate the socket
+ //
+ pSocket = NULL;
+ if ( NULL != pSocketProtocol ) {
+ pSocket = SOCKET_FROM_PROTOCOL ( pSocketProtocol );
+
+ //
+ // Verify the socket state
+ //
+ if ( !pSocket->bConfigured ) {
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Validate the local address
+ //
+ switch ( pSocket->Domain ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket address family: %d\r\n",
+ pSocket->Domain ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ //
+ // Verify the port state
+ //
+ Status = EslTcpSocketIsConfigured4 ( pSocket );
+ break;
+
+ case SOCK_DGRAM:
+ //
+ // Verify the port state
+ //
+ Status = EslUdpSocketIsConfigured4 ( pSocket );
+ break;
+ }
+ break;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+
+ //
+ // Set errno if a failure occurs
+ //
+ if ( EFI_ERROR ( Status )) {
+ pSocket->errno = EADDRNOTAVAIL;
+ }
+ }
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Verify that transmit is still allowed
+ //
+ if ( !pSocket->bTxDisable ) {
+ //
+ // Validate the buffer length
+ //
+ if (( NULL == pDataLength )
+ && ( 0 > pDataLength )
+ && ( NULL == pBuffer )) {
+ if ( NULL == pDataLength ) {
+ DEBUG (( DEBUG_RX,
+ "ERROR - pDataLength is NULL!\r\n" ));
+ }
+ else if ( NULL == pBuffer ) {
+ DEBUG (( DEBUG_RX,
+ "ERROR - pBuffer is NULL!\r\n" ));
+ }
+ else {
+ DEBUG (( DEBUG_RX,
+ "ERROR - Data length < 0!\r\n" ));
+ }
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EFAULT;
+ }
+ else {
+ //
+ // Validate the remote network address
+ //
+ if (( NULL != pAddress )
+ && ( AddressLength < pAddress->sa_len )) {
+ DEBUG (( DEBUG_TX,
+ "ERROR - Invalid sin_len field in address\r\n" ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EFAULT;
+ }
+ else {
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Validate the local address
+ //
+ switch ( pSocket->Domain ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket address family: %d\r\n",
+ pSocket->Domain ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case AF_INET:
+ //
+ // Determine the connection point within the network stack
+ //
+ switch ( pSocket->Type ) {
+ default:
+ DEBUG (( DEBUG_RX,
+ "ERROR - Invalid socket type: %d\r\n",
+ pSocket->Type));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ Status = EslTcpTxBuffer4 ( pSocket,
+ Flags,
+ BufferLength,
+ pBuffer,
+ pDataLength );
+ break;
+
+ case SOCK_DGRAM:
+ Status = EslUdpTxBuffer4 ( pSocket,
+ Flags,
+ BufferLength,
+ pBuffer,
+ pDataLength,
+ pAddress,
+ AddressLength );
+ break;
+ }
+ break;
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ }
+ }
+ else {
+ //
+ // The transmitter was shutdown
+ //
+ pSocket->errno = EPIPE;
+ Status = EFI_NOT_STARTED;
+ }
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ if ( NULL != pErrno ) {
+ if ( NULL != pSocket ) {
+ *pErrno = pSocket->errno;
+ }
+ else
+ {
+ Status = EFI_INVALID_PARAMETER;
+ *pErrno = EBADF;
+ }
+ }
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Socket layer's service binding protocol delcaration.
+**/
+EFI_SERVICE_BINDING_PROTOCOL mEfiServiceBinding = {
+ EslSocketCreateChild,
+ EslSocketDestroyChild
+};
diff --git a/StdLib/EfiSocketLib/Socket.h b/StdLib/EfiSocketLib/Socket.h new file mode 100644 index 0000000000..42377eb29c --- /dev/null +++ b/StdLib/EfiSocketLib/Socket.h @@ -0,0 +1,1337 @@ +/** @file
+ Definitions for the Socket layer driver.
+
+ 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
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+#ifndef _SOCKET_H_
+#define _SOCKET_H_
+
+#include <Efi/EfiSocketLib.h>
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+#define DEBUG_SOCKET 0x20000000 ///< Display Socket related messages
+#define DEBUG_BIND 0x10000000 ///< Display bind related messages
+#define DEBUG_LISTEN 0x08000000 ///< Display listen related messages
+#define DEBUG_CONNECTION 0x04000000 ///< Display connection list related messages
+#define DEBUG_POLL 0x02000000 ///< Display poll messages
+#define DEBUG_ACCEPT 0x01000000 ///< Display accept related messages
+#define DEBUG_RX 0x00800000 ///< Display receive messages
+#define DEBUG_TX 0x00400000 ///< Display transmit messages
+#define DEBUG_CLOSE 0x00200000 ///< Display close messages
+#define DEBUG_CONNECT 0x00100000 ///< Display connect messages
+
+#define MAX_PENDING_CONNECTIONS 1 ///< Maximum connection FIFO depth
+#define MAX_RX_DATA 65536 ///< Maximum receive data size
+#define MAX_TX_DATA ( MAX_RX_DATA * 2 )
+#define RX_PACKET_DATA 16384 ///< Maximum number of bytes in a RX packet
+
+#define LAYER_SIGNATURE SIGNATURE_32('S','k','t','L') ///< DT_LAYER memory signature
+#define SERVICE_SIGNATURE SIGNATURE_32('S','k','t','S') ///< DT_SERVICE memory signature
+#define SOCKET_SIGNATURE SIGNATURE_32('S','c','k','t') ///< DT_SOCKET memory signature
+#define PORT_SIGNATURE SIGNATURE_32('P','o','r','t') ///< DT_PORT memory signature
+
+typedef enum
+{
+ NETWORK_TYPE_UNKNOWN = 0,
+ NETWORK_TYPE_RAW,
+ NETWORK_TYPE_TCP4,
+ NETWORK_TYPE_TCP6,
+ NETWORK_TYPE_UDP4,
+ NETWORK_TYPE_UDP6
+} NETWORK_TYPE;
+
+typedef enum
+{
+ SOCKET_STATE_NOT_CONFIGURED = 0, ///< socket call was successful
+ SOCKET_STATE_BOUND, ///< bind call was successful
+ SOCKET_STATE_LISTENING, ///< listen call was successful
+ SOCKET_STATE_NO_PORTS, ///< No ports available
+ SOCKET_STATE_IN_FIFO, ///< Socket on FIFO
+ SOCKET_STATE_CONNECTING, ///< Connecting to a remote system
+ SOCKET_STATE_CONNECTED, ///< Accept or connect call was successful
+
+ //
+ // Close state must be the last in the list
+ //
+ SOCKET_STATE_CLOSED ///< Close call was successful
+} SOCKET_STATE;
+
+typedef enum
+{
+ PORT_STATE_ALLOCATED = 0, ///< Port allocated
+ PORT_STATE_OPEN, ///< Port opened
+ PORT_STATE_RX_ERROR, ///< Receive error detected
+
+ //
+ // Close state must be last in the list
+ //
+ PORT_STATE_CLOSE_STARTED, ///< Close started on port
+ PORT_STATE_CLOSE_TX_DONE, ///< Transmits shutdown
+ PORT_STATE_CLOSE_RX_DONE, ///< Receives shutdown
+ PORT_STATE_CLOSE_DONE ///< Port close operation complete
+} PORT_STATE;
+
+//------------------------------------------------------------------------------
+// Data Types
+//------------------------------------------------------------------------------
+
+typedef struct _DT_PACKET DT_PACKET; ///< Forward declaration
+typedef struct _DT_PORT DT_PORT; ///< Forward declaration
+typedef struct _DT_SOCKET DT_SOCKET; ///< Forward declaration
+
+typedef struct
+{
+ EFI_TCP4_RECEIVE_DATA RxData; ///< Receive operation description
+ size_t ValidBytes; ///< Length of valid data in bytes
+ UINT8 * pBuffer; ///< Current data pointer
+ UINT8 Buffer [ RX_PACKET_DATA ]; ///< Data buffer
+} DT_TCP4_RX_DATA;
+
+typedef struct
+{
+ EFI_TCP4_TRANSMIT_DATA TxData; ///< Transmit operation description
+ UINT8 Buffer [ 1 ]; ///< Data buffer
+} DT_TCP4_TX_DATA;
+
+typedef struct
+{
+ EFI_UDP4_SESSION_DATA Session; ///< * Remote network address
+ EFI_UDP4_RECEIVE_DATA * pRxData; ///< * Receive operation description
+} DT_UDP4_RX_DATA;
+
+typedef struct
+{
+ EFI_UDP4_SESSION_DATA Session; ///< Remote network address
+ EFI_UDP4_TRANSMIT_DATA TxData; ///< Transmit operation description
+ UINT8 Buffer [ 1 ]; ///< Data buffer
+} DT_UDP4_TX_DATA;
+
+typedef struct _DT_PACKET {
+ DT_PACKET * pNext; ///< Next packet in the receive list
+ size_t PacketSize; ///< Size of this data structure
+ union {
+ DT_TCP4_RX_DATA Tcp4Rx; ///< Receive operation description
+ DT_TCP4_TX_DATA Tcp4Tx; ///< Transmit operation description
+ DT_UDP4_RX_DATA Udp4Rx; ///< Receive operation description
+ DT_UDP4_TX_DATA Udp4Tx; ///< Transmit operation description
+ } Op;
+} GCC_DT_PACKET;
+
+/**
+ Service control structure
+
+ The driver uses this structure to manage the network devices.
+**/
+typedef struct _DT_SERVICE {
+ UINTN Signature; ///< Structure identification
+
+ //
+ // Links
+ //
+ DT_SERVICE * pNext; ///< Next service in the service list
+
+ //
+ // Service data
+ //
+ CONST DT_SOCKET_BINDING * pSocketBinding; ///< Name and shutdown routine
+ EFI_HANDLE Controller; ///< Controller for the service
+ VOID * pInterface; ///< Network layer service binding interface
+
+ //
+ // Network data
+ //
+ NETWORK_TYPE NetworkType; ///< Type of network service
+ DT_PORT * pPortList; ///< List of ports using this service
+}GCC_DT_SERVICE;
+
+/**
+ Start the close operation on a TCP4 port.
+
+ @param [in] pPort Address of the port structure.
+ @param [in] bAbort Set TRUE to abort active transfers
+ @param [in] DebugFlags Flags for debug messages
+
+**/
+typedef
+EFI_STATUS
+PFN_PORT_CLOSE_START (
+ IN DT_PORT * pPort,
+ IN BOOLEAN bAbort,
+ IN UINTN DebugFlags
+ );
+
+/**
+ TCP4 context structure
+
+ The driver uses this structure to manage the TCP4 connections.
+**/
+typedef struct {
+ //
+ // TCP4 context
+ //
+ EFI_HANDLE Handle; ///< TCP4 port handle
+ EFI_TCP4_PROTOCOL * pProtocol; ///< TCP4 protocol pointer
+ EFI_TCP4_CONFIG_DATA ConfigData; ///< TCP4 configuration data
+ EFI_TCP4_OPTION Option; ///< TCP4 port options
+ BOOLEAN bConfigured; ///< TRUE if configuration was successful
+
+ //
+ // Tokens
+ //
+ EFI_TCP4_LISTEN_TOKEN ListenToken; ///< Listen control
+ EFI_TCP4_CONNECTION_TOKEN ConnectToken; ///< Connection control
+ EFI_TCP4_CLOSE_TOKEN CloseToken; ///< Close control
+
+ //
+ // Receive data management
+ //
+ EFI_TCP4_IO_TOKEN RxToken; ///< Receive token
+ DT_PACKET * pReceivePending; ///< Receive operation in progress
+
+ //
+ // Transmit data management
+ //
+ EFI_TCP4_IO_TOKEN TxOobToken; ///< Urgent data token
+ DT_PACKET * pTxOobPacket; ///< Urgent data in progress
+
+ EFI_TCP4_IO_TOKEN TxToken; ///< Normal data token
+ DT_PACKET * pTxPacket; ///< Normal transmit in progress
+} DT_TCP4_CONTEXT;
+
+/**
+ UDP4 context structure
+
+ The driver uses this structure to manage the UDP4 connections.
+**/
+typedef struct {
+ //
+ // UDP4 context
+ //
+ EFI_HANDLE Handle; ///< UDP4 port handle
+ EFI_UDP4_PROTOCOL * pProtocol; ///< UDP4 protocol pointer
+ EFI_UDP4_CONFIG_DATA ConfigData; ///< UDP4 configuration data
+ BOOLEAN bConfigured; ///< TRUE if configuration was successful
+
+ //
+ // Receive data management
+ //
+ EFI_UDP4_COMPLETION_TOKEN RxToken;///< Receive token
+ DT_PACKET * pReceivePending; ///< Receive operation in progress
+
+ //
+ // Transmit data management
+ //
+ EFI_UDP4_COMPLETION_TOKEN TxToken;///< Transmit token
+ DT_PACKET * pTxPacket; ///< Transmit in progress
+} DT_UDP4_CONTEXT;
+
+
+/**
+ Port control structure
+
+ The driver uses this structure to manager the socket's connection
+ with the network driver.
+**/
+typedef struct _DT_PORT {
+ UINTN Signature; ///< Structure identification
+
+ //
+ // List links
+ //
+ DT_PORT * pLinkService; ///< Link in service port list
+ DT_PORT * pLinkSocket; ///< Link in socket port list
+
+ //
+ // Structures
+ //
+ DT_SERVICE * pService; ///< Service for this port
+ DT_SOCKET * pSocket; ///< Socket for this port
+// PFN_CLOSE_PORT pfnClosePort; ///< Routine to immediately close the port
+ PFN_PORT_CLOSE_START * pfnCloseStart; ///< Routine to start closing the port
+
+ //
+ // Protocol specific management data
+ //
+ PORT_STATE State; ///< State of the port
+ UINTN DebugFlags; ///< Debug flags used to close the port
+ BOOLEAN bCloseNow; ///< TRUE = Close the port immediately
+
+ union {
+ DT_TCP4_CONTEXT Tcp4; ///< TCPv4 management data
+ DT_UDP4_CONTEXT Udp4; ///< UDPv4 management data
+ } Context;
+}GCC_DT_PORT;
+
+/**
+ Socket control structure
+
+ The driver uses this structure to manage the socket.
+**/
+typedef struct _DT_SOCKET {
+ UINTN Signature; ///< Structure identification
+
+ //
+ // Protocol binding
+ //
+ EFI_SOCKET_PROTOCOL SocketProtocol; ///< Socket protocol declaration
+
+ //
+ // Socket management
+ //
+ DT_SOCKET * pNext; ///< Next socket in the list of sockets
+ int errno; ///< Error information for this socket
+ EFI_STATUS Status; ///< Asyncronous error information for this socket
+ SOCKET_STATE State; ///< Socket state
+
+ //
+ // Socket data
+ //
+ int Domain; ///< Specifies family of protocols
+ int Type; ///< Specifies how to make network connection
+ int Protocol; ///< Specifies lower layer protocol to use
+ BOOLEAN bConfigured; ///< Set after the socket is configured
+
+ BOOLEAN bRxDisable; ///< Receive disabled via shutdown
+ size_t RxBytes; ///< Total Rx bytes
+ size_t RxOobBytes; ///< Urgent Rx bytes
+ EFI_STATUS RxError; ///< Error during receive
+
+ BOOLEAN bTxDisable; ///< Transmit disabled via shutdown
+ size_t TxBytes; ///< Normal Tx bytes
+ size_t TxOobBytes; ///< Urgent Tx bytes
+ EFI_STATUS TxError; ///< Error during transmit
+
+ //
+ // Pending connection data
+ //
+ BOOLEAN bConnected; ///< Set when connected, cleared by poll
+ EFI_STATUS ConnectStatus; ///< Connection status
+ UINTN MaxFifoDepth; ///< Maximum FIFO depth
+ UINTN FifoDepth; ///< Number of sockets in the FIFO
+ DT_SOCKET * pFifoHead; ///< Head of the FIFO
+ DT_SOCKET * pFifoTail; ///< Tail of the FIFO
+ DT_SOCKET * pNextConnection; ///< Link in the FIFO
+
+ //
+ // Network use
+ //
+ DT_PORT * pPortList; ///< List of ports managed by this socket
+ EFI_EVENT WaitAccept; ///< Wait for accept completion
+
+ //
+ // Receive data management
+ //
+ UINT32 MaxRxBuf; ///< Maximum size of the receive buffer
+ struct timeval RxTimeout; ///< Receive timeout
+ DT_PACKET * pRxFree; ///< Free packet list
+ DT_PACKET * pRxOobPacketListHead; ///< Urgent data list head
+ DT_PACKET * pRxOobPacketListTail; ///< Urgent data list tail
+ DT_PACKET * pRxPacketListHead; ///< Normal data list head
+ DT_PACKET * pRxPacketListTail; ///< Normal data list tail
+
+ //
+ // Transmit data management
+ //
+ UINT32 MaxTxBuf; ///< Maximum size of the transmit buffer
+ DT_PACKET * pTxOobPacketListHead; ///< Urgent data list head
+ DT_PACKET * pTxOobPacketListTail; ///< Urgent data list tail
+ DT_PACKET * pTxPacketListHead; ///< Normal data list head
+ DT_PACKET * pTxPacketListTail; ///< Normal data list tail
+}GCC_DT_SOCKET;
+
+#define SOCKET_FROM_PROTOCOL(a) CR(a, DT_SOCKET, SocketProtocol, SOCKET_SIGNATURE) ///< Locate DT_SOCKET from protocol
+
+/**
+ Socket layer control structure
+
+ The driver uses this structure to manage the driver.
+**/
+typedef struct {
+ UINTN Signature; ///< Structure identification
+
+ //
+ // Service binding interface
+ //
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;///< Driver's binding
+
+ //
+ // Image data
+ //
+ EFI_HANDLE ImageHandle; ///< Image handle
+
+ //
+ // Network services
+ //
+ DT_SERVICE * pTcp4List; ///< List of Tcp4 services
+ DT_SERVICE * pUdp4List; ///< List of Udp4 services
+
+ //
+ // Socket management
+ //
+ DT_SOCKET * pSocketList; ///< List of sockets
+
+ //
+ // TCP4 service
+ //
+ UINTN TcpCloseMax4; ///< Number of entries in the ring buffer
+ UINTN TcpCloseIn4; ///< Offset into TcpClose4 ring buffer - Close request
+ UINTN TcpCloseOut4; ///< Offset into TcpClose4 ring buffer - Close operation
+ EFI_TCP4_PROTOCOL ** ppTcpClose4; ///< Ring buffer to close TCP4 ports
+} DT_LAYER;
+
+#define LAYER_FROM_SERVICE(a) CR(a, DT_LAYER, ServiceBinding, LAYER_SIGNATURE) ///< Locate DT_LAYER from service binding
+
+//------------------------------------------------------------------------------
+// Data
+//------------------------------------------------------------------------------
+
+extern DT_LAYER mEslLayer;
+
+//------------------------------------------------------------------------------
+// Socket Support Routines
+//------------------------------------------------------------------------------
+
+/**
+ Allocate and initialize a DT_SOCKET structure.
+
+ The ::SocketAllocate() function allocates a DT_SOCKET structure
+ and installs a protocol on ChildHandle. If pChildHandle is a
+ pointer to NULL, then a new handle is created and returned in
+ pChildHandle. If pChildHandle is not a pointer to NULL, then
+ the protocol installs on the existing pChildHandle.
+
+ @param [in, out] pChildHandle Pointer to the handle of the child to create.
+ If it is NULL, then a new handle is created.
+ If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI
+ handle.
+ @param [in] DebugFlags Flags for debug messages
+ @param [in, out] ppSocket The buffer to receive the DT_SOCKET structure address.
+
+ @retval EFI_SUCCESS The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+EslSocketAllocate (
+ IN OUT EFI_HANDLE * pChildHandle,
+ IN UINTN DebugFlags,
+ IN OUT DT_SOCKET ** ppSocket
+ );
+
+/**
+ Allocate a packet for a receive or transmit operation
+
+ @param [in] ppPacket Address to receive the DT_PACKET structure
+ @param [in] LengthInBytes Length of the packet structure
+ @param [in] DebugFlags Flags for debug messages
+
+ @retval EFI_SUCCESS - The packet was allocated successfully
+
+ **/
+EFI_STATUS
+EslSocketPacketAllocate (
+ IN DT_PACKET ** ppPacket,
+ IN size_t LengthInBytes,
+ IN UINTN DebugFlags
+ );
+
+/**
+ Free a packet used for receive or transmit operation
+
+ @param [in] pPacket Address of the DT_PACKET structure
+ @param [in] DebugFlags Flags for debug messages
+
+ @retval EFI_SUCCESS - The packet was allocated successfully
+
+ **/
+EFI_STATUS
+EslSocketPacketFree (
+ IN DT_PACKET * pPacket,
+ IN UINTN DebugFlags
+ );
+
+//------------------------------------------------------------------------------
+// Tcp4 Routines
+//------------------------------------------------------------------------------
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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 listeen completion event
+
+ @param pPort The DT_PORT structure address
+
+**/
+VOID
+EslTcpListenComplete4 (
+ IN EFI_EVENT Event,
+ IN DT_PORT * pPort
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ Start the close operation on a TCP4 port.
+
+ @param [in] pPort Address of the port structure.
+ @param [in] bAbort Set TRUE to abort active transfers
+ @param [in] DebugFlags Flags for debug messages
+
+**/
+EFI_STATUS
+EslTcpPortCloseStart4 (
+ IN DT_PORT * pPort,
+ IN BOOLEAN bAbort,
+ IN UINTN DebugFlags
+ );
+
+/**
+ Port close state 2
+
+ Continue the close operation after the transmission is complete.
+
+ @param [in] pPort Address of the port structure.
+
+ @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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ Start a receive operation
+
+ @param [in] pPort Address of the DT_PORT structure.
+
+ **/
+VOID
+EslTcpRxStart4 (
+ IN DT_PORT * pPort
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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.
+
+ @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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+//------------------------------------------------------------------------------
+// Udp4 Routines
+//------------------------------------------------------------------------------
+
+/**
+ Bind a name to a socket.
+
+ The ::UdpBind4 routine connects a name to a UDP4 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
+EslUdpBind4 (
+ IN DT_SOCKET * pSocket,
+ IN const struct sockaddr * pSockAddr,
+ IN socklen_t SockAddrLength
+ );
+
+/**
+ Initialize the UDP4 service.
+
+ This routine initializes the UDP4 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
+EslUdpInitialize4 (
+ IN DT_SERVICE * pService
+ );
+
+/**
+ 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 Udp4 child handle
+ @param [in] pIpAddress Buffer containing IP4 network address of the local host
+ @param [in] PortNumber Udp4 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
+EslUdpPortAllocate4 (
+ 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
+ );
+
+/**
+ Close a UDP4 port.
+
+ This routine releases the resources allocated by
+ ::UdpPortAllocate4().
+
+ @param [in] pPort Address of the port structure.
+
+ @retval EFI_SUCCESS The port is closed
+ @retval other Port close error
+
+**/
+EFI_STATUS
+EslUdpPortClose4 (
+ IN DT_PORT * pPort
+ );
+
+/**
+ Start the close operation on a UDP4 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
+EslUdpPortCloseStart4 (
+ IN DT_PORT * pPort,
+ IN BOOLEAN bCloseNow,
+ IN UINTN DebugFlags
+ );
+
+/**
+ 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
+EslUdpPortCloseTxDone4 (
+ IN DT_PORT * pPort
+ );
+
+/**
+ Connect to a remote system via the network.
+
+ The ::UdpConnectStart4= routine sets the remote address for the connection.
+
+ @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
+EslUdpConnect4 (
+ IN DT_SOCKET * pSocket,
+ IN const struct sockaddr * pSockAddr,
+ IN socklen_t SockAddrLength
+ );
+
+/**
+ 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
+EslUdpGetLocalAddress4 (
+ IN DT_SOCKET * pSocket,
+ OUT struct sockaddr * pAddress,
+ IN OUT socklen_t * pAddressLength
+ );
+
+/**
+ 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
+EslUdpGetRemoteAddress4 (
+ IN DT_SOCKET * pSocket,
+ OUT struct sockaddr * pAddress,
+ IN OUT socklen_t * pAddressLength
+ );
+
+/**
+ Receive data from a network connection.
+
+ To minimize the number of buffer copies, the ::UdpRxComplete4
+ routine queues the UDP4 driver's buffer to a list of datagrams
+ waiting to be received. The socket driver holds on to the
+ buffers from the UDP4 driver until the application layer requests
+ the data or the socket is closed.
+
+ The application calls this routine in the socket layer to
+ receive datagrams from one or more remote systems. This routine
+ removes the next available datagram from the list of datagrams
+ and copies the data from the UDP4 driver's buffer into the
+ application's buffer. The UDP4 driver's buffer is then returned.
+
+ @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
+EslUdpReceive4 (
+ 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
+ );
+
+/**
+ Cancel the receive operations
+
+ @param [in] pSocket Address of a DT_SOCKET structure
+
+ @retval EFI_SUCCESS - The cancel was successful
+
+ **/
+EFI_STATUS
+EslUdpRxCancel4 (
+ IN DT_SOCKET * pSocket
+ );
+
+/**
+ Process the receive completion
+
+ Keep the UDP4 driver's buffer and append it to the list of
+ datagrams for the application to receive. The UDP4 driver's
+ buffer will be returned by either ::UdpReceive4 or
+ ::UdpPortCloseTxDone4.
+
+ @param Event The receive completion event
+
+ @param pPort The DT_PORT structure address
+
+**/
+VOID
+EslUdpRxComplete4 (
+ IN EFI_EVENT Event,
+ IN DT_PORT * pPort
+ );
+
+/**
+ Start a receive operation
+
+ @param [in] pPort Address of the DT_PORT structure.
+
+ **/
+VOID
+EslUdpRxStart4 (
+ IN DT_PORT * pPort
+ );
+
+/**
+ 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
+ EslUdpSocketIsConfigured4 (
+ IN DT_SOCKET * pSocket
+ );
+
+/**
+ Process the transmit completion
+
+ @param Event The normal transmit completion event
+
+ @param pPort The DT_PORT structure address
+
+**/
+VOID
+EslUdpTxComplete4 (
+ IN EFI_EVENT Event,
+ IN DT_PORT * pPort
+ );
+
+/**
+ Shutdown the UDP4 service.
+
+ This routine undoes the work performed by ::UdpInitialize4.
+
+ @param [in] pService DT_SERVICE structure address
+
+**/
+VOID
+EFIAPI
+EslUdpShutdown4 (
+ IN DT_SERVICE * pService
+ );
+
+/**
+ Buffer data for transmission over a network connection.
+
+ This routine is called by the socket layer API to buffer
+ data for transmission. The data is copied into a local buffer
+ freeing the application buffer for reuse upon return. When
+ necessary, this routine will start the transmit engine that
+ performs the data transmission on the network connection. The
+ transmit engine transmits the data a packet at a time over the
+ network connection.
+
+ Transmission errors are returned during the next transmission or
+ during the close operation. Only buffering errors are returned
+ during the current transmission attempt.
+
+ @param [in] pSocket Address of 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 [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
+EslUdpTxBuffer4 (
+ IN DT_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
+ );
+
+/**
+ Transmit data using a network connection.
+
+ @param [in] pPort Address of a DT_PORT structure
+
+ **/
+VOID
+EslUdpTxStart4 (
+ IN DT_PORT * pPort
+ );
+
+//------------------------------------------------------------------------------
+
+#endif // _SOCKET_H_
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 ( );
+}
diff --git a/StdLib/EfiSocketLib/Udp4.c b/StdLib/EfiSocketLib/Udp4.c new file mode 100644 index 0000000000..95fc66517a --- /dev/null +++ b/StdLib/EfiSocketLib/Udp4.c @@ -0,0 +1,2408 @@ +/** @file
+ Implement the UDP4 driver support for the socket layer.
+
+ Copyright (c) 2011, Intel Corporation
+ All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Socket.h"
+
+
+/**
+ Bind a name to a socket.
+
+ The ::UdpBind4 routine connects a name to a UDP4 stack on the local machine.
+
+ The configure call to the UDP4 driver occurs on the first poll, recv, recvfrom,
+ send or sentto call. Until then, all changes are made in the local UDP context
+ structure.
+
+ @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
+EslUdpBind4 (
+ 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 * pUdp4Service;
+ 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->pUdp4List;
+ while ( NULL != pService ) {
+
+ //
+ // Create the UDP port
+ //
+ pUdp4Service = pService->pInterface;
+ ChildHandle = NULL;
+ Status = pUdp4Service->CreateChild ( pUdp4Service,
+ &ChildHandle );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_BIND | DEBUG_POOL,
+ "0x%08x: Udp4 port handle created\r\n",
+ ChildHandle ));
+
+ //
+ // Open the port
+ //
+ Status = EslUdpPortAllocate4 ( 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 Udp4 port handle, Status: %r\r\n",
+ Status ));
+ ChildHandle = NULL;
+ }
+
+ //
+ // Close the port if necessary
+ //
+ if (( EFI_ERROR ( Status )) && ( NULL != ChildHandle )) {
+ TempStatus = pUdp4Service->DestroyChild ( pUdp4Service,
+ ChildHandle );
+ if ( !EFI_ERROR ( TempStatus )) {
+ DEBUG (( DEBUG_BIND | DEBUG_POOL,
+ "0x%08x: Udp4 port handle destroyed\r\n",
+ ChildHandle ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_BIND | DEBUG_POOL,
+ "ERROR - Failed to destroy the Udp4 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 Udp4 address length: %d\r\n",
+ SockAddrLength ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EINVAL;
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Initialize the UDP4 service.
+
+ This routine initializes the UDP4 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
+EslUdpInitialize4 (
+ IN DT_SERVICE * pService
+ )
+{
+ DT_LAYER * pLayer;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Identify the service
+ //
+ pService->NetworkType = NETWORK_TYPE_UDP4;
+
+ //
+ // Connect this service to the service list
+ //
+ pLayer = &mEslLayer;
+ pService->pNext = pLayer->pUdp4List;
+ pLayer->pUdp4List = pService;
+
+ //
+ // Assume the list is empty
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Return the initialization status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ 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 Udp4 child handle
+ @param [in] pIpAddress Buffer containing IP4 network address of the local host
+ @param [in] PortNumber Udp4 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
+EslUdpPortAllocate4 (
+ 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_UDP4_CONFIG_DATA * pConfig;
+ DT_LAYER * pLayer;
+ DT_PORT * pPort;
+ DT_UDP4_CONTEXT * pUdp4;
+ 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 = EslUdpPortCloseStart4;
+ pPort->DebugFlags = DebugFlags;
+
+ //
+ // Allocate the receive event
+ //
+ pUdp4 = &pPort->Context.Udp4;
+ Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,
+ TPL_SOCKETS,
+ (EFI_EVENT_NOTIFY)EslUdpRxComplete4,
+ pPort,
+ &pUdp4->RxToken.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",
+ pUdp4->RxToken.Event ));
+
+ //
+ // Allocate the transmit event
+ //
+ Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,
+ TPL_SOCKETS,
+ (EFI_EVENT_NOTIFY)EslUdpTxComplete4,
+ pPort,
+ &pUdp4->TxToken.Event);
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DebugFlags,
+ "ERROR - Failed to create the transmit event, Status: %r\r\n",
+ Status ));
+ pSocket->errno = ENOMEM;
+ break;
+ }
+ DEBUG (( DEBUG_CLOSE | DEBUG_POOL,
+ "0x%08x: Created transmit event\r\n",
+ pUdp4->TxToken.Event ));
+
+ //
+ // Open the port protocol
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &pUdp4->pProtocol,
+ pLayer->ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DebugFlags,
+ "ERROR - Failed to open gEfiUdp4ProtocolGuid on controller 0x%08x\r\n",
+ pUdp4->Handle ));
+ pSocket->errno = EEXIST;
+ break;
+ }
+ DEBUG (( DebugFlags,
+ "0x%08x: gEfiUdp4ProtocolGuid opened on controller 0x%08x\r\n",
+ pUdp4->pProtocol,
+ ChildHandle ));
+
+ //
+ // Set the port address
+ //
+ pUdp4->Handle = ChildHandle;
+ pConfig = &pPort->Context.Udp4.ConfigData;
+ pConfig->StationPort = PortNumber;
+ if (( 0 == pIpAddress[0])
+ && ( 0 == pIpAddress[1])
+ && ( 0 == pIpAddress[2])
+ && ( 0 == pIpAddress[3])) {
+ pConfig->UseDefaultAddress = TRUE;
+ }
+ else {
+ pConfig->StationAddress.Addr[0] = pIpAddress[0];
+ pConfig->StationAddress.Addr[1] = pIpAddress[1];
+ pConfig->StationAddress.Addr[2] = pIpAddress[2];
+ pConfig->StationAddress.Addr[3] = pIpAddress[3];
+ pConfig->SubnetMask.Addr[0] = 0xff;
+ pConfig->SubnetMask.Addr[1] = 0xff;
+ pConfig->SubnetMask.Addr[2] = 0xff;
+ pConfig->SubnetMask.Addr[3] = 0xff;
+ }
+ pConfig->TimeToLive = 255;
+ pConfig->AcceptAnyPort = FALSE;
+ pConfig->AcceptBroadcast = FALSE;
+ pConfig->AcceptPromiscuous = FALSE;
+ pConfig->AllowDuplicatePort = TRUE;
+ pConfig->DoNotFragment = TRUE;
+
+ //
+ // 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
+ //
+ EslUdpPortClose4 ( pPort );
+ }
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Close a UDP4 port.
+
+ This routine releases the resources allocated by
+ ::UdpPortAllocate4().
+
+ @param [in] pPort Address of the port structure.
+
+ @retval EFI_SUCCESS The port is closed
+ @retval other Port close error
+
+**/
+EFI_STATUS
+EslUdpPortClose4 (
+ 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 * pUdp4Service;
+ DT_UDP4_CONTEXT * pUdp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Verify the socket layer synchronization
+ //
+ VERIFY_TPL ( TPL_SOCKETS );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+ pSocket = pPort->pSocket;
+ pSocket->errno = 0;
+
+ //
+ // Locate the port in the socket list
+ //
+ pLayer = &mEslLayer;
+ DebugFlags = pPort->DebugFlags;
+ 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 receive queue
+ //
+ ASSERT ( NULL == pSocket->pRxPacketListHead );
+ ASSERT ( NULL == pSocket->pRxPacketListTail );
+ 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 receive event
+ //
+ pUdp4 = &pPort->Context.Udp4;
+ if ( NULL != pUdp4->RxToken.Event ) {
+ Status = gBS->CloseEvent ( pUdp4->RxToken.Event );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Closed receive event\r\n",
+ pUdp4->RxToken.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 transmit event
+ //
+ if ( NULL != pUdp4->TxToken.Event ) {
+ Status = gBS->CloseEvent ( pUdp4->TxToken.Event );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Closed normal transmit event\r\n",
+ pUdp4->TxToken.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 UDP protocol
+ //
+ pUdp4Service = pService->pInterface;
+ if ( NULL != pUdp4->pProtocol ) {
+ Status = gBS->CloseProtocol ( pUdp4->Handle,
+ &gEfiUdp4ProtocolGuid,
+ pLayer->ImageHandle,
+ NULL );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags,
+ "0x%08x: gEfiUdp4ProtocolGuid closed on controller 0x%08x\r\n",
+ pUdp4->pProtocol,
+ pUdp4->Handle ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DebugFlags,
+ "ERROR - Failed to close gEfiUdp4ProtocolGuid opened on controller 0x%08x, Status: %r\r\n",
+ pUdp4->Handle,
+ Status ));
+ ASSERT ( EFI_SUCCESS == Status );
+ }
+ }
+
+ //
+ // Done with the UDP port
+ //
+ if ( NULL != pUdp4->Handle ) {
+ Status = pUdp4Service->DestroyChild ( pUdp4Service,
+ pUdp4->Handle );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DebugFlags | DEBUG_POOL,
+ "0x%08x: Udp4 port handle destroyed\r\n",
+ pUdp4->Handle ));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DebugFlags | DEBUG_POOL,
+ "ERROR - Failed to destroy the Udp4 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 );
+ }
+
+ //
+ // Mark the socket as closed if necessary
+ //
+ if ( NULL == pSocket->pPortList ) {
+ pSocket->State = SOCKET_STATE_CLOSED;
+ DEBUG (( DEBUG_CLOSE | DEBUG_INFO,
+ "0x%08x: Socket State: SOCKET_STATE_CLOSED\r\n",
+ pSocket ));
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Start the close operation on a UDP4 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
+EslUdpPortCloseStart4 (
+ 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 = EslUdpPortCloseTxDone4 ( 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
+EslUdpPortCloseRxDone4 (
+ IN DT_PORT * pPort
+ )
+{
+ PORT_STATE PortState;
+ DT_SOCKET * pSocket;
+ DT_UDP4_CONTEXT * pUdp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Verify the socket layer synchronization
+ //
+ VERIFY_TPL ( TPL_SOCKETS );
+
+ //
+ // Verify that the port is closing
+ //
+ Status = EFI_ALREADY_STARTED;
+ pSocket = pPort->pSocket;
+ pSocket->errno = EALREADY;
+ 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;
+ pSocket->errno = EAGAIN;
+ pUdp4 = &pPort->Context.Udp4;
+ if ( NULL == pUdp4->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 ));
+
+ //
+ // The close operation has completed
+ // Release the port resources
+ //
+ Status = EslUdpPortClose4 ( 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
+EslUdpPortCloseTxDone4 (
+ IN DT_PORT * pPort
+ )
+{
+ DT_PACKET * pPacket;
+ DT_SOCKET * pSocket;
+ DT_UDP4_CONTEXT * pUdp4;
+ EFI_UDP4_PROTOCOL * pUdp4Protocol;
+ 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->TxBytes )) {
+ //
+ // Start the close operation on the port
+ //
+ pUdp4 = &pPort->Context.Udp4;
+ pUdp4Protocol = pUdp4->pProtocol;
+ if ( !pUdp4->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 {
+ //
+ // 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 ));
+
+ //
+ // Empty the receive queue
+ //
+ while ( NULL != pSocket->pRxPacketListHead ) {
+ pPacket = pSocket->pRxPacketListHead;
+ pSocket->pRxPacketListHead = pPacket->pNext;
+ pSocket->RxBytes -= pPacket->Op.Udp4Rx.pRxData->DataLength;
+
+ //
+ // Return the buffer to the UDP4 driver
+ //
+ gBS->SignalEvent ( pPacket->Op.Udp4Rx.pRxData->RecycleSignal );
+
+ //
+ // Done with this packet
+ //
+ EslSocketPacketFree ( pPacket, DEBUG_RX );
+ }
+ pSocket->pRxPacketListTail = NULL;
+ ASSERT ( 0 == pSocket->RxBytes );
+
+ //
+ // Reset the port, cancel the outstanding receive
+ //
+ Status = pUdp4Protocol->Configure ( pUdp4Protocol,
+ NULL );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO,
+ "0x%08x: Port reset\r\n",
+ pPort ));
+
+ //
+ // Free the receive packet
+ //
+ Status = gBS->CheckEvent ( pUdp4->RxToken.Event );
+ if ( EFI_SUCCESS != Status ) {
+ EslSocketPacketFree ( pUdp4->pReceivePending, DEBUG_CLOSE );
+ pUdp4->pReceivePending = NULL;
+ Status = EFI_SUCCESS;
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO,
+ "ERROR - Port 0x%08x reset failed, Status: %r\r\n",
+ pPort,
+ Status ));
+ ASSERT ( EFI_SUCCESS == Status );
+ }
+ }
+
+ //
+ // Determine if the receive operation is pending
+ //
+ if ( !EFI_ERROR ( Status )) {
+ Status = EslUdpPortCloseRxDone4 ( 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;
+}
+
+
+/**
+ Connect to a remote system via the network.
+
+ The ::UdpConnectStart4= routine sets the remote address for the connection.
+
+ @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
+EslUdpConnect4 (
+ IN DT_SOCKET * pSocket,
+ IN const struct sockaddr * pSockAddr,
+ IN socklen_t SockAddrLength
+ )
+{
+ struct sockaddr_in LocalAddress;
+ DT_PORT * pPort;
+ struct sockaddr_in * pRemoteAddress;
+ DT_UDP4_CONTEXT * pUdp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume failure
+ //
+ Status = EFI_NETWORK_UNREACHABLE;
+ pSocket->errno = ENETUNREACH;
+
+ //
+ // Get the address
+ //
+ pRemoteAddress = (struct sockaddr_in *)pSockAddr;
+
+ //
+ // Validate the address length
+ //
+ if ( SockAddrLength >= ( sizeof ( *pRemoteAddress )
+ - sizeof ( pRemoteAddress->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 );
+ }
+
+ //
+ // Walk the list of ports
+ //
+ pPort = pSocket->pPortList;
+ while ( NULL != pPort ) {
+ //
+ // Set the remote address
+ //
+ pUdp4 = &pPort->Context.Udp4;
+ pUdp4->ConfigData.RemoteAddress.Addr[0] = (UINT8)( pRemoteAddress->sin_addr.s_addr );
+ pUdp4->ConfigData.RemoteAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 );
+ pUdp4->ConfigData.RemoteAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 );
+ pUdp4->ConfigData.RemoteAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 );
+ pUdp4->ConfigData.RemotePort = SwapBytes16 ( pRemoteAddress->sin_port );
+
+ //
+ // At least one path exists
+ //
+ Status = EFI_SUCCESS;
+ pSocket->errno = 0;
+
+ //
+ // Set the next port
+ //
+ pPort = pPort->pLinkSocket;
+ }
+ }
+ else {
+ DEBUG (( DEBUG_CONNECT,
+ "ERROR - Invalid UDP4 address length: %d\r\n",
+ SockAddrLength ));
+ Status = EFI_INVALID_PARAMETER;
+ pSocket->errno = EINVAL;
+ }
+
+ //
+ // Return the connect 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
+EslUdpGetLocalAddress4 (
+ IN DT_SOCKET * pSocket,
+ OUT struct sockaddr * pAddress,
+ IN OUT socklen_t * pAddressLength
+ )
+{
+ socklen_t LengthInBytes;
+ DT_PORT * pPort;
+ struct sockaddr_in * pLocalAddress;
+ DT_UDP4_CONTEXT * pUdp4;
+ 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
+ //
+ pUdp4 = &pPort->Context.Udp4;
+ pLocalAddress = (struct sockaddr_in *)pAddress;
+ ZeroMem ( pLocalAddress, LengthInBytes );
+ pLocalAddress->sin_family = AF_INET;
+ pLocalAddress->sin_len = (uint8_t)LengthInBytes;
+ pLocalAddress->sin_port = SwapBytes16 ( pUdp4->ConfigData.StationPort );
+ CopyMem ( &pLocalAddress->sin_addr,
+ &pUdp4->ConfigData.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
+EslUdpGetRemoteAddress4 (
+ IN DT_SOCKET * pSocket,
+ OUT struct sockaddr * pAddress,
+ IN OUT socklen_t * pAddressLength
+ )
+{
+ socklen_t LengthInBytes;
+ DT_PORT * pPort;
+ struct sockaddr_in * pRemoteAddress;
+ DT_UDP4_CONTEXT * pUdp4;
+ 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
+ //
+ pUdp4 = &pPort->Context.Udp4;
+ pRemoteAddress = (struct sockaddr_in *)pAddress;
+ ZeroMem ( pRemoteAddress, LengthInBytes );
+ pRemoteAddress->sin_family = AF_INET;
+ pRemoteAddress->sin_len = (uint8_t)LengthInBytes;
+ pRemoteAddress->sin_port = SwapBytes16 ( pUdp4->ConfigData.RemotePort );
+ CopyMem ( &pRemoteAddress->sin_addr,
+ &pUdp4->ConfigData.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;
+}
+
+
+/**
+ Receive data from a network connection.
+
+ To minimize the number of buffer copies, the ::UdpRxComplete4
+ routine queues the UDP4 driver's buffer to a list of datagrams
+ waiting to be received. The socket driver holds on to the
+ buffers from the UDP4 driver until the application layer requests
+ the data or the socket is closed.
+
+ The application calls this routine in the socket layer to
+ receive datagrams from one or more remote systems. This routine
+ removes the next available datagram from the list of datagrams
+ and copies the data from the UDP4 driver's buffer into the
+ application's buffer. The UDP4 driver's buffer is then returned.
+
+ @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
+EslUdpReceive4 (
+ 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;
+ size_t DataBytes;
+ UINT32 Fragment;
+ in_addr_t IpAddress;
+ size_t LengthInBytes;
+ UINT8 * pData;
+ DT_PACKET * pPacket;
+ DT_PORT * pPort;
+ struct sockaddr_in * pRemoteAddress;
+ EFI_UDP4_RECEIVE_DATA * pRxData;
+ DT_UDP4_CONTEXT * pUdp4;
+ 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 if there is any data on the queue
+ //
+ pUdp4 = &pPort->Context.Udp4;
+ pPacket = pSocket->pRxPacketListHead;
+ if ( NULL != pPacket ) {
+ //
+ // Validate the return address parameters
+ //
+ pRxData = pPacket->Op.Udp4Rx.pRxData;
+ if (( NULL == pAddress ) || ( NULL != pAddressLength )) {
+ //
+ // Return the remote system address if requested
+ //
+ if ( NULL != pAddress ) {
+ //
+ // Build the remote address
+ //
+ DEBUG (( DEBUG_RX,
+ "Getting packet source address: %d.%d.%d.%d:%d\r\n",
+ pRxData->UdpSession.SourceAddress.Addr[0],
+ pRxData->UdpSession.SourceAddress.Addr[1],
+ pRxData->UdpSession.SourceAddress.Addr[2],
+ pRxData->UdpSession.SourceAddress.Addr[3],
+ pRxData->UdpSession.SourcePort ));
+ ZeroMem ( &RemoteAddress, sizeof ( RemoteAddress ));
+ RemoteAddress.sin_len = sizeof ( RemoteAddress );
+ RemoteAddress.sin_family = AF_INET;
+ IpAddress = pRxData->UdpSession.SourceAddress.Addr[3];
+ IpAddress <<= 8;
+ IpAddress |= pRxData->UdpSession.SourceAddress.Addr[2];
+ IpAddress <<= 8;
+ IpAddress |= pRxData->UdpSession.SourceAddress.Addr[1];
+ IpAddress <<= 8;
+ IpAddress |= pRxData->UdpSession.SourceAddress.Addr[0];
+ RemoteAddress.sin_addr.s_addr = IpAddress;
+ RemoteAddress.sin_port = SwapBytes16 ( pRxData->UdpSession.SourcePort );
+
+ //
+ // 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;
+ }
+
+ //
+ // Reduce the buffer length if necessary
+ //
+ DataBytes = pRxData->DataLength;
+ if ( DataBytes < BufferLength ) {
+ BufferLength = DataBytes;
+ }
+
+ //
+ // Copy the received data
+ //
+ LengthInBytes = 0;
+ Fragment = 0;
+ do {
+ //
+ // Determine the amount of received data
+ //
+ pData = pRxData->FragmentTable[Fragment].FragmentBuffer;
+ BytesToCopy = pRxData->FragmentTable[Fragment].FragmentLength;
+ 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, pData, BytesToCopy );
+ } while ( BufferLength > LengthInBytes );
+
+ //
+ // Determine if the data is being read
+ //
+ if ( 0 == ( Flags & MSG_PEEK )) {
+ //
+ // Display for the bytes consumed
+ //
+ DEBUG (( DEBUG_RX,
+ "0x%08x: Port account for 0x%08x bytes\r\n",
+ pPort,
+ BufferLength ));
+
+ //
+ // All done with this packet
+ // Account for any discarded data
+ //
+ pSocket->RxBytes -= DataBytes;
+ if ( 0 != ( DataBytes - BufferLength )) {
+ DEBUG (( DEBUG_RX,
+ "0x%08x: Port, packet read, skipping over 0x%08x bytes\r\n",
+ pPort,
+ DataBytes - BufferLength ));
+ }
+
+ //
+ // Remove this packet from the queue
+ //
+ pSocket->pRxPacketListHead = pPacket->pNext;
+ if ( NULL == pSocket->pRxPacketListHead ) {
+ pSocket->pRxPacketListTail = NULL;
+ }
+
+ //
+ // Return this packet to the UDP4 driver
+ //
+ gBS->SignalEvent ( pRxData->RecycleSignal );
+
+ //
+ // 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 == pUdp4->pReceivePending )
+ && ( MAX_RX_DATA > pSocket->RxBytes )) {
+ EslUdpRxStart4 ( pPort );
+ }
+ }
+
+ //
+ // 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 )) {
+ Status = pSocket->RxError;
+ switch ( Status ) {
+ default:
+ pSocket->errno = EIO;
+ 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
+EslUdpRxCancel4 (
+ IN DT_SOCKET * pSocket
+ )
+{
+ DT_PACKET * pPacket;
+ DT_PORT * pPort;
+ DT_UDP4_CONTEXT * pUdp4;
+ EFI_UDP4_PROTOCOL * pUdp4Protocol;
+ 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
+ //
+ pUdp4 = &pPort->Context.Udp4;
+ pPacket = pUdp4->pReceivePending;
+ if ( NULL != pPacket ) {
+ //
+ // Attempt to cancel the receive operation
+ //
+ pUdp4Protocol = pUdp4->pProtocol;
+ Status = pUdp4Protocol->Cancel ( pUdp4Protocol,
+ &pUdp4->RxToken );
+ 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
+
+ Keep the UDP4 driver's buffer and append it to the list of
+ datagrams for the application to receive. The UDP4 driver's
+ buffer will be returned by either ::UdpReceive4 or
+ ::UdpPortCloseTxDone4.
+
+ @param Event The receive completion event
+
+ @param pPort The DT_PORT structure address
+
+**/
+VOID
+EslUdpRxComplete4 (
+ IN EFI_EVENT Event,
+ IN DT_PORT * pPort
+ )
+{
+ size_t LengthInBytes;
+ DT_PACKET * pPacket;
+ DT_PACKET * pPrevious;
+ EFI_UDP4_RECEIVE_DATA * pRxData;
+ DT_SOCKET * pSocket;
+ DT_UDP4_CONTEXT * pUdp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Mark this receive complete
+ //
+ pUdp4 = &pPort->Context.Udp4;
+ pPacket = pUdp4->pReceivePending;
+ pUdp4->pReceivePending = NULL;
+
+ //
+ // Determine if this receive was successful
+ //
+ pSocket = pPort->pSocket;
+ Status = pUdp4->RxToken.Status;
+ if (( !EFI_ERROR ( Status )) && ( !pSocket->bRxDisable )) {
+ pRxData = pUdp4->RxToken.Packet.RxData;
+ if ( PORT_STATE_CLOSE_STARTED >= pPort->State ) {
+ //
+ // Save the data in the packet
+ //
+ pPacket->Op.Udp4Rx.pRxData = pRxData;
+
+ //
+ // Queue this packet
+ //
+ pPrevious = pSocket->pRxPacketListTail;
+ if ( NULL == pPrevious ) {
+ pSocket->pRxPacketListHead = pPacket;
+ }
+ else {
+ pPrevious->pNext = pPacket;
+ }
+ pSocket->pRxPacketListTail = pPacket;
+
+ //
+ // Account for the normal data
+ //
+ LengthInBytes = pRxData->DataLength;
+ pSocket->RxBytes += LengthInBytes;
+
+ //
+ // Log the received data
+ //
+ DEBUG (( DEBUG_RX | DEBUG_INFO,
+ "Received packet from: %d.%d.%d.%d:%d\r\n",
+ pRxData->UdpSession.SourceAddress.Addr[0],
+ pRxData->UdpSession.SourceAddress.Addr[1],
+ pRxData->UdpSession.SourceAddress.Addr[2],
+ pRxData->UdpSession.SourceAddress.Addr[3],
+ pRxData->UdpSession.SourcePort ));
+ DEBUG (( DEBUG_RX | DEBUG_INFO,
+ "Received packet sent to: %d.%d.%d.%d:%d\r\n",
+ pRxData->UdpSession.DestinationAddress.Addr[0],
+ pRxData->UdpSession.DestinationAddress.Addr[1],
+ pRxData->UdpSession.DestinationAddress.Addr[2],
+ pRxData->UdpSession.DestinationAddress.Addr[3],
+ pRxData->UdpSession.DestinationPort ));
+ DEBUG (( DEBUG_RX | DEBUG_INFO,
+ "0x%08x: Packet queued on port 0x%08x with 0x%08x bytes of data\r\n",
+ pPacket,
+ pPort,
+ LengthInBytes ));
+
+ //
+ // Attempt to restart this receive operation
+ //
+ if ( pSocket->MaxRxBuf > pSocket->RxBytes ) {
+ EslUdpRxStart4 ( pPort );
+ }
+ else {
+ DEBUG (( DEBUG_RX,
+ "0x%08x: Port RX suspended, 0x%08x bytes queued\r\n",
+ pPort,
+ pSocket->RxBytes ));
+ }
+ }
+ else {
+ //
+ // The port is being closed
+ // Return the buffer to the UDP4 driver
+ //
+ gBS->SignalEvent ( pRxData->RecycleSignal );
+
+ //
+ // Free the packet
+ //
+ EslSocketPacketFree ( pPacket, DEBUG_RX );
+ }
+ }
+ 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 ) {
+ EslUdpPortCloseRxDone4 ( 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
+EslUdpRxStart4 (
+ IN DT_PORT * pPort
+ )
+{
+ DT_PACKET * pPacket;
+ DT_SOCKET * pSocket;
+ DT_UDP4_CONTEXT * pUdp4;
+ EFI_UDP4_PROTOCOL * pUdp4Protocol;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Determine if a receive is already pending
+ //
+ Status = EFI_SUCCESS;
+ pPacket = NULL;
+ pSocket = pPort->pSocket;
+ pUdp4 = &pPort->Context.Udp4;
+ if ( !EFI_ERROR ( pPort->pSocket->RxError )) {
+ if (( NULL == pUdp4->pReceivePending )
+ && ( PORT_STATE_CLOSE_STARTED > pPort->State )) {
+ //
+ // Determine if there are any free packets
+ //
+ pPacket = pSocket->pRxFree;
+ 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.Udp4Rx ),
+ 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
+ //
+ pPacket->pNext = NULL;
+ pPacket->Op.Udp4Rx.pRxData = NULL;
+ pUdp4->RxToken.Packet.RxData = NULL;
+ pUdp4->pReceivePending = pPacket;
+
+ //
+ // Start the receive on the packet
+ //
+ pUdp4Protocol = pUdp4->pProtocol;
+ Status = pUdp4Protocol->Receive ( pUdp4Protocol,
+ &pUdp4->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 ));
+ if ( !EFI_ERROR ( pSocket->RxError )) {
+ //
+ // Save the error status
+ //
+ pSocket->RxError = Status;
+ }
+
+ //
+ // Free the packet
+ //
+ pUdp4->pReceivePending = NULL;
+ pPacket->pNext = pSocket->pRxFree;
+ pSocket->pRxFree = pPacket;
+ }
+ }
+ }
+ }
+
+ DBG_EXIT ( );
+}
+
+
+/**
+ Shutdown the UDP4 service.
+
+ This routine undoes the work performed by ::UdpInitialize4.
+
+ @param [in] pService DT_SERVICE structure address
+
+**/
+VOID
+EFIAPI
+EslUdpShutdown4 (
+ 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, 0 );
+ }
+ } while ( NULL != pPort );
+
+ //
+ // Remove the service from the service list
+ //
+ pLayer = &mEslLayer;
+ pPreviousService = pLayer->pUdp4List;
+ if ( pService == pPreviousService ) {
+ //
+ // Remove the service from the beginning of the list
+ //
+ pLayer->pUdp4List = 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 sockedt 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
+ EslUdpSocketIsConfigured4 (
+ IN DT_SOCKET * pSocket
+ )
+{
+ DT_PORT * pPort;
+ DT_PORT * pNextPort;
+ DT_UDP4_CONTEXT * pUdp4;
+ EFI_UDP4_PROTOCOL * pUdp4Protocol;
+ EFI_STATUS Status;
+ struct sockaddr_in LocalAddress;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Configure the port if necessary
+ //
+ if ( !pSocket->bConfigured ) {
+ //
+ // Fill in the port list if necessary
+ //
+ if ( NULL == pSocket->pPortList ) {
+ LocalAddress.sin_len = sizeof ( LocalAddress );
+ LocalAddress.sin_family = AF_INET;
+ LocalAddress.sin_addr.s_addr = 0;
+ LocalAddress.sin_port = 0;
+ Status = EslUdpBind4 ( pSocket,
+ (struct sockaddr *)&LocalAddress,
+ LocalAddress.sin_len );
+ }
+
+ //
+ // Walk the port list
+ //
+ pPort = pSocket->pPortList;
+ while ( NULL != pPort ) {
+ //
+ // Attempt to configure the port
+ //
+ pNextPort = pPort->pLinkSocket;
+ pUdp4 = &pPort->Context.Udp4;
+ pUdp4Protocol = pUdp4->pProtocol;
+ Status = pUdp4Protocol->Configure ( pUdp4Protocol,
+ &pUdp4->ConfigData );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_LISTEN,
+ "ERROR - Failed to configure the Udp4 port, Status: %r\r\n",
+ Status ));
+ switch ( Status ) {
+ case EFI_ACCESS_DENIED:
+ pSocket->errno = EACCES;
+ break;
+
+ default:
+ case EFI_DEVICE_ERROR:
+ pSocket->errno = EIO;
+ break;
+
+ case EFI_INVALID_PARAMETER:
+ pSocket->errno = EADDRNOTAVAIL;
+ break;
+
+ case EFI_NO_MAPPING:
+ pSocket->errno = EAFNOSUPPORT;
+ break;
+
+ case EFI_OUT_OF_RESOURCES:
+ pSocket->errno = ENOBUFS;
+ break;
+
+ case EFI_UNSUPPORTED:
+ pSocket->errno = EOPNOTSUPP;
+ break;
+ }
+ }
+ else {
+ DEBUG (( DEBUG_LISTEN,
+ "0x%08x: Port configured\r\n",
+ pPort ));
+ pUdp4->bConfigured = TRUE;
+
+ //
+ // Start the first read on the port
+ //
+ EslUdpRxStart4 ( pPort );
+
+ //
+ // The socket is connected
+ //
+ pSocket->State = SOCKET_STATE_CONNECTED;
+ }
+
+ //
+ // Set the next port
+ //
+ pPort = pNextPort;
+ }
+
+ //
+ // Determine the configuration status
+ //
+ if ( NULL != pSocket->pPortList ) {
+ pSocket->bConfigured = TRUE;
+ }
+ }
+
+ //
+ // Determine the socket configuration status
+ //
+ if ( !EFI_ERROR ( Status )) {
+ Status = pSocket->bConfigured ? EFI_SUCCESS : EFI_NOT_STARTED;
+ }
+
+ //
+ // Return the port connected state.
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Buffer data for transmission over a network connection.
+
+ This routine is called by the socket layer API to buffer
+ data for transmission. The data is copied into a local buffer
+ freeing the application buffer for reuse upon return. When
+ necessary, this routine will start the transmit engine that
+ performs the data transmission on the network connection. The
+ transmit engine transmits the data a packet at a time over the
+ network connection.
+
+ Transmission errors are returned during the next transmission or
+ during the close operation. Only buffering errors are returned
+ during the current transmission attempt.
+
+ @param [in] pSocket Address of 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 [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
+EslUdpTxBuffer4 (
+ IN DT_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
+ )
+{
+ DT_PACKET * pPacket;
+ DT_PACKET * pPreviousPacket;
+ DT_PACKET ** ppPacket;
+ DT_PORT * pPort;
+ const struct sockaddr_in * pRemoteAddress;
+ DT_UDP4_CONTEXT * pUdp4;
+ EFI_UDP4_COMPLETION_TOKEN * pToken;
+ size_t * pTxBytes;
+ DT_UDP4_TX_DATA * pTxData;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume failure
+ //
+ Status = EFI_UNSUPPORTED;
+ pSocket->errno = ENOTCONN;
+ * pDataLength = 0;
+
+ //
+ // Verify that the socket is connected
+ //
+ if ( SOCKET_STATE_CONNECTED == pSocket->State ) {
+ //
+ // Locate the port
+ //
+ pPort = pSocket->pPortList;
+ if ( NULL != pPort ) {
+ //
+ // Determine the queue head
+ //
+ pUdp4 = &pPort->Context.Udp4;
+ ppPacket = &pUdp4->pTxPacket;
+ pToken = &pUdp4->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.Udp4Tx )
+ - sizeof ( pPacket->Op.Udp4Tx.Buffer )
+ + BufferLength,
+ DEBUG_TX );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Initialize the transmit operation
+ //
+ pTxData = &pPacket->Op.Udp4Tx;
+ pTxData->TxData.GatewayAddress = NULL;
+ pTxData->TxData.UdpSessionData = NULL;
+ pTxData->TxData.DataLength = (UINT32) BufferLength;
+ pTxData->TxData.FragmentCount = 1;
+ pTxData->TxData.FragmentTable[0].FragmentLength = (UINT32) BufferLength;
+ pTxData->TxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Udp4Tx.Buffer[0];
+
+ //
+ // Set the remote system address if necessary
+ //
+ if ( NULL != pAddress ) {
+ pRemoteAddress = (const struct sockaddr_in *)pAddress;
+ pTxData->Session.SourceAddress.Addr[0] = 0;
+ pTxData->Session.SourceAddress.Addr[1] = 0;
+ pTxData->Session.SourceAddress.Addr[2] = 0;
+ pTxData->Session.SourceAddress.Addr[3] = 0;
+ pTxData->Session.SourcePort = 0;
+ pTxData->Session.DestinationAddress.Addr[0] = (UINT8)pRemoteAddress->sin_addr.s_addr;
+ pTxData->Session.DestinationAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 );
+ pTxData->Session.DestinationAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 );
+ pTxData->Session.DestinationAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 );
+ pTxData->Session.DestinationPort = SwapBytes16 ( pRemoteAddress->sin_port );
+
+ //
+ // Use the remote system address when sending this packet
+ //
+ pTxData->TxData.UdpSessionData = &pTxData->Session;
+ }
+
+ //
+ // Copy the data into the buffer
+ //
+ CopyMem ( &pPacket->Op.Udp4Tx.Buffer[0],
+ pBuffer,
+ BufferLength );
+
+ //
+ // Synchronize with the socket layer
+ //
+ RAISE_TPL ( TplPrevious, TPL_SOCKETS );
+
+ //
+ // Stop transmission after an error
+ //
+ if ( !EFI_ERROR ( pSocket->TxError )) {
+ //
+ // Display the request
+ //
+ DEBUG (( DEBUG_TX,
+ "Send %d %s bytes from 0x%08x\r\n",
+ BufferLength,
+ pBuffer ));
+
+ //
+ // Queue the data for transmission
+ //
+ pPacket->pNext = NULL;
+ pPreviousPacket = pSocket->pTxPacketListTail;
+ if ( NULL == pPreviousPacket ) {
+ pSocket->pTxPacketListHead = pPacket;
+ }
+ else {
+ pPreviousPacket->pNext = pPacket;
+ }
+ pSocket->pTxPacketListTail = pPacket;
+ DEBUG (( DEBUG_TX,
+ "0x%08x: Packet on transmit list\r\n",
+ pPacket ));
+
+ //
+ // Account for the buffered data
+ //
+ *pTxBytes += BufferLength;
+ *pDataLength = BufferLength;
+
+ //
+ // Start the transmit engine if it is idle
+ //
+ if ( NULL == pUdp4->pTxPacket ) {
+ EslUdpTxStart4 ( pSocket->pPortList );
+ }
+ }
+ else {
+ //
+ // Previous transmit error
+ // Stop transmission
+ //
+ Status = pSocket->TxError;
+ pSocket->errno = EIO;
+
+ //
+ // Free the packet
+ //
+ EslSocketPacketFree ( pPacket, DEBUG_TX );
+ }
+
+ //
+ // Release the socket layer synchronization
+ //
+ RESTORE_TPL ( TplPrevious );
+ }
+ else {
+ //
+ // Packet allocation failed
+ //
+ pSocket->errno = ENOMEM;
+ }
+ }
+ else {
+ //
+ // Not enough buffer space available
+ //
+ pSocket->errno = EAGAIN;
+ Status = EFI_NOT_READY;
+ }
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Process the transmit completion
+
+ @param Event The normal transmit completion event
+
+ @param pPort The DT_PORT structure address
+
+**/
+VOID
+EslUdpTxComplete4 (
+ IN EFI_EVENT Event,
+ IN DT_PORT * pPort
+ )
+{
+ UINT32 LengthInBytes;
+ DT_PACKET * pCurrentPacket;
+ DT_PACKET * pNextPacket;
+ DT_PACKET * pPacket;
+ DT_SOCKET * pSocket;
+ DT_UDP4_CONTEXT * pUdp4;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Locate the active transmit packet
+ //
+ pSocket = pPort->pSocket;
+ pUdp4 = &pPort->Context.Udp4;
+ pPacket = pUdp4->pTxPacket;
+
+ //
+ // Mark this packet as complete
+ //
+ pUdp4->pTxPacket = NULL;
+ LengthInBytes = pPacket->Op.Udp4Tx.TxData.DataLength;
+ pSocket->TxBytes -= LengthInBytes;
+
+ //
+ // Save any transmit error
+ //
+ Status = pUdp4->TxToken.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
+ //
+ EslUdpTxStart4 ( pPort );
+ }
+ }
+
+ //
+ // Release this packet
+ //
+ EslSocketPacketFree ( pPacket, DEBUG_TX );
+
+ //
+ // Finish the close operation if necessary
+ //
+ if (( PORT_STATE_CLOSE_STARTED <= pPort->State )
+ && ( NULL == pSocket->pTxPacketListHead )
+ && ( NULL == pUdp4->pTxPacket )) {
+ //
+ // Indicate that the transmit is complete
+ //
+ EslUdpPortCloseTxDone4 ( pPort );
+ }
+ DBG_EXIT ( );
+}
+
+
+/**
+ Transmit data using a network connection.
+
+ @param [in] pPort Address of a DT_PORT structure
+
+ **/
+VOID
+EslUdpTxStart4 (
+ IN DT_PORT * pPort
+ )
+{
+ DT_PACKET * pNextPacket;
+ DT_PACKET * pPacket;
+ DT_SOCKET * pSocket;
+ DT_UDP4_CONTEXT * pUdp4;
+ EFI_UDP4_PROTOCOL * pUdp4Protocol;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Get the packet from the queue head
+ //
+ pSocket = pPort->pSocket;
+ pPacket = pSocket->pTxPacketListHead;
+ if ( NULL != pPacket ) {
+ //
+ // Remove the packet from the queue
+ //
+ pNextPacket = pPacket->pNext;
+ pSocket->pTxPacketListHead = pNextPacket;
+ if ( NULL == pNextPacket ) {
+ pSocket->pTxPacketListTail = NULL;
+ }
+
+ //
+ // Set the packet as active
+ //
+ pUdp4 = &pPort->Context.Udp4;
+ pUdp4->pTxPacket = pPacket;
+
+ //
+ // Start the transmit operation
+ //
+ pUdp4Protocol = pUdp4->pProtocol;
+ pUdp4->TxToken.Packet.TxData = &pPacket->Op.Udp4Tx.TxData;
+ Status = pUdp4Protocol->Transmit ( pUdp4Protocol, &pUdp4->TxToken );
+ if ( EFI_ERROR ( Status )) {
+ pSocket = pPort->pSocket;
+ if ( EFI_SUCCESS == pSocket->TxError ) {
+ pSocket->TxError = Status;
+ }
+ }
+ }
+
+ DBG_EXIT ( );
+}
+
diff --git a/StdLib/EfiSocketLib/UseEfiSocketLib.c b/StdLib/EfiSocketLib/UseEfiSocketLib.c new file mode 100644 index 0000000000..b0e8ef671a --- /dev/null +++ b/StdLib/EfiSocketLib/UseEfiSocketLib.c @@ -0,0 +1,242 @@ +/** @file
+ Implement the connection to the EFI socket library
+
+ 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>
+
+
+CONST EFI_GUID mEslRawServiceGuid = {
+ 0xc31bf4a5, 0x2c7, 0x49d2, { 0xa5, 0x58, 0xfe, 0x62, 0x6f, 0x7e, 0xd4, 0x77 }
+};
+
+CONST EFI_GUID mEslTcp4ServiceGuid = {
+ 0xffc659c2, 0x4ef2, 0x4532, { 0xb8, 0x75, 0xcd, 0x9a, 0xa4, 0x27, 0x4c, 0xde }
+};
+
+CONST EFI_GUID mEslUdp4ServiceGuid = {
+ 0x44e03a55, 0x8d97, 0x4511, { 0xbf, 0xef, 0xa, 0x8b, 0xc6, 0x2c, 0x25, 0xae }
+};
+
+
+/**
+ Connect to the EFI socket library
+
+ @param [in] ppSocketProtocol Address to receive the socket protocol address
+
+ @retval 0 Successfully returned the socket protocol
+ @retval other Value for errno
+ **/
+int
+EslServiceGetProtocol (
+ IN EFI_SOCKET_PROTOCOL ** ppSocketProtocol
+ )
+{
+ EFI_HANDLE ChildHandle;
+ DT_SOCKET * pSocket;
+ int RetVal;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ RetVal = 0;
+
+ //
+ // Locate the socket protocol
+ //
+ ChildHandle = NULL;
+ Status = EslSocketAllocate ( &ChildHandle,
+ DEBUG_SOCKET,
+ &pSocket );
+ if ( !EFI_ERROR ( Status )) {
+ *ppSocketProtocol = &pSocket->SocketProtocol;
+ }
+ else {
+ //
+ // No resources
+ //
+ RetVal = ENOMEM;
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_DEC ( RetVal );
+ return RetVal;
+}
+
+
+/**
+ Connect to the network layer
+
+ @retval EFI_SUCCESS Successfully connected to the network layer
+
+ **/
+EFI_STATUS
+EslServiceNetworkConnect (
+ VOID
+ )
+{
+ UINTN HandleCount;
+ EFI_HANDLE * pHandles;
+ UINTN Index;
+ CONST DT_SOCKET_BINDING * pSocketBinding;
+ CONST DT_SOCKET_BINDING * pEnd;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Initialize the socket layer
+ //
+ Status = EFI_SUCCESS;
+ EslServiceLoad ( gImageHandle );
+
+ //
+ // Connect the network devices
+ //
+ pSocketBinding = &cEslSocketBinding [0];
+ pEnd = &pSocketBinding [ cEslSocketBindingEntries ];
+ while ( pEnd > pSocketBinding ) {
+ //
+ // Attempt to locate the network adapters
+ //
+ HandleCount = 0;
+ pHandles = NULL;
+ Status = gBS->LocateHandleBuffer ( ByProtocol,
+ pSocketBinding->pNetworkBinding,
+ NULL,
+ &HandleCount,
+ &pHandles );
+ if ( EFI_ERROR ( Status )) {
+ break;
+ }
+ if ( NULL != pHandles ) {
+ //
+ // Attempt to connect to this network adapter
+ //
+ for ( Index = 0; HandleCount > Index; Index++ ) {
+ Status = EslServiceConnect ( gImageHandle,
+ pHandles [ Index ]);
+ if ( EFI_ERROR ( Status )) {
+ break;
+ }
+ }
+
+ //
+ // Done with the handles
+ //
+ gBS->FreePool ( pHandles );
+ }
+
+ //
+ // Set the next network protocol
+ //
+ pSocketBinding += 1;
+ }
+
+ //
+ // Return the network connection status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Disconnect from the network layer
+
+ @retval EFI_SUCCESS Successfully disconnected from the network layer
+
+ **/
+EFI_STATUS
+EslServiceNetworkDisconnect (
+ VOID
+ )
+{
+ UINTN HandleCount;
+ EFI_HANDLE * pHandles;
+ UINTN Index;
+ CONST DT_SOCKET_BINDING * pSocketBinding;
+ CONST DT_SOCKET_BINDING * pEnd;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Disconnect the network devices
+ //
+ pSocketBinding = &cEslSocketBinding [0];
+ pEnd = &pSocketBinding [ cEslSocketBindingEntries ];
+ while ( pEnd > pSocketBinding ) {
+ //
+ // Attempt to locate the network adapters
+ //
+ HandleCount = 0;
+ pHandles = NULL;
+ Status = gBS->LocateHandleBuffer ( ByProtocol,
+ pSocketBinding->pNetworkBinding,
+ NULL,
+ &HandleCount,
+ &pHandles );
+ if ( EFI_ERROR ( Status )) {
+ break;
+ }
+ if ( NULL != pHandles ) {
+ //
+ // Attempt to disconnect from this network adapter
+ //
+ for ( Index = 0; HandleCount > Index; Index++ ) {
+ Status = EslServiceDisconnect ( gImageHandle,
+ pHandles [ Index ]);
+ if ( EFI_ERROR ( Status )) {
+ break;
+ }
+ }
+
+ //
+ // Done with the handles
+ //
+ gBS->FreePool ( pHandles );
+ }
+
+ //
+ // Set the next network protocol
+ //
+ pSocketBinding += 1;
+ }
+
+ //
+ // Finish the disconnect operation
+ //
+ if ( !EFI_ERROR ( Status )) {
+ EslServiceUnload ( );
+ }
+
+ //
+ // Return the network connection status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+PFN_ESL_xSTRUCTOR mpfnEslConstructor = EslServiceNetworkConnect;
+PFN_ESL_xSTRUCTOR mpfnEslDestructor = EslServiceNetworkDisconnect;
|