+/** @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