]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.c
NetworkPkg: Move Network library and drivers from MdeModulePkg to NetworkPkg
[mirror_edk2.git] / NetworkPkg / Library / DxeTcpIoLib / DxeTcpIoLib.c
diff --git a/NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.c b/NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.c
new file mode 100644 (file)
index 0000000..341295d
--- /dev/null
@@ -0,0 +1,1011 @@
+/** @file\r
+  This library is used to share code between UEFI network stack modules.\r
+  It provides the helper routines to access TCP service.\r
+\r
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Library/TcpIoLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+\r
+/**\r
+  The common notify function associated with various TcpIo events.\r
+\r
+  @param[in]  Event   The event signaled.\r
+  @param[in]  Context The context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpIoCommonNotify (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  if ((Event == NULL) || (Context == NULL)) {\r
+    return ;\r
+  }\r
+\r
+  *((BOOLEAN *) Context) = TRUE;\r
+}\r
+\r
+/**\r
+  The internal function for delay configuring TCP6 when IP6 driver is still in DAD.\r
+\r
+  @param[in]  Tcp6               The EFI_TCP6_PROTOCOL protocol instance.\r
+  @param[in]  Tcp6ConfigData     The Tcp6 configuration data.\r
+\r
+  @retval EFI_SUCCESS            The operational settings successfully\r
+                                 completed.\r
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.\r
+  @retval Others                 Failed to finish the operation.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpIoGetMapping (\r
+  IN EFI_TCP6_PROTOCOL    *Tcp6,\r
+  IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  EFI_EVENT               Event;\r
+\r
+  if ((Tcp6 == NULL) || (Tcp6ConfigData == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Event  = NULL;\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  Event,\r
+                  TimerRelative,\r
+                  TCP_GET_MAPPING_TIMEOUT\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  while (EFI_ERROR (gBS->CheckEvent (Event))) {\r
+\r
+    Tcp6->Poll (Tcp6);\r
+\r
+    Status = Tcp6->Configure (Tcp6, Tcp6ConfigData);\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (Event != NULL) {\r
+    gBS->CloseEvent (Event);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Create a TCP socket with the specified configuration data.\r
+\r
+  @param[in]  Image      The handle of the driver image.\r
+  @param[in]  Controller The handle of the controller.\r
+  @param[in]  TcpVersion The version of Tcp, TCP_VERSION_4 or TCP_VERSION_6.\r
+  @param[in]  ConfigData The Tcp configuration data.\r
+  @param[out] TcpIo      The TcpIo.\r
+\r
+  @retval EFI_SUCCESS            The TCP socket is created and configured.\r
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.\r
+  @retval EFI_UNSUPPORTED        One or more of the control options are not\r
+                                 supported in the implementation.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.\r
+  @retval Others                 Failed to create the TCP socket or configure it.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpIoCreateSocket (\r
+  IN EFI_HANDLE             Image,\r
+  IN EFI_HANDLE             Controller,\r
+  IN UINT8                  TcpVersion,\r
+  IN TCP_IO_CONFIG_DATA     *ConfigData,\r
+  OUT TCP_IO                *TcpIo\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_EVENT                 Event;\r
+  EFI_GUID                  *ServiceBindingGuid;\r
+  EFI_GUID                  *ProtocolGuid;\r
+  VOID                      **Interface;\r
+  EFI_TCP4_OPTION           ControlOption;\r
+  EFI_TCP4_CONFIG_DATA      Tcp4ConfigData;\r
+  EFI_TCP4_ACCESS_POINT     *AccessPoint4;\r
+  EFI_TCP4_PROTOCOL         *Tcp4;\r
+  EFI_TCP6_CONFIG_DATA      Tcp6ConfigData;\r
+  EFI_TCP6_ACCESS_POINT     *AccessPoint6;\r
+  EFI_TCP6_PROTOCOL         *Tcp6;\r
+  EFI_TCP4_RECEIVE_DATA     *RxData;\r
+\r
+  if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (TcpIo == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Tcp4 = NULL;\r
+  Tcp6 = NULL;\r
+\r
+  ZeroMem (TcpIo, sizeof (TCP_IO));\r
+\r
+  if (TcpVersion == TCP_VERSION_4) {\r
+    ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+    ProtocolGuid       = &gEfiTcp4ProtocolGuid;\r
+    Interface          = (VOID **) (&TcpIo->Tcp.Tcp4);\r
+  } else if (TcpVersion == TCP_VERSION_6) {\r
+    ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+    ProtocolGuid       = &gEfiTcp6ProtocolGuid;\r
+    Interface          = (VOID **) (&TcpIo->Tcp.Tcp6);\r
+  } else {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  TcpIo->TcpVersion = TcpVersion;\r
+\r
+  //\r
+  // Create the TCP child instance and get the TCP protocol.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             Controller,\r
+             Image,\r
+             ServiceBindingGuid,\r
+             &TcpIo->Handle\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  TcpIo->Handle,\r
+                  ProtocolGuid,\r
+                  Interface,\r
+                  Image,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status) || (*Interface == NULL)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  if (TcpVersion == TCP_VERSION_4) {\r
+    Tcp4             = TcpIo->Tcp.Tcp4;\r
+  } else {\r
+    Tcp6             = TcpIo->Tcp.Tcp6;\r
+  }\r
+\r
+  TcpIo->Image       = Image;\r
+  TcpIo->Controller  = Controller;\r
+\r
+  //\r
+  // Set the configuration parameters.\r
+  //\r
+  ControlOption.ReceiveBufferSize       = 0x200000;\r
+  ControlOption.SendBufferSize          = 0x200000;\r
+  ControlOption.MaxSynBackLog           = 0;\r
+  ControlOption.ConnectionTimeout       = 0;\r
+  ControlOption.DataRetries             = 6;\r
+  ControlOption.FinTimeout              = 0;\r
+  ControlOption.TimeWaitTimeout         = 0;\r
+  ControlOption.KeepAliveProbes         = 4;\r
+  ControlOption.KeepAliveTime           = 0;\r
+  ControlOption.KeepAliveInterval       = 0;\r
+  ControlOption.EnableNagle             = FALSE;\r
+  ControlOption.EnableTimeStamp         = FALSE;\r
+  ControlOption.EnableWindowScaling     = TRUE;\r
+  ControlOption.EnableSelectiveAck      = FALSE;\r
+  ControlOption.EnablePathMtuDiscovery  = FALSE;\r
+\r
+  if (TcpVersion == TCP_VERSION_4) {\r
+    Tcp4ConfigData.TypeOfService        = 8;\r
+    Tcp4ConfigData.TimeToLive           = 255;\r
+    Tcp4ConfigData.ControlOption        = &ControlOption;\r
+\r
+    AccessPoint4                        = &Tcp4ConfigData.AccessPoint;\r
+\r
+    ZeroMem (AccessPoint4, sizeof (EFI_TCP4_ACCESS_POINT));\r
+    AccessPoint4->StationPort           = ConfigData->Tcp4IoConfigData.StationPort;\r
+    AccessPoint4->RemotePort            = ConfigData->Tcp4IoConfigData.RemotePort;\r
+    AccessPoint4->ActiveFlag            = ConfigData->Tcp4IoConfigData.ActiveFlag;\r
+\r
+    CopyMem (\r
+      &AccessPoint4->StationAddress,\r
+      &ConfigData->Tcp4IoConfigData.LocalIp,\r
+      sizeof (EFI_IPv4_ADDRESS)\r
+      );\r
+    CopyMem (\r
+      &AccessPoint4->SubnetMask,\r
+      &ConfigData->Tcp4IoConfigData.SubnetMask,\r
+      sizeof (EFI_IPv4_ADDRESS)\r
+      );\r
+    CopyMem (\r
+      &AccessPoint4->RemoteAddress,\r
+      &ConfigData->Tcp4IoConfigData.RemoteIp,\r
+      sizeof (EFI_IPv4_ADDRESS)\r
+      );\r
+\r
+    ASSERT (Tcp4 != NULL);\r
+\r
+    //\r
+    // Configure the TCP4 protocol.\r
+    //\r
+    Status = Tcp4->Configure (Tcp4, &Tcp4ConfigData);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    if (!EFI_IP4_EQUAL (&ConfigData->Tcp4IoConfigData.Gateway, &mZeroIp4Addr)) {\r
+      //\r
+      // The gateway is not zero. Add the default route manually.\r
+      //\r
+      Status = Tcp4->Routes (\r
+                       Tcp4,\r
+                       FALSE,\r
+                       &mZeroIp4Addr,\r
+                       &mZeroIp4Addr,\r
+                       &ConfigData->Tcp4IoConfigData.Gateway\r
+                       );\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_ERROR;\r
+      }\r
+    }\r
+  } else {\r
+    Tcp6ConfigData.TrafficClass         = 0;\r
+    Tcp6ConfigData.HopLimit             = 255;\r
+    Tcp6ConfigData.ControlOption        = (EFI_TCP6_OPTION *) &ControlOption;\r
+\r
+    AccessPoint6                        = &Tcp6ConfigData.AccessPoint;\r
+\r
+    ZeroMem (AccessPoint6, sizeof (EFI_TCP6_ACCESS_POINT));\r
+    AccessPoint6->StationPort           = ConfigData->Tcp6IoConfigData.StationPort;\r
+    AccessPoint6->RemotePort            = ConfigData->Tcp6IoConfigData.RemotePort;\r
+    AccessPoint6->ActiveFlag            = ConfigData->Tcp6IoConfigData.ActiveFlag;\r
+\r
+    IP6_COPY_ADDRESS (&AccessPoint6->RemoteAddress, &ConfigData->Tcp6IoConfigData.RemoteIp);\r
+\r
+\r
+    ASSERT (Tcp6 != NULL);\r
+    //\r
+    // Configure the TCP6 protocol.\r
+    //\r
+    Status = Tcp6->Configure (Tcp6, &Tcp6ConfigData);\r
+    if (Status == EFI_NO_MAPPING) {\r
+      Status = TcpIoGetMapping (Tcp6, &Tcp6ConfigData);\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Create events for variuos asynchronous operations.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  TcpIoCommonNotify,\r
+                  &TcpIo->IsConnDone,\r
+                  &Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  TcpIo->ConnToken.Tcp4Token.CompletionToken.Event = Event;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  TcpIoCommonNotify,\r
+                  &TcpIo->IsListenDone,\r
+                  &Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  TcpIo->ListenToken.Tcp4Token.CompletionToken.Event = Event;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  TcpIoCommonNotify,\r
+                  &TcpIo->IsTxDone,\r
+                  &Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  TcpIo->TxToken.Tcp4Token.CompletionToken.Event = Event;\r
+\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  TcpIoCommonNotify,\r
+                  &TcpIo->IsRxDone,\r
+                  &Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  TcpIo->RxToken.Tcp4Token.CompletionToken.Event = Event;\r
+\r
+  RxData = (EFI_TCP4_RECEIVE_DATA *) AllocateZeroPool (sizeof (EFI_TCP4_RECEIVE_DATA));\r
+  if (RxData == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  TcpIo->RxToken.Tcp4Token.Packet.RxData = RxData;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  TcpIoCommonNotify,\r
+                  &TcpIo->IsCloseDone,\r
+                  &Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  TcpIo->CloseToken.Tcp4Token.CompletionToken.Event = Event;\r
+\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  TcpIoDestroySocket (TcpIo);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Destroy the socket.\r
+\r
+  @param[in]  TcpIo The TcpIo which wraps the socket to be destroyed.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpIoDestroySocket (\r
+  IN TCP_IO                 *TcpIo\r
+  )\r
+{\r
+  EFI_EVENT                 Event;\r
+  EFI_TCP4_PROTOCOL         *Tcp4;\r
+  EFI_TCP6_PROTOCOL         *Tcp6;\r
+  UINT8                     TcpVersion;\r
+  EFI_GUID                  *ServiceBindingGuid;\r
+  EFI_GUID                  *ProtocolGuid;\r
+  EFI_HANDLE                ChildHandle;\r
+\r
+  if (TcpIo == NULL) {\r
+    return ;\r
+  }\r
+\r
+  TcpVersion = TcpIo->TcpVersion;\r
+\r
+  if ((TcpVersion != TCP_VERSION_4) && (TcpVersion != TCP_VERSION_6)) {\r
+    return ;\r
+  }\r
+\r
+  Event = TcpIo->ConnToken.Tcp4Token.CompletionToken.Event;\r
+\r
+  if (Event != NULL) {\r
+    gBS->CloseEvent (Event);\r
+  }\r
+\r
+  Event = TcpIo->ListenToken.Tcp4Token.CompletionToken.Event;\r
+\r
+  if (Event != NULL) {\r
+    gBS->CloseEvent (Event);\r
+  }\r
+\r
+  Event = TcpIo->TxToken.Tcp4Token.CompletionToken.Event;\r
+\r
+  if (Event != NULL) {\r
+    gBS->CloseEvent (Event);\r
+  }\r
+\r
+  Event = TcpIo->RxToken.Tcp4Token.CompletionToken.Event;\r
+\r
+  if (Event != NULL) {\r
+    gBS->CloseEvent (Event);\r
+  }\r
+\r
+  Event = TcpIo->CloseToken.Tcp4Token.CompletionToken.Event;\r
+\r
+  if (Event != NULL) {\r
+    gBS->CloseEvent (Event);\r
+  }\r
+\r
+  if (TcpIo->RxToken.Tcp4Token.Packet.RxData != NULL) {\r
+    FreePool (TcpIo->RxToken.Tcp4Token.Packet.RxData);\r
+  }\r
+\r
+  Tcp4 = NULL;\r
+  Tcp6 = NULL;\r
+\r
+\r
+  if (TcpVersion == TCP_VERSION_4) {\r
+    ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+    ProtocolGuid       = &gEfiTcp4ProtocolGuid;\r
+    Tcp4 = TcpIo->Tcp.Tcp4;\r
+    if (Tcp4 != NULL) {\r
+      Tcp4->Configure (Tcp4, NULL);\r
+    }\r
+  } else {\r
+    ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+    ProtocolGuid       = &gEfiTcp6ProtocolGuid;\r
+    Tcp6 = TcpIo->Tcp.Tcp6;\r
+    if (Tcp6 != NULL) {\r
+      Tcp6->Configure (Tcp6, NULL);\r
+    }\r
+  }\r
+\r
+  if ((Tcp4 != NULL) || (Tcp6 != NULL)) {\r
+\r
+    gBS->CloseProtocol (\r
+           TcpIo->Handle,\r
+           ProtocolGuid,\r
+           TcpIo->Image,\r
+           TcpIo->Controller\r
+           );\r
+  }\r
+\r
+  ChildHandle = NULL;\r
+\r
+  if (TcpIo->IsListenDone) {\r
+    if (TcpVersion == TCP_VERSION_4) {\r
+      Tcp4 = TcpIo->NewTcp.Tcp4;\r
+      if (Tcp4 != NULL) {\r
+        Tcp4->Configure (Tcp4, NULL);\r
+        ChildHandle = TcpIo->ListenToken.Tcp4Token.NewChildHandle;\r
+      }\r
+    } else {\r
+      Tcp6 = TcpIo->NewTcp.Tcp6;\r
+      if (Tcp6 != NULL) {\r
+        Tcp6->Configure (Tcp6, NULL);\r
+        ChildHandle = TcpIo->ListenToken.Tcp6Token.NewChildHandle;\r
+      }\r
+    }\r
+\r
+    if (ChildHandle != NULL) {\r
+\r
+      gBS->CloseProtocol (\r
+             ChildHandle,\r
+             ProtocolGuid,\r
+             TcpIo->Image,\r
+             TcpIo->Controller\r
+             );\r
+    }\r
+  }\r
+\r
+  NetLibDestroyServiceChild (\r
+    TcpIo->Controller,\r
+    TcpIo->Image,\r
+    ServiceBindingGuid,\r
+    TcpIo->Handle\r
+    );\r
+}\r
+\r
+/**\r
+  Connect to the other endpoint of the TCP socket.\r
+\r
+  @param[in, out]  TcpIo     The TcpIo wrapping the TCP socket.\r
+  @param[in]       Timeout   The time to wait for connection done. Set to NULL for infinite wait.\r
+\r
+  @retval EFI_SUCCESS            Connect to the other endpoint of the TCP socket\r
+                                 successfully.\r
+  @retval EFI_TIMEOUT            Failed to connect to the other endpoint of the\r
+                                 TCP socket in the specified time period.\r
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.\r
+  @retval EFI_UNSUPPORTED        One or more of the control options are not\r
+                                 supported in the implementation.\r
+  @retval Others                 Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpIoConnect (\r
+  IN OUT TCP_IO             *TcpIo,\r
+  IN     EFI_EVENT          Timeout        OPTIONAL\r
+  )\r
+{\r
+  EFI_TCP4_PROTOCOL         *Tcp4;\r
+  EFI_TCP6_PROTOCOL         *Tcp6;\r
+  EFI_STATUS                Status;\r
+\r
+  if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  TcpIo->IsConnDone = FALSE;\r
+\r
+  Tcp4 = NULL;\r
+  Tcp6 = NULL;\r
+\r
+  if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+    Tcp4   = TcpIo->Tcp.Tcp4;\r
+    Status = Tcp4->Connect (Tcp4, &TcpIo->ConnToken.Tcp4Token);\r
+  } else if (TcpIo->TcpVersion == TCP_VERSION_6) {\r
+    Tcp6   = TcpIo->Tcp.Tcp6;\r
+    Status = Tcp6->Connect (Tcp6, &TcpIo->ConnToken.Tcp6Token);\r
+  } else {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  while (!TcpIo->IsConnDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {\r
+    if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+      Tcp4->Poll (Tcp4);\r
+    } else {\r
+      Tcp6->Poll (Tcp6);\r
+    }\r
+  }\r
+\r
+  if (!TcpIo->IsConnDone) {\r
+    if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+      Tcp4->Cancel (Tcp4, &TcpIo->ConnToken.Tcp4Token.CompletionToken);\r
+    } else {\r
+      Tcp6->Cancel (Tcp6, &TcpIo->ConnToken.Tcp6Token.CompletionToken);\r
+    }\r
+    Status = EFI_TIMEOUT;\r
+  } else {\r
+    Status = TcpIo->ConnToken.Tcp4Token.CompletionToken.Status;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Accept the incomding request from the other endpoint of the TCP socket.\r
+\r
+  @param[in, out]  TcpIo     The TcpIo wrapping the TCP socket.\r
+  @param[in]       Timeout   The time to wait for connection done. Set to NULL for infinite wait.\r
+\r
+\r
+  @retval EFI_SUCCESS            Connect to the other endpoint of the TCP socket\r
+                                 successfully.\r
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.\r
+  @retval EFI_UNSUPPORTED        One or more of the control options are not\r
+                                 supported in the implementation.\r
+\r
+  @retval EFI_TIMEOUT            Failed to connect to the other endpoint of the\r
+                                 TCP socket in the specified time period.\r
+  @retval Others                 Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpIoAccept (\r
+  IN OUT TCP_IO             *TcpIo,\r
+  IN     EFI_EVENT          Timeout        OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_GUID                  *ProtocolGuid;\r
+  EFI_TCP4_PROTOCOL         *Tcp4;\r
+  EFI_TCP6_PROTOCOL         *Tcp6;\r
+\r
+  if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  TcpIo->IsListenDone = FALSE;\r
+\r
+  Tcp4 = NULL;\r
+  Tcp6 = NULL;\r
+\r
+  if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+    Tcp4   = TcpIo->Tcp.Tcp4;\r
+    Status = Tcp4->Accept (Tcp4, &TcpIo->ListenToken.Tcp4Token);\r
+  } else if (TcpIo->TcpVersion == TCP_VERSION_6) {\r
+    Tcp6   = TcpIo->Tcp.Tcp6;\r
+    Status = Tcp6->Accept (Tcp6, &TcpIo->ListenToken.Tcp6Token);\r
+  } else {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  while (!TcpIo->IsListenDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {\r
+    if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+      Tcp4->Poll (Tcp4);\r
+    } else {\r
+      Tcp6->Poll (Tcp6);\r
+    }\r
+  }\r
+\r
+  if (!TcpIo->IsListenDone) {\r
+    if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+      Tcp4->Cancel (Tcp4, &TcpIo->ListenToken.Tcp4Token.CompletionToken);\r
+    } else {\r
+      Tcp6->Cancel (Tcp6, &TcpIo->ListenToken.Tcp6Token.CompletionToken);\r
+    }\r
+    Status = EFI_TIMEOUT;\r
+  } else {\r
+    Status = TcpIo->ListenToken.Tcp4Token.CompletionToken.Status;\r
+  }\r
+\r
+  //\r
+  // The new TCP instance handle created for the established connection is\r
+  // in ListenToken.\r
+  //\r
+  if (!EFI_ERROR (Status)) {\r
+    if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+      ProtocolGuid = &gEfiTcp4ProtocolGuid;\r
+    } else {\r
+      ProtocolGuid = &gEfiTcp6ProtocolGuid;\r
+    }\r
+\r
+    Status = gBS->OpenProtocol (\r
+                    TcpIo->ListenToken.Tcp4Token.NewChildHandle,\r
+                    ProtocolGuid,\r
+                    (VOID **) (&TcpIo->NewTcp.Tcp4),\r
+                    TcpIo->Image,\r
+                    TcpIo->Controller,\r
+                    EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                    );\r
+\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Reset the socket.\r
+\r
+  @param[in, out]  TcpIo The TcpIo wrapping the TCP socket.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpIoReset (\r
+  IN OUT TCP_IO             *TcpIo\r
+  )\r
+{\r
+  EFI_TCP4_PROTOCOL         *Tcp4;\r
+  EFI_TCP6_PROTOCOL         *Tcp6;\r
+  EFI_STATUS                Status;\r
+\r
+  if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {\r
+    return ;\r
+  }\r
+\r
+  TcpIo->IsCloseDone = FALSE;\r
+  Tcp4               = NULL;\r
+  Tcp6               = NULL;\r
+\r
+  if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+    TcpIo->CloseToken.Tcp4Token.AbortOnClose = TRUE;\r
+    Tcp4 = TcpIo->Tcp.Tcp4;\r
+    Status = Tcp4->Close (Tcp4, &TcpIo->CloseToken.Tcp4Token);\r
+  } else if (TcpIo->TcpVersion == TCP_VERSION_6) {\r
+    TcpIo->CloseToken.Tcp6Token.AbortOnClose = TRUE;\r
+    Tcp6 = TcpIo->Tcp.Tcp6;\r
+    Status = Tcp6->Close (Tcp6, &TcpIo->CloseToken.Tcp6Token);\r
+  } else {\r
+    return ;\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return ;\r
+  }\r
+\r
+  while (!TcpIo->IsCloseDone) {\r
+    if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+      Tcp4->Poll (Tcp4);\r
+    } else {\r
+      Tcp6->Poll (Tcp6);\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Transmit the Packet to the other endpoint of the socket.\r
+\r
+  @param[in]   TcpIo           The TcpIo wrapping the TCP socket.\r
+  @param[in]   Packet          The packet to transmit.\r
+\r
+  @retval EFI_SUCCESS            The packet is trasmitted.\r
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.\r
+  @retval EFI_UNSUPPORTED        One or more of the control options are not\r
+                                 supported in the implementation.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.\r
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.\r
+  @retval Others                 Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpIoTransmit (\r
+  IN TCP_IO                 *TcpIo,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  VOID                      *Data;\r
+  EFI_TCP4_PROTOCOL         *Tcp4;\r
+  EFI_TCP6_PROTOCOL         *Tcp6;\r
+  UINTN                     Size;\r
+\r
+  if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)|| (Packet == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+\r
+    Size = sizeof (EFI_TCP4_TRANSMIT_DATA) +\r
+           (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA);\r
+  } else if (TcpIo->TcpVersion == TCP_VERSION_6) {\r
+    Size = sizeof (EFI_TCP6_TRANSMIT_DATA) +\r
+           (Packet->BlockOpNum - 1) * sizeof (EFI_TCP6_FRAGMENT_DATA);\r
+  } else {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Data = AllocatePool (Size);\r
+  if (Data == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  ((EFI_TCP4_TRANSMIT_DATA *) Data)->Push        = TRUE;\r
+  ((EFI_TCP4_TRANSMIT_DATA *) Data)->Urgent      = FALSE;\r
+  ((EFI_TCP4_TRANSMIT_DATA *) Data)->DataLength  = Packet->TotalSize;\r
+\r
+  //\r
+  // Build the fragment table.\r
+  //\r
+  ((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum;\r
+\r
+  NetbufBuildExt (\r
+    Packet,\r
+    (NET_FRAGMENT *) &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentTable[0],\r
+    &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount\r
+    );\r
+\r
+  Tcp4   = NULL;\r
+  Tcp6   = NULL;\r
+  Status = EFI_DEVICE_ERROR;\r
+\r
+  //\r
+  // Trasnmit the packet.\r
+  //\r
+  if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+    TcpIo->TxToken.Tcp4Token.Packet.TxData = (EFI_TCP4_TRANSMIT_DATA *) Data;\r
+    Tcp4    = TcpIo->Tcp.Tcp4;\r
+    if (TcpIo->IsListenDone) {\r
+      Tcp4 = TcpIo->NewTcp.Tcp4;\r
+    }\r
+\r
+    if (Tcp4 == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Status  = Tcp4->Transmit (Tcp4, &TcpIo->TxToken.Tcp4Token);\r
+  } else {\r
+    TcpIo->TxToken.Tcp6Token.Packet.TxData = (EFI_TCP6_TRANSMIT_DATA *) Data;\r
+    Tcp6    = TcpIo->Tcp.Tcp6;\r
+    if (TcpIo->IsListenDone) {\r
+      Tcp6 = TcpIo->NewTcp.Tcp6;\r
+    }\r
+\r
+    if (Tcp6 == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Status  = Tcp6->Transmit (Tcp6, &TcpIo->TxToken.Tcp6Token);\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  while (!TcpIo->IsTxDone) {\r
+    if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+      Tcp4->Poll (Tcp4);\r
+    } else {\r
+      Tcp6->Poll (Tcp6);\r
+    }\r
+  }\r
+\r
+  TcpIo->IsTxDone  = FALSE;\r
+  Status           = TcpIo->TxToken.Tcp4Token.CompletionToken.Status;\r
+\r
+ON_EXIT:\r
+\r
+  FreePool (Data);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Receive data from the socket.\r
+\r
+  @param[in, out]  TcpIo       The TcpIo which wraps the socket to be destroyed.\r
+  @param[in]       Packet      The buffer to hold the data copy from the socket rx buffer.\r
+  @param[in]       AsyncMode   Is this receive asyncronous or not.\r
+  @param[in]       Timeout     The time to wait for receiving the amount of data the Packet\r
+                               can hold. Set to NULL for infinite wait.\r
+\r
+  @retval EFI_SUCCESS            The required amount of data is received from the socket.\r
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.\r
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate momery.\r
+  @retval EFI_TIMEOUT            Failed to receive the required amount of data in the\r
+                                 specified time period.\r
+  @retval Others                 Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpIoReceive (\r
+  IN OUT TCP_IO             *TcpIo,\r
+  IN     NET_BUF            *Packet,\r
+  IN     BOOLEAN            AsyncMode,\r
+  IN     EFI_EVENT          Timeout       OPTIONAL\r
+  )\r
+{\r
+  EFI_TCP4_PROTOCOL         *Tcp4;\r
+  EFI_TCP6_PROTOCOL         *Tcp6;\r
+  EFI_TCP4_RECEIVE_DATA     *RxData;\r
+  EFI_STATUS                Status;\r
+  NET_FRAGMENT              *Fragment;\r
+  UINT32                    FragmentCount;\r
+  UINT32                    CurrentFragment;\r
+\r
+  if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)|| (Packet == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  RxData = TcpIo->RxToken.Tcp4Token.Packet.RxData;\r
+  if (RxData == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Tcp4 = NULL;\r
+  Tcp6 = NULL;\r
+\r
+  if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+    Tcp4 = TcpIo->Tcp.Tcp4;\r
+\r
+    if (TcpIo->IsListenDone) {\r
+      Tcp4 = TcpIo->NewTcp.Tcp4;\r
+    }\r
+\r
+    if (Tcp4 == NULL) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+  } else if (TcpIo->TcpVersion == TCP_VERSION_6) {\r
+    Tcp6 = TcpIo->Tcp.Tcp6;\r
+\r
+    if (TcpIo->IsListenDone) {\r
+      Tcp6 = TcpIo->NewTcp.Tcp6;\r
+    }\r
+\r
+    if (Tcp6 == NULL) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+  } else {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  FragmentCount = Packet->BlockOpNum;\r
+  Fragment      = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT));\r
+  if (Fragment == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Build the fragment table.\r
+  //\r
+  NetbufBuildExt (Packet, Fragment, &FragmentCount);\r
+\r
+  RxData->FragmentCount         = 1;\r
+  CurrentFragment               = 0;\r
+  Status                        = EFI_SUCCESS;\r
+\r
+  while (CurrentFragment < FragmentCount) {\r
+    RxData->DataLength                       = Fragment[CurrentFragment].Len;\r
+    RxData->FragmentTable[0].FragmentLength  = Fragment[CurrentFragment].Len;\r
+    RxData->FragmentTable[0].FragmentBuffer  = Fragment[CurrentFragment].Bulk;\r
+\r
+    if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+      Status = Tcp4->Receive (Tcp4, &TcpIo->RxToken.Tcp4Token);\r
+    } else {\r
+      Status = Tcp6->Receive (Tcp6, &TcpIo->RxToken.Tcp6Token);\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    while (!TcpIo->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {\r
+      //\r
+      // Poll until some data is received or an error occurs.\r
+      //\r
+      if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+        Tcp4->Poll (Tcp4);\r
+      } else {\r
+        Tcp6->Poll (Tcp6);\r
+      }\r
+    }\r
+\r
+    if (!TcpIo->IsRxDone) {\r
+      //\r
+      // Timeout occurs, cancel the receive request.\r
+      //\r
+      if (TcpIo->TcpVersion == TCP_VERSION_4) {\r
+        Tcp4->Cancel (Tcp4, &TcpIo->RxToken.Tcp4Token.CompletionToken);\r
+      } else {\r
+        Tcp6->Cancel (Tcp6, &TcpIo->RxToken.Tcp6Token.CompletionToken);\r
+      }\r
+\r
+      Status = EFI_TIMEOUT;\r
+      goto ON_EXIT;\r
+    } else {\r
+      TcpIo->IsRxDone = FALSE;\r
+    }\r
+\r
+    Status = TcpIo->RxToken.Tcp4Token.CompletionToken.Status;\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Fragment[CurrentFragment].Len -= RxData->FragmentTable[0].FragmentLength;\r
+    if (Fragment[CurrentFragment].Len == 0) {\r
+      CurrentFragment++;\r
+    } else {\r
+      Fragment[CurrentFragment].Bulk += RxData->FragmentTable[0].FragmentLength;\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (Fragment != NULL) {\r
+    FreePool (Fragment);\r
+  }\r
+\r
+  return Status;\r
+}\r