mirror of https://github.com/acidanthera/audk.git
1003 lines
27 KiB
C
1003 lines
27 KiB
C
/** @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;
|
|
}
|