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