/** @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; | |
} |