/** @file
The driver binding and service binding protocol for the TCP driver.
Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "TcpMain.h"
UINT16 mTcp4RandomPort;
UINT16 mTcp6RandomPort;
TCP_HEARTBEAT_TIMER mTcpTimer = {
NULL,
0
};
EFI_TCP4_PROTOCOL gTcp4ProtocolTemplate = {
Tcp4GetModeData,
Tcp4Configure,
Tcp4Routes,
Tcp4Connect,
Tcp4Accept,
Tcp4Transmit,
Tcp4Receive,
Tcp4Close,
Tcp4Cancel,
Tcp4Poll
};
EFI_TCP6_PROTOCOL gTcp6ProtocolTemplate = {
Tcp6GetModeData,
Tcp6Configure,
Tcp6Connect,
Tcp6Accept,
Tcp6Transmit,
Tcp6Receive,
Tcp6Close,
Tcp6Cancel,
Tcp6Poll
};
SOCK_INIT_DATA mTcpDefaultSockData = {
SockStream,
SO_CLOSED,
NULL,
TCP_BACKLOG,
TCP_SND_BUF_SIZE,
TCP_RCV_BUF_SIZE,
IP_VERSION_4,
NULL,
TcpCreateSocketCallback,
TcpDestroySocketCallback,
NULL,
NULL,
0,
TcpDispatcher,
NULL,
};
EFI_DRIVER_BINDING_PROTOCOL gTcpDriverBinding = {
TcpDriverBindingSupported,
TcpDriverBindingStart,
TcpDriverBindingStop,
0xa,
NULL,
NULL
};
EFI_SERVICE_BINDING_PROTOCOL gTcpServiceBinding = {
TcpServiceBindingCreateChild,
TcpServiceBindingDestroyChild
};
/**
Create and start the heartbeat timer for the TCP driver.
@retval EFI_SUCCESS The timer was successfully created and started.
@retval other The timer was not created.
**/
EFI_STATUS
TcpCreateTimer (
VOID
)
{
EFI_STATUS Status;
Status = EFI_SUCCESS;
if (mTcpTimer.RefCnt == 0) {
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
TcpTicking,
NULL,
&mTcpTimer.TimerEvent
);
if (!EFI_ERROR (Status)) {
Status = gBS->SetTimer (
mTcpTimer.TimerEvent,
TimerPeriodic,
(UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ)
);
}
}
if (!EFI_ERROR (Status)) {
mTcpTimer.RefCnt++;
}
return Status;
}
/**
Stop and destroy the heartbeat timer for TCP driver.
**/
VOID
TcpDestroyTimer (
VOID
)
{
ASSERT (mTcpTimer.RefCnt > 0);
mTcpTimer.RefCnt--;
if (mTcpTimer.RefCnt > 0) {
return;
}
gBS->SetTimer (mTcpTimer.TimerEvent, TimerCancel, 0);
gBS->CloseEvent (mTcpTimer.TimerEvent);
mTcpTimer.TimerEvent = NULL;
}
/**
The entry point for Tcp driver, which is used to install Tcp driver on the ImageHandle.
@param[in] ImageHandle The firmware allocated handle for this driver image.
@param[in] SystemTable Pointer to the EFI system table.
@retval EFI_SUCCESS The driver loaded.
@retval other The driver did not load.
**/
EFI_STATUS
EFIAPI
TcpDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINT32 Seed;
//
// Install the TCP Driver Binding Protocol
//
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gTcpDriverBinding,
ImageHandle,
&gTcpComponentName,
&gTcpComponentName2
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Initialize ISS and random port.
//
Seed = NetRandomInitSeed ();
mTcpGlobalIss = NET_RANDOM (Seed) % mTcpGlobalIss;
mTcp4RandomPort = (UINT16) (TCP_PORT_KNOWN + (NET_RANDOM (Seed) % TCP_PORT_KNOWN));
mTcp6RandomPort = mTcp4RandomPort;
return EFI_SUCCESS;
}
/**
Create a new TCP4 or TCP6 driver service binding protocol
@param[in] Controller Controller handle of device to bind driver to.
@param[in] Image The TCP driver's image handle.
@param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
@retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.
@retval EFI_SUCCESS A new IP6 service binding private was created.
**/
EFI_STATUS
TcpCreateService (
IN EFI_HANDLE Controller,
IN EFI_HANDLE Image,
IN UINT8 IpVersion
)
{
EFI_STATUS Status;
EFI_GUID *IpServiceBindingGuid;
EFI_GUID *TcpServiceBindingGuid;
TCP_SERVICE_DATA *TcpServiceData;
IP_IO_OPEN_DATA OpenData;
if (IpVersion == IP_VERSION_4) {
IpServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid;
TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
} else {
IpServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid;
TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
}
Status = gBS->OpenProtocol (
Controller,
TcpServiceBindingGuid,
NULL,
Image,
Controller,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (!EFI_ERROR (Status)) {
return EFI_ALREADY_STARTED;
}
Status = gBS->OpenProtocol (
Controller,
IpServiceBindingGuid,
NULL,
Image,
Controller,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
//
// Create the TCP service data.
//
TcpServiceData = AllocateZeroPool (sizeof (TCP_SERVICE_DATA));
if (TcpServiceData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
TcpServiceData->Signature = TCP_DRIVER_SIGNATURE;
TcpServiceData->ControllerHandle = Controller;
TcpServiceData->DriverBindingHandle = Image;
TcpServiceData->IpVersion = IpVersion;
CopyMem (
&TcpServiceData->ServiceBinding,
&gTcpServiceBinding,
sizeof (EFI_SERVICE_BINDING_PROTOCOL)
);
TcpServiceData->IpIo = IpIoCreate (Image, Controller, IpVersion);
if (TcpServiceData->IpIo == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_ERROR;
}
InitializeListHead (&TcpServiceData->SocketList);
ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA));
if (IpVersion == IP_VERSION_4) {
CopyMem (
&OpenData.IpConfigData.Ip4CfgData,
&mIp4IoDefaultIpConfigData,
sizeof (EFI_IP4_CONFIG_DATA)
);
OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
} else {
CopyMem (
&OpenData.IpConfigData.Ip6CfgData,
&mIp6IoDefaultIpConfigData,
sizeof (EFI_IP6_CONFIG_DATA)
);
OpenData.IpConfigData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
}
OpenData.PktRcvdNotify = TcpRxCallback;
Status = IpIoOpen (TcpServiceData->IpIo, &OpenData);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
Status = TcpCreateTimer ();
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
Status = gBS->InstallMultipleProtocolInterfaces (
&Controller,
TcpServiceBindingGuid,
&TcpServiceData->ServiceBinding,
NULL
);
if (EFI_ERROR (Status)) {
TcpDestroyTimer ();
goto ON_ERROR;
}
TcpSetVariableData (TcpServiceData);
return EFI_SUCCESS;
ON_ERROR:
if (TcpServiceData->IpIo != NULL) {
IpIoDestroy (TcpServiceData->IpIo);
}
FreePool (TcpServiceData);
return Status;
}
/**
Destroy a TCP6 or TCP4 service binding instance. It will release all
the resources allocated by the instance.
@param[in] Controller Controller handle of device to bind driver to.
@param[in] ImageHandle The TCP driver's image handle.
@param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number
of children is zero stop the entire bus driver.
@param[in] IpVersion IP_VERSION_4 or IP_VERSION_6
@retval EFI_SUCCESS The resources used by the instance were cleaned up.
@retval Others Failed to clean up some of the resources.
**/
EFI_STATUS
TcpDestroyService (
IN EFI_HANDLE Controller,
IN EFI_HANDLE ImageHandle,
IN UINTN NumberOfChildren,
IN UINT8 IpVersion
)
{
EFI_HANDLE NicHandle;
EFI_GUID *IpProtocolGuid;
EFI_GUID *ServiceBindingGuid;
EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
TCP_SERVICE_DATA *TcpServiceData;
EFI_STATUS Status;
SOCKET *Sock;
ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));
if (IpVersion == IP_VERSION_4) {
IpProtocolGuid = &gEfiIp4ProtocolGuid;
ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
} else {
IpProtocolGuid = &gEfiIp6ProtocolGuid;
ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
}
NicHandle = NetLibGetNicHandle (Controller, IpProtocolGuid);
if (NicHandle == NULL) {
return EFI_NOT_FOUND;
}
Status = gBS->OpenProtocol (
NicHandle,
ServiceBindingGuid,
(VOID **) &ServiceBinding,
ImageHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
TcpServiceData = TCP_SERVICE_FROM_THIS (ServiceBinding);
if (NumberOfChildren == 0) {
//
// Uninstall TCP servicebinding protocol
//
gBS->UninstallMultipleProtocolInterfaces (
NicHandle,
ServiceBindingGuid,
ServiceBinding,
NULL
);
//
// Destroy the IpIO consumed by TCP driver
//
IpIoDestroy (TcpServiceData->IpIo);
//
// Destroy the heartbeat timer.
//
TcpDestroyTimer ();
//
// Clear the variable.
//
TcpClearVariableData (TcpServiceData);
//
// Release the TCP service data
//
FreePool (TcpServiceData);
} else {
while (!IsListEmpty (&TcpServiceData->SocketList)) {
Sock = NET_LIST_HEAD (&TcpServiceData->SocketList, SOCKET, Link);
ServiceBinding->DestroyChild (ServiceBinding, Sock->SockHandle);
}
}
return EFI_SUCCESS;
}
/**
Test to see if this driver supports ControllerHandle.
@param[in] This Protocol instance pointer.
@param[in] ControllerHandle Handle of device to test.
@param[in] RemainingDevicePath Optional parameter use to pick a specific
child device to start.
@retval EFI_SUCCESS This driver supports this device.
@retval EFI_ALREADY_STARTED This driver is already running on this device.
@retval other This driver does not support this device.
**/
EFI_STATUS
EFIAPI
TcpDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
BOOLEAN IsTcp4Started;
//
// Test for the Tcp4ServiceBinding Protocol
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiTcp4ServiceBindingProtocolGuid,
NULL,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (EFI_ERROR (Status)) {
//
// Test for the Ip4ServiceBinding Protocol
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiIp4ServiceBindingProtocolGuid,
NULL,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (!EFI_ERROR (Status)) {
return EFI_SUCCESS;
}
IsTcp4Started = FALSE;
} else {
IsTcp4Started = TRUE;
}
//
// Check the Tcp6ServiceBinding Protocol
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiTcp6ServiceBindingProtocolGuid,
NULL,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (EFI_ERROR (Status)) {
//
// Test for the Ip6ServiceBinding Protocol
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiIp6ServiceBindingProtocolGuid,
NULL,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (!EFI_ERROR (Status)) {
return EFI_SUCCESS;
}
} else if (IsTcp4Started) {
return EFI_ALREADY_STARTED;
}
return EFI_UNSUPPORTED;
}
/**
Start this driver on ControllerHandle.
@param[in] This Protocol instance pointer.
@param[in] ControllerHandle Handle of device to bind driver to.
@param[in] RemainingDevicePath Optional parameter use to pick a specific child
device to start.
@retval EFI_SUCCESS The driver is added to ControllerHandle.
@retval EFI_OUT_OF_RESOURCES There are not enough resources to start the
driver.
@retval other The driver cannot be added to ControllerHandle.
**/
EFI_STATUS
EFIAPI
TcpDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Tcp4Status;
EFI_STATUS Tcp6Status;
Tcp4Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_4);
if ((Tcp4Status == EFI_ALREADY_STARTED) || (Tcp4Status == EFI_UNSUPPORTED)) {
Tcp4Status = EFI_SUCCESS;
}
Tcp6Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_6);
if ((Tcp6Status == EFI_ALREADY_STARTED) || (Tcp6Status == EFI_UNSUPPORTED)) {
Tcp6Status = EFI_SUCCESS;
}
if (!EFI_ERROR (Tcp4Status) || !EFI_ERROR (Tcp6Status)) {
return EFI_SUCCESS;
} else if (EFI_ERROR (Tcp4Status)) {
return Tcp4Status;
} else {
return Tcp6Status;
}
}
/**
Stop this driver on ControllerHandle.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle A handle to the device being stopped. The handle must
support a bus specific I/O protocol for the driver
to use to stop the device.
@param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
@param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
if NumberOfChildren is 0.
@retval EFI_SUCCESS The device was stopped.
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
**/
EFI_STATUS
EFIAPI
TcpDriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
)
{
EFI_STATUS Tcp4Status;
EFI_STATUS Tcp6Status;
Tcp4Status = TcpDestroyService (
ControllerHandle,
This->DriverBindingHandle,
NumberOfChildren,
IP_VERSION_4
);
Tcp6Status = TcpDestroyService (
ControllerHandle,
This->DriverBindingHandle,
NumberOfChildren,
IP_VERSION_6
);
if (EFI_ERROR (Tcp4Status) && EFI_ERROR (Tcp6Status)) {
return EFI_DEVICE_ERROR;
} else {
return EFI_SUCCESS;
}
}
/**
The Callback funtion called after the TCP socket was created.
@param[in] This Pointer to the socket just created
@param[in] Context Context of the socket
@retval EFI_SUCCESS This protocol installed successfully.
@retval other An error occured.
**/
EFI_STATUS
TcpCreateSocketCallback (
IN SOCKET *This,
IN VOID *Context
)
{
EFI_STATUS Status;
TCP_SERVICE_DATA *TcpServiceData;
EFI_GUID *IpProtocolGuid;
VOID *Ip;
if (This->IpVersion == IP_VERSION_4) {
IpProtocolGuid = &gEfiIp4ProtocolGuid;
} else {
IpProtocolGuid = &gEfiIp6ProtocolGuid;
}
TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService;
//
// Open the default IP protocol of IP_IO BY_DRIVER.
//
Status = gBS->OpenProtocol (
TcpServiceData->IpIo->ChildHandle,
IpProtocolGuid,
&Ip,
TcpServiceData->DriverBindingHandle,
This->SockHandle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Open the device path on the handle where service binding resides on.
//
Status = gBS->OpenProtocol (
TcpServiceData->ControllerHandle,
&gEfiDevicePathProtocolGuid,
(VOID **) &This->ParentDevicePath,
TcpServiceData->DriverBindingHandle,
This->SockHandle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
if (EFI_ERROR (Status)) {
gBS->CloseProtocol (
TcpServiceData->IpIo->ChildHandle,
IpProtocolGuid,
TcpServiceData->DriverBindingHandle,
This->SockHandle
);
} else {
//
// Insert this socket into the SocketList.
//
InsertTailList (&TcpServiceData->SocketList, &This->Link);
}
return Status;
}
/**
The callback function called before the TCP socket was to be destroyed.
@param[in] This The TCP socket to be destroyed.
@param[in] Context The context of the socket.
**/
VOID
TcpDestroySocketCallback (
IN SOCKET *This,
IN VOID *Context
)
{
TCP_SERVICE_DATA *TcpServiceData;
EFI_GUID *IpProtocolGuid;
if (This->IpVersion == IP_VERSION_4) {
IpProtocolGuid = &gEfiIp4ProtocolGuid;
} else {
IpProtocolGuid = &gEfiIp6ProtocolGuid;
}
TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService;
//
// Remove this node from the list.
//
RemoveEntryList (&This->Link);
//
// Close the device path protocol
//
gBS->CloseProtocol (
TcpServiceData->ControllerHandle,
&gEfiDevicePathProtocolGuid,
TcpServiceData->DriverBindingHandle,
This->SockHandle
);
//
// Close the IP protocol.
//
gBS->CloseProtocol (
TcpServiceData->IpIo->ChildHandle,
IpProtocolGuid,
TcpServiceData->DriverBindingHandle,
This->SockHandle
);
}
/**
Creates a child handle with a set of TCP services.
The CreateChild() function installs a protocol on ChildHandle.
If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
@param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
@param[in, out] ChildHandle Pointer to the handle of the child to create.
If it is NULL, then a new handle is created.
If it is a pointer to an existing UEFI handle,
then the protocol is added to the existing UEFI handle.
@retval EFI_SUCCES The protocol was added to ChildHandle.
@retval EFI_INVALID_PARAMETER ChildHandle is NULL.
@retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
the child.
@retval other The child handle was not created.
**/
EFI_STATUS
EFIAPI
TcpServiceBindingCreateChild (
IN EFI_SERVICE_BINDING_PROTOCOL *This,
IN OUT EFI_HANDLE *ChildHandle
)
{
SOCKET *Sock;
TCP_SERVICE_DATA *TcpServiceData;
TCP_PROTO_DATA TcpProto;
EFI_STATUS Status;
EFI_TPL OldTpl;
if (NULL == This || NULL == ChildHandle) {
return EFI_INVALID_PARAMETER;
}
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
Status = EFI_SUCCESS;
TcpServiceData = TCP_SERVICE_FROM_THIS (This);
TcpProto.TcpService = TcpServiceData;
TcpProto.TcpPcb = NULL;
//
// Create a tcp instance with defualt Tcp default
// sock init data and TcpProto
//
mTcpDefaultSockData.ProtoData = &TcpProto;
mTcpDefaultSockData.DataSize = sizeof (TCP_PROTO_DATA);
mTcpDefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle;
mTcpDefaultSockData.IpVersion = TcpServiceData->IpVersion;
if (TcpServiceData->IpVersion == IP_VERSION_4) {
mTcpDefaultSockData.Protocol = &gTcp4ProtocolTemplate;
} else {
mTcpDefaultSockData.Protocol = &gTcp6ProtocolTemplate;
}
Sock = SockCreateChild (&mTcpDefaultSockData);
if (NULL == Sock) {
DEBUG (
(EFI_D_ERROR,
"TcpDriverBindingCreateChild: No resource to create a Tcp Child\n")
);
Status = EFI_OUT_OF_RESOURCES;
} else {
*ChildHandle = Sock->SockHandle;
}
mTcpDefaultSockData.ProtoData = NULL;
gBS->RestoreTPL (OldTpl);
return Status;
}
/**
Destroys a child handle with a set of TCP services.
The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
that was installed by CreateChild() from ChildHandle. If the removed protocol is the
last protocol on ChildHandle, then ChildHandle is destroyed.
@param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
@param ChildHandle Handle of the child to be destroyed.
@retval EFI_SUCCES The protocol was removed from ChildHandle.
@retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
@retval EFI_INVALID_PARAMETER Child handle is NULL.
@retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
because its services are being used.
@retval other The child handle was not destroyed.
**/
EFI_STATUS
EFIAPI
TcpServiceBindingDestroyChild (
IN EFI_SERVICE_BINDING_PROTOCOL *This,
IN EFI_HANDLE ChildHandle
)
{
EFI_STATUS Status;
VOID *Tcp;
SOCKET *Sock;
EFI_TPL OldTpl;
if (NULL == This || NULL == ChildHandle) {
return EFI_INVALID_PARAMETER;
}
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
//
// retrieve the Tcp4 protocol from ChildHandle
//
Status = gBS->OpenProtocol (
ChildHandle,
&gEfiTcp4ProtocolGuid,
&Tcp,
gTcpDriverBinding.DriverBindingHandle,
ChildHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
//
// No Tcp4, try the Tcp6 protocol
//
Status = gBS->OpenProtocol (
ChildHandle,
&gEfiTcp6ProtocolGuid,
&Tcp,
gTcpDriverBinding.DriverBindingHandle,
ChildHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
Status = EFI_UNSUPPORTED;
}
}
if (!EFI_ERROR (Status)) {
//
// destroy this sock and related Tcp protocol control
// block
//
Sock = SOCK_FROM_THIS (Tcp);
SockDestroyChild (Sock);
}
gBS->RestoreTPL (OldTpl);
return Status;
}