blob: f378c00f496952064b4bc668c10777e7664ff1ac [file] [log] [blame]
/** @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.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/TcpIoLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
/**
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;
}