CloverBootloader/NetworkPkg/Library/DxeUdpIoLib/DxeUdpIoLib.c

1123 lines
32 KiB
C
Raw Normal View History

/** @file
Help functions to access UDP service, it is used by both the DHCP and MTFTP.
Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Protocol/Udp4.h>
#include <Protocol/Udp6.h>
#include <Library/UdpIoLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DpcLib.h>
/**
Free a UDP_TX_TOKEN. The TX event is closed.
@param[in] TxToken The UDP_TX_TOKEN to release.
**/
VOID
UdpIoFreeTxToken (
IN UDP_TX_TOKEN *TxToken
)
{
if (TxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
gBS->CloseEvent (TxToken->Token.Udp4.Event);
} else if (TxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION) {
gBS->CloseEvent (TxToken->Token.Udp6.Event);
} else {
ASSERT (FALSE);
}
FreePool (TxToken);
}
/**
Free a UDP_RX_TOKEN. The RX event is closed.
@param[in] RxToken The UDP_RX_TOKEN to release.
**/
VOID
UdpIoFreeRxToken (
IN UDP_RX_TOKEN *RxToken
)
{
if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
gBS->CloseEvent (RxToken->Token.Udp4.Event);
} else if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION) {
gBS->CloseEvent (RxToken->Token.Udp6.Event);
} else {
ASSERT (FALSE);
}
FreePool (RxToken);
}
/**
The callback function when the packet is sent by UDP.
It will remove the packet from the local list then call
the packet owner's callback function set by UdpIoSendDatagram.
@param[in] Context The UDP TX Token.
**/
VOID
EFIAPI
UdpIoOnDgramSentDpc (
IN VOID *Context
)
{
UDP_TX_TOKEN *TxToken;
TxToken = (UDP_TX_TOKEN *)Context;
ASSERT (TxToken->Signature == UDP_IO_TX_SIGNATURE);
ASSERT (
(TxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
(TxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION)
);
RemoveEntryList (&TxToken->Link);
if (TxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
TxToken->CallBack (TxToken->Packet, NULL, TxToken->Token.Udp4.Status, TxToken->Context);
} else {
TxToken->CallBack (TxToken->Packet, NULL, TxToken->Token.Udp6.Status, TxToken->Context);
}
UdpIoFreeTxToken (TxToken);
}
/**
Request UdpIoOnDgramSentDpc as a DPC at TPL_CALLBACK.
@param[in] Event The event signaled.
@param[in] Context The UDP TX Token.
**/
VOID
EFIAPI
UdpIoOnDgramSent (
IN EFI_EVENT Event,
IN VOID *Context
)
{
//
// Request UdpIoOnDgramSentDpc as a DPC at TPL_CALLBACK
//
QueueDpc (TPL_CALLBACK, UdpIoOnDgramSentDpc, Context);
}
/**
Recycle the received UDP data.
@param[in] Context The UDP_RX_TOKEN.
**/
VOID
EFIAPI
UdpIoRecycleDgram (
IN VOID *Context
)
{
UDP_RX_TOKEN *RxToken;
RxToken = (UDP_RX_TOKEN *)Context;
if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
gBS->SignalEvent (RxToken->Token.Udp4.Packet.RxData->RecycleSignal);
} else if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION) {
gBS->SignalEvent (RxToken->Token.Udp6.Packet.RxData->RecycleSignal);
} else {
ASSERT (FALSE);
}
UdpIoFreeRxToken (RxToken);
}
/**
The event handle for UDP receive request.
It will build a NET_BUF from the received UDP data, then deliver it
to the receiver.
@param[in] Context The UDP RX token.
**/
VOID
EFIAPI
UdpIoOnDgramRcvdDpc (
IN VOID *Context
)
{
EFI_STATUS Status;
VOID *Token;
VOID *RxData;
VOID *Session;
UDP_RX_TOKEN *RxToken;
UDP_END_POINT EndPoint;
NET_BUF *Netbuf;
RxToken = (UDP_RX_TOKEN *)Context;
ZeroMem (&EndPoint, sizeof (UDP_END_POINT));
ASSERT (
(RxToken->Signature == UDP_IO_RX_SIGNATURE) &&
(RxToken == RxToken->UdpIo->RecvRequest)
);
ASSERT (
(RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
(RxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION)
);
//
// Clear the receive request first in case that the caller
// wants to restart the receive in the callback.
//
RxToken->UdpIo->RecvRequest = NULL;
if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
Token = &RxToken->Token.Udp4;
RxData = ((EFI_UDP4_COMPLETION_TOKEN *)Token)->Packet.RxData;
Status = ((EFI_UDP4_COMPLETION_TOKEN *)Token)->Status;
} else {
Token = &RxToken->Token.Udp6;
RxData = ((EFI_UDP6_COMPLETION_TOKEN *)Token)->Packet.RxData;
Status = ((EFI_UDP6_COMPLETION_TOKEN *)Token)->Status;
}
if (EFI_ERROR (Status) || (RxData == NULL)) {
if (Status != EFI_ABORTED) {
//
// Invoke the CallBack only if the reception is not actively aborted.
//
RxToken->CallBack (NULL, NULL, Status, RxToken->Context);
}
UdpIoFreeRxToken (RxToken);
return;
}
//
// Build a NET_BUF from the UDP receive data, then deliver it up.
//
if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
if (((EFI_UDP4_RECEIVE_DATA *)RxData)->DataLength == 0) {
//
// Discard zero length data payload packet.
//
goto Resume;
}
Netbuf = NetbufFromExt (
(NET_FRAGMENT *)((EFI_UDP4_RECEIVE_DATA *)RxData)->FragmentTable,
((EFI_UDP4_RECEIVE_DATA *)RxData)->FragmentCount,
0,
(UINT32)RxToken->HeadLen,
UdpIoRecycleDgram,
RxToken
);
if (Netbuf == NULL) {
gBS->SignalEvent (((EFI_UDP4_RECEIVE_DATA *)RxData)->RecycleSignal);
RxToken->CallBack (NULL, NULL, EFI_OUT_OF_RESOURCES, RxToken->Context);
UdpIoFreeRxToken (RxToken);
return;
}
Session = &((EFI_UDP4_RECEIVE_DATA *)RxData)->UdpSession;
EndPoint.LocalPort = ((EFI_UDP4_SESSION_DATA *)Session)->DestinationPort;
EndPoint.RemotePort = ((EFI_UDP4_SESSION_DATA *)Session)->SourcePort;
CopyMem (
&EndPoint.LocalAddr,
&((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress,
sizeof (EFI_IPv4_ADDRESS)
);
CopyMem (
&EndPoint.RemoteAddr,
&((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress,
sizeof (EFI_IPv4_ADDRESS)
);
EndPoint.LocalAddr.Addr[0] = NTOHL (EndPoint.LocalAddr.Addr[0]);
EndPoint.RemoteAddr.Addr[0] = NTOHL (EndPoint.RemoteAddr.Addr[0]);
} else {
if (((EFI_UDP6_RECEIVE_DATA *)RxData)->DataLength == 0) {
//
// Discard zero length data payload packet.
//
goto Resume;
}
Netbuf = NetbufFromExt (
(NET_FRAGMENT *)((EFI_UDP6_RECEIVE_DATA *)RxData)->FragmentTable,
((EFI_UDP6_RECEIVE_DATA *)RxData)->FragmentCount,
0,
(UINT32)RxToken->HeadLen,
UdpIoRecycleDgram,
RxToken
);
if (Netbuf == NULL) {
gBS->SignalEvent (((EFI_UDP6_RECEIVE_DATA *)RxData)->RecycleSignal);
RxToken->CallBack (NULL, NULL, EFI_OUT_OF_RESOURCES, RxToken->Context);
UdpIoFreeRxToken (RxToken);
return;
}
Session = &((EFI_UDP6_RECEIVE_DATA *)RxData)->UdpSession;
EndPoint.LocalPort = ((EFI_UDP6_SESSION_DATA *)Session)->DestinationPort;
EndPoint.RemotePort = ((EFI_UDP6_SESSION_DATA *)Session)->SourcePort;
CopyMem (
&EndPoint.LocalAddr,
&((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress,
sizeof (EFI_IPv6_ADDRESS)
);
CopyMem (
&EndPoint.RemoteAddr,
&((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress,
sizeof (EFI_IPv6_ADDRESS)
);
Ip6Swap128 (&EndPoint.LocalAddr.v6);
Ip6Swap128 (&EndPoint.RemoteAddr.v6);
}
RxToken->CallBack (Netbuf, &EndPoint, EFI_SUCCESS, RxToken->Context);
return;
Resume:
if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
gBS->SignalEvent (((EFI_UDP4_RECEIVE_DATA *)RxData)->RecycleSignal);
RxToken->UdpIo->Protocol.Udp4->Receive (RxToken->UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
} else {
gBS->SignalEvent (((EFI_UDP6_RECEIVE_DATA *)RxData)->RecycleSignal);
RxToken->UdpIo->Protocol.Udp6->Receive (RxToken->UdpIo->Protocol.Udp6, &RxToken->Token.Udp6);
}
}
/**
Request UdpIoOnDgramRcvdDpc() as a DPC at TPL_CALLBACK.
@param[in] Event The UDP receive request event.
@param[in] Context The UDP RX token.
**/
VOID
EFIAPI
UdpIoOnDgramRcvd (
IN EFI_EVENT Event,
IN VOID *Context
)
{
//
// Request UdpIoOnDgramRcvdDpc as a DPC at TPL_CALLBACK
//
QueueDpc (TPL_CALLBACK, UdpIoOnDgramRcvdDpc, Context);
}
/**
Create a UDP_RX_TOKEN to wrap the request.
@param[in] UdpIo The UdpIo to receive packets from.
@param[in] CallBack The function to call when receive finished.
@param[in] Context The opaque parameter to the CallBack.
@param[in] HeadLen The head length to reserve for the packet.
@return The Wrapped request or NULL if failed to allocate resources or some errors happened.
**/
UDP_RX_TOKEN *
UdpIoCreateRxToken (
IN UDP_IO *UdpIo,
IN UDP_IO_CALLBACK CallBack,
IN VOID *Context,
IN UINT32 HeadLen
)
{
UDP_RX_TOKEN *Token;
EFI_STATUS Status;
ASSERT (
(UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
(UdpIo->UdpVersion == UDP_IO_UDP6_VERSION)
);
Token = AllocatePool (sizeof (UDP_RX_TOKEN));
if (Token == NULL) {
return NULL;
}
Token->Signature = UDP_IO_RX_SIGNATURE;
Token->UdpIo = UdpIo;
Token->CallBack = CallBack;
Token->Context = Context;
Token->HeadLen = HeadLen;
if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
Token->Token.Udp4.Status = EFI_NOT_READY;
Token->Token.Udp4.Packet.RxData = NULL;
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
UdpIoOnDgramRcvd,
Token,
&Token->Token.Udp4.Event
);
} else {
Token->Token.Udp6.Status = EFI_NOT_READY;
Token->Token.Udp6.Packet.RxData = NULL;
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
UdpIoOnDgramRcvd,
Token,
&Token->Token.Udp6.Event
);
}
if (EFI_ERROR (Status)) {
FreePool (Token);
return NULL;
}
return Token;
}
/**
Wrap a transmit request into a new created UDP_TX_TOKEN.
If Packet is NULL, then ASSERT().
If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
@param[in] UdpIo The UdpIo to send packet to.
@param[in] Packet The user's packet.
@param[in] EndPoint The local and remote access point.
@param[in] Gateway The overridden next hop.
@param[in] CallBack The function to call when transmission completed.
@param[in] Context The opaque parameter to the call back.
@return The wrapped transmission request or NULL if failed to allocate resources
or for some errors.
**/
UDP_TX_TOKEN *
UdpIoCreateTxToken (
IN UDP_IO *UdpIo,
IN NET_BUF *Packet,
IN UDP_END_POINT *EndPoint OPTIONAL,
IN EFI_IP_ADDRESS *Gateway OPTIONAL,
IN UDP_IO_CALLBACK CallBack,
IN VOID *Context
)
{
UDP_TX_TOKEN *TxToken;
VOID *Token;
VOID *Data;
EFI_STATUS Status;
UINT32 Count;
UINTN Size;
IP4_ADDR Ip;
ASSERT (Packet != NULL);
ASSERT (
(UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
(UdpIo->UdpVersion == UDP_IO_UDP6_VERSION)
);
if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
Size = sizeof (UDP_TX_TOKEN) + sizeof (EFI_UDP4_FRAGMENT_DATA) * (Packet->BlockOpNum - 1);
} else {
Size = sizeof (UDP_TX_TOKEN) + sizeof (EFI_UDP6_FRAGMENT_DATA) * (Packet->BlockOpNum - 1);
}
TxToken = AllocatePool (Size);
if (TxToken == NULL) {
return NULL;
}
TxToken->Signature = UDP_IO_TX_SIGNATURE;
InitializeListHead (&TxToken->Link);
TxToken->UdpIo = UdpIo;
TxToken->CallBack = CallBack;
TxToken->Packet = Packet;
TxToken->Context = Context;
Token = &(TxToken->Token);
Count = Packet->BlockOpNum;
if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
((EFI_UDP4_COMPLETION_TOKEN *)Token)->Status = EFI_NOT_READY;
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
UdpIoOnDgramSent,
TxToken,
&((EFI_UDP4_COMPLETION_TOKEN *)Token)->Event
);
if (EFI_ERROR (Status)) {
FreePool (TxToken);
return NULL;
}
Data = &(TxToken->Data.Udp4);
((EFI_UDP4_COMPLETION_TOKEN *)Token)->Packet.TxData = Data;
((EFI_UDP4_TRANSMIT_DATA *)Data)->UdpSessionData = NULL;
((EFI_UDP4_TRANSMIT_DATA *)Data)->GatewayAddress = NULL;
((EFI_UDP4_TRANSMIT_DATA *)Data)->DataLength = Packet->TotalSize;
NetbufBuildExt (
Packet,
(NET_FRAGMENT *)((EFI_UDP4_TRANSMIT_DATA *)Data)->FragmentTable,
&Count
);
((EFI_UDP4_TRANSMIT_DATA *)Data)->FragmentCount = Count;
if (EndPoint != NULL) {
Ip = HTONL (EndPoint->LocalAddr.Addr[0]);
CopyMem (
&TxToken->Session.Udp4.SourceAddress,
&Ip,
sizeof (EFI_IPv4_ADDRESS)
);
Ip = HTONL (EndPoint->RemoteAddr.Addr[0]);
CopyMem (
&TxToken->Session.Udp4.DestinationAddress,
&Ip,
sizeof (EFI_IPv4_ADDRESS)
);
TxToken->Session.Udp4.SourcePort = EndPoint->LocalPort;
TxToken->Session.Udp4.DestinationPort = EndPoint->RemotePort;
((EFI_UDP4_TRANSMIT_DATA *)Data)->UdpSessionData = &(TxToken->Session.Udp4);
}
if ((Gateway != NULL) && (Gateway->Addr[0] != 0)) {
Ip = HTONL (Gateway->Addr[0]);
CopyMem (&TxToken->Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));
((EFI_UDP4_TRANSMIT_DATA *)Data)->GatewayAddress = &TxToken->Gateway;
}
} else {
((EFI_UDP6_COMPLETION_TOKEN *)Token)->Status = EFI_NOT_READY;
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
UdpIoOnDgramSent,
TxToken,
&((EFI_UDP6_COMPLETION_TOKEN *)Token)->Event
);
if (EFI_ERROR (Status)) {
FreePool (TxToken);
return NULL;
}
Data = &(TxToken->Data.Udp6);
((EFI_UDP6_COMPLETION_TOKEN *)Token)->Packet.TxData = Data;
((EFI_UDP6_TRANSMIT_DATA *)Data)->UdpSessionData = NULL;
((EFI_UDP6_TRANSMIT_DATA *)Data)->DataLength = Packet->TotalSize;
NetbufBuildExt (
Packet,
(NET_FRAGMENT *)((EFI_UDP6_TRANSMIT_DATA *)Data)->FragmentTable,
&Count
);
((EFI_UDP6_TRANSMIT_DATA *)Data)->FragmentCount = Count;
if (EndPoint != NULL) {
CopyMem (
&TxToken->Session.Udp6.SourceAddress,
&EndPoint->LocalAddr.v6,
sizeof (EFI_IPv6_ADDRESS)
);
CopyMem (
&TxToken->Session.Udp6.DestinationAddress,
&EndPoint->RemoteAddr.v6,
sizeof (EFI_IPv6_ADDRESS)
);
TxToken->Session.Udp6.SourcePort = EndPoint->LocalPort;
TxToken->Session.Udp6.DestinationPort = EndPoint->RemotePort;
((EFI_UDP6_TRANSMIT_DATA *)Data)->UdpSessionData = &(TxToken->Session.Udp6);
}
}
return TxToken;
}
/**
Creates a UDP_IO to access the UDP service. It creates and configures
a UDP child.
If Configure is NULL, then ASSERT().
If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
It locates the UDP service binding prototype on the Controller parameter
uses the UDP service binding prototype to create a UDP child (also known as
a UDP instance) configures the UDP child by calling Configure function prototype.
Any failures in creating or configuring the UDP child return NULL for failure.
@param[in] Controller The controller that has the UDP service binding.
protocol installed.
@param[in] ImageHandle The image handle for the driver.
@param[in] Configure The function to configure the created UDP child.
@param[in] UdpVersion The UDP protocol version, UDP4 or UDP6.
@param[in] Context The opaque parameter for the Configure function.
@return Newly-created UDP_IO or NULL if failed.
**/
UDP_IO *
EFIAPI
UdpIoCreateIo (
IN EFI_HANDLE Controller,
IN EFI_HANDLE ImageHandle,
IN UDP_IO_CONFIG Configure,
IN UINT8 UdpVersion,
IN VOID *Context
)
{
UDP_IO *UdpIo;
EFI_STATUS Status;
ASSERT (Configure != NULL);
ASSERT ((UdpVersion == UDP_IO_UDP4_VERSION) || (UdpVersion == UDP_IO_UDP6_VERSION));
UdpIo = AllocatePool (sizeof (UDP_IO));
if (UdpIo == NULL) {
return NULL;
}
UdpIo->UdpVersion = UdpVersion;
UdpIo->Signature = UDP_IO_SIGNATURE;
InitializeListHead (&UdpIo->Link);
UdpIo->RefCnt = 1;
UdpIo->Controller = Controller;
UdpIo->Image = ImageHandle;
InitializeListHead (&UdpIo->SentDatagram);
UdpIo->RecvRequest = NULL;
UdpIo->UdpHandle = NULL;
if (UdpVersion == UDP_IO_UDP4_VERSION) {
//
// Create a UDP child then open and configure it
//
Status = NetLibCreateServiceChild (
Controller,
ImageHandle,
&gEfiUdp4ServiceBindingProtocolGuid,
&UdpIo->UdpHandle
);
if (EFI_ERROR (Status)) {
goto FREE_MEM;
}
Status = gBS->OpenProtocol (
UdpIo->UdpHandle,
&gEfiUdp4ProtocolGuid,
(VOID **)&UdpIo->Protocol.Udp4,
ImageHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
goto FREE_CHILD;
}
if (EFI_ERROR (Configure (UdpIo, Context))) {
goto CLOSE_PROTOCOL;
}
Status = UdpIo->Protocol.Udp4->GetModeData (
UdpIo->Protocol.Udp4,
NULL,
NULL,
NULL,
&UdpIo->SnpMode
);
if (EFI_ERROR (Status)) {
goto CLOSE_PROTOCOL;
}
} else {
Status = NetLibCreateServiceChild (
Controller,
ImageHandle,
&gEfiUdp6ServiceBindingProtocolGuid,
&UdpIo->UdpHandle
);
if (EFI_ERROR (Status)) {
goto FREE_MEM;
}
Status = gBS->OpenProtocol (
UdpIo->UdpHandle,
&gEfiUdp6ProtocolGuid,
(VOID **)&UdpIo->Protocol.Udp6,
ImageHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
goto FREE_CHILD;
}
if (EFI_ERROR (Configure (UdpIo, Context))) {
goto CLOSE_PROTOCOL;
}
Status = UdpIo->Protocol.Udp6->GetModeData (
UdpIo->Protocol.Udp6,
NULL,
NULL,
NULL,
&UdpIo->SnpMode
);
if (EFI_ERROR (Status)) {
goto CLOSE_PROTOCOL;
}
}
return UdpIo;
CLOSE_PROTOCOL:
if (UdpVersion == UDP_IO_UDP4_VERSION) {
gBS->CloseProtocol (UdpIo->UdpHandle, &gEfiUdp4ProtocolGuid, ImageHandle, Controller);
} else {
gBS->CloseProtocol (UdpIo->UdpHandle, &gEfiUdp6ProtocolGuid, ImageHandle, Controller);
}
FREE_CHILD:
if (UdpVersion == UDP_IO_UDP4_VERSION) {
NetLibDestroyServiceChild (
Controller,
ImageHandle,
&gEfiUdp4ServiceBindingProtocolGuid,
UdpIo->UdpHandle
);
} else {
NetLibDestroyServiceChild (
Controller,
ImageHandle,
&gEfiUdp6ServiceBindingProtocolGuid,
UdpIo->UdpHandle
);
}
FREE_MEM:
FreePool (UdpIo);
return NULL;
}
/**
Cancel all the sent datagram that pass the selection criteria of ToCancel.
If ToCancel is NULL, all the datagrams are cancelled.
If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
@param[in] UdpIo The UDP_IO to cancel packet.
@param[in] IoStatus The IoStatus to return to the packet owners.
@param[in] ToCancel The select function to test whether to cancel this
packet or not.
@param[in] Context The opaque parameter to the ToCancel.
**/
VOID
EFIAPI
UdpIoCancelDgrams (
IN UDP_IO *UdpIo,
IN EFI_STATUS IoStatus,
IN UDP_IO_TO_CANCEL ToCancel OPTIONAL,
IN VOID *Context OPTIONAL
)
{
LIST_ENTRY *Entry;
LIST_ENTRY *Next;
UDP_TX_TOKEN *TxToken;
ASSERT (
(UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
(UdpIo->UdpVersion == UDP_IO_UDP6_VERSION)
);
NET_LIST_FOR_EACH_SAFE (Entry, Next, &UdpIo->SentDatagram) {
TxToken = NET_LIST_USER_STRUCT (Entry, UDP_TX_TOKEN, Link);
if ((ToCancel == NULL) || (ToCancel (TxToken, Context))) {
if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
UdpIo->Protocol.Udp4->Cancel (UdpIo->Protocol.Udp4, &TxToken->Token.Udp4);
} else {
UdpIo->Protocol.Udp6->Cancel (UdpIo->Protocol.Udp6, &TxToken->Token.Udp6);
}
}
}
}
/**
Free the UDP_IO and all its related resources.
If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
The function will cancel all sent datagram and receive request.
@param[in] UdpIo The UDP_IO to free.
@retval EFI_SUCCESS The UDP_IO is freed.
@retval Others Failed to free UDP_IO.
**/
EFI_STATUS
EFIAPI
UdpIoFreeIo (
IN UDP_IO *UdpIo
)
{
EFI_STATUS Status;
UDP_RX_TOKEN *RxToken;
ASSERT (
(UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
(UdpIo->UdpVersion == UDP_IO_UDP6_VERSION)
);
//
// Cancel all the sent datagram and receive requests. The
// callbacks of transmit requests are executed to allow the
// caller to release the resource. The callback of receive
// request are NOT executed. This is because it is most
// likely that the current user of the UDP IO port is closing
// itself.
//
UdpIoCancelDgrams (UdpIo, EFI_ABORTED, NULL, NULL);
if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
if ((RxToken = UdpIo->RecvRequest) != NULL) {
Status = UdpIo->Protocol.Udp4->Cancel (UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Close then destroy the Udp4 child
//
Status = gBS->CloseProtocol (
UdpIo->UdpHandle,
&gEfiUdp4ProtocolGuid,
UdpIo->Image,
UdpIo->Controller
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = NetLibDestroyServiceChild (
UdpIo->Controller,
UdpIo->Image,
&gEfiUdp4ServiceBindingProtocolGuid,
UdpIo->UdpHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
} else {
if ((RxToken = UdpIo->RecvRequest) != NULL) {
Status = UdpIo->Protocol.Udp6->Cancel (UdpIo->Protocol.Udp6, &RxToken->Token.Udp6);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Close then destroy the Udp6 child
//
Status = gBS->CloseProtocol (
UdpIo->UdpHandle,
&gEfiUdp6ProtocolGuid,
UdpIo->Image,
UdpIo->Controller
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = NetLibDestroyServiceChild (
UdpIo->Controller,
UdpIo->Image,
&gEfiUdp6ServiceBindingProtocolGuid,
UdpIo->UdpHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
}
if (!IsListEmpty (&UdpIo->Link)) {
RemoveEntryList (&UdpIo->Link);
}
FreePool (UdpIo);
return EFI_SUCCESS;
}
/**
Clean up the UDP_IO without freeing it. The function is called when
user wants to re-use the UDP_IO later.
If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
It will release all the transmitted datagrams and receive request. It will
also configure NULL for the UDP instance.
@param[in] UdpIo The UDP_IO to clean up.
**/
VOID
EFIAPI
UdpIoCleanIo (
IN UDP_IO *UdpIo
)
{
UDP_RX_TOKEN *RxToken;
ASSERT (
(UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
(UdpIo->UdpVersion == UDP_IO_UDP6_VERSION)
);
//
// Cancel all the sent datagram and receive requests.
//
UdpIoCancelDgrams (UdpIo, EFI_ABORTED, NULL, NULL);
if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
if ((RxToken = UdpIo->RecvRequest) != NULL) {
UdpIo->Protocol.Udp4->Cancel (UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
}
UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, NULL);
} else {
if ((RxToken = UdpIo->RecvRequest) != NULL) {
UdpIo->Protocol.Udp6->Cancel (UdpIo->Protocol.Udp6, &RxToken->Token.Udp6);
}
UdpIo->Protocol.Udp6->Configure (UdpIo->Protocol.Udp6, NULL);
}
}
/**
Send a packet through the UDP_IO.
If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
The packet will be wrapped in UDP_TX_TOKEN. Function Callback will be called
when the packet is sent. The optional parameter EndPoint overrides the default
address pair if specified.
@param[in] UdpIo The UDP_IO to send the packet through.
@param[in] Packet The packet to send.
@param[in] EndPoint The local and remote access point. Override the
default address pair set during configuration.
@param[in] Gateway The gateway to use.
@param[in] CallBack The function being called when packet is
transmitted or failed.
@param[in] Context The opaque parameter passed to CallBack.
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the packet.
@retval EFI_SUCCESS The packet is successfully delivered to UDP for
transmission.
**/
EFI_STATUS
EFIAPI
UdpIoSendDatagram (
IN UDP_IO *UdpIo,
IN NET_BUF *Packet,
IN UDP_END_POINT *EndPoint OPTIONAL,
IN EFI_IP_ADDRESS *Gateway OPTIONAL,
IN UDP_IO_CALLBACK CallBack,
IN VOID *Context
)
{
UDP_TX_TOKEN *TxToken;
EFI_STATUS Status;
ASSERT (
(UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
(UdpIo->UdpVersion == UDP_IO_UDP6_VERSION)
);
TxToken = UdpIoCreateTxToken (UdpIo, Packet, EndPoint, Gateway, CallBack, Context);
if (TxToken == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Insert the tx token into SendDatagram list before transmitting it. Remove
// it from the list if the returned status is not EFI_SUCCESS.
//
InsertHeadList (&UdpIo->SentDatagram, &TxToken->Link);
if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
Status = UdpIo->Protocol.Udp4->Transmit (UdpIo->Protocol.Udp4, &TxToken->Token.Udp4);
} else {
Status = UdpIo->Protocol.Udp6->Transmit (UdpIo->Protocol.Udp6, &TxToken->Token.Udp6);
}
if (EFI_ERROR (Status)) {
RemoveEntryList (&TxToken->Link);
UdpIoFreeTxToken (TxToken);
return Status;
}
return EFI_SUCCESS;
}
/**
The select function to cancel a single sent datagram.
@param[in] Token The UDP_TX_TOKEN to test against
@param[in] Context The NET_BUF of the sent datagram
@retval TRUE The packet is to be cancelled.
@retval FALSE The packet is not to be cancelled.
**/
BOOLEAN
EFIAPI
UdpIoCancelSingleDgram (
IN UDP_TX_TOKEN *Token,
IN VOID *Context
)
{
NET_BUF *Packet;
Packet = (NET_BUF *)Context;
if (Token->Packet == Packet) {
return TRUE;
}
return FALSE;
}
/**
Cancel a single sent datagram.
@param[in] UdpIo The UDP_IO to cancel the packet from
@param[in] Packet The packet to cancel
**/
VOID
EFIAPI
UdpIoCancelSentDatagram (
IN UDP_IO *UdpIo,
IN NET_BUF *Packet
)
{
UdpIoCancelDgrams (UdpIo, EFI_ABORTED, UdpIoCancelSingleDgram, Packet);
}
/**
Issue a receive request to the UDP_IO.
If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
This function is called when upper-layer needs packet from UDP for processing.
Only one receive request is acceptable at a time so a common usage model is
to invoke this function inside its Callback function when the former packet
is processed.
@param[in] UdpIo The UDP_IO to receive the packet from.
@param[in] CallBack The call back function to execute when the packet
is received.
@param[in] Context The opaque context passed to Callback.
@param[in] HeadLen The length of the upper-layer's protocol header.
@retval EFI_ALREADY_STARTED There is already a pending receive request. Only
one receive request is supported at a time.
@retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources.
@retval EFI_SUCCESS The receive request is issued successfully.
@retval EFI_UNSUPPORTED The UDP version in UDP_IO is not supported.
**/
EFI_STATUS
EFIAPI
UdpIoRecvDatagram (
IN UDP_IO *UdpIo,
IN UDP_IO_CALLBACK CallBack,
IN VOID *Context,
IN UINT32 HeadLen
)
{
UDP_RX_TOKEN *RxToken;
EFI_STATUS Status;
ASSERT (
(UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
(UdpIo->UdpVersion == UDP_IO_UDP6_VERSION)
);
if (UdpIo->RecvRequest != NULL) {
return EFI_ALREADY_STARTED;
}
RxToken = UdpIoCreateRxToken (UdpIo, CallBack, Context, HeadLen);
if (RxToken == NULL) {
return EFI_OUT_OF_RESOURCES;
}
UdpIo->RecvRequest = RxToken;
if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
Status = UdpIo->Protocol.Udp4->Receive (UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
} else {
Status = UdpIo->Protocol.Udp6->Receive (UdpIo->Protocol.Udp6, &RxToken->Token.Udp6);
}
if (EFI_ERROR (Status)) {
UdpIo->RecvRequest = NULL;
UdpIoFreeRxToken (RxToken);
}
return Status;
}