1 /** @file
2   Help functions to access UDP service, it is used by both the DHCP and MTFTP.
3 
4 Copyright (c) 2005 - 2012, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at<BR>
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 **/
13 
14 #include <Uefi.h>
15 
16 #include <Protocol/Udp4.h>
17 #include <Protocol/Udp6.h>
18 
19 #include <Library/UdpIoLib.h>
20 #include <Library/BaseLib.h>
21 #include <Library/DebugLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
23 #include <Library/MemoryAllocationLib.h>
24 #include <Library/BaseMemoryLib.h>
25 #include <Library/DpcLib.h>
26 
27 
28 /**
29   Free a UDP_TX_TOKEN. The TX event is closed.
30 
31   @param[in]  TxToken                 The UDP_TX_TOKEN to release.
32 
33 **/
34 VOID
UdpIoFreeTxToken(IN UDP_TX_TOKEN * TxToken)35 UdpIoFreeTxToken (
36   IN UDP_TX_TOKEN           *TxToken
37   )
38 {
39 
40   if (TxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
41     gBS->CloseEvent (TxToken->Token.Udp4.Event);
42   } else if (TxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION) {
43     gBS->CloseEvent (TxToken->Token.Udp6.Event);
44   } else {
45     ASSERT (FALSE);
46   }
47 
48   FreePool (TxToken);
49 }
50 
51 /**
52   Free a UDP_RX_TOKEN. The RX event is closed.
53 
54   @param[in]  RxToken                 The UDP_RX_TOKEN to release.
55 
56 **/
57 VOID
UdpIoFreeRxToken(IN UDP_RX_TOKEN * RxToken)58 UdpIoFreeRxToken (
59   IN UDP_RX_TOKEN           *RxToken
60   )
61 {
62   if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
63     gBS->CloseEvent (RxToken->Token.Udp4.Event);
64   } else if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION) {
65     gBS->CloseEvent (RxToken->Token.Udp6.Event);
66   } else {
67     ASSERT (FALSE);
68   }
69 
70   FreePool (RxToken);
71 }
72 
73 /**
74   The callback function when the packet is sent by UDP.
75 
76   It will remove the packet from the local list then call
77   the packet owner's callback function set by UdpIoSendDatagram.
78 
79   @param[in]  Context               The UDP TX Token.
80 
81 **/
82 VOID
83 EFIAPI
UdpIoOnDgramSentDpc(IN VOID * Context)84 UdpIoOnDgramSentDpc (
85   IN VOID                   *Context
86   )
87 {
88   UDP_TX_TOKEN              *TxToken;
89 
90   TxToken = (UDP_TX_TOKEN *) Context;
91   ASSERT (TxToken->Signature == UDP_IO_TX_SIGNATURE);
92   ASSERT ((TxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
93           (TxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
94 
95   RemoveEntryList (&TxToken->Link);
96 
97   if (TxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
98     TxToken->CallBack (TxToken->Packet, NULL, TxToken->Token.Udp4.Status, TxToken->Context);
99   } else {
100     TxToken->CallBack (TxToken->Packet, NULL, TxToken->Token.Udp6.Status, TxToken->Context);
101   }
102 
103   UdpIoFreeTxToken (TxToken);
104 }
105 
106 /**
107   Request UdpIoOnDgramSentDpc as a DPC at TPL_CALLBACK.
108 
109   @param[in]  Event                 The event signaled.
110   @param[in]  Context               The UDP TX Token.
111 
112 **/
113 VOID
114 EFIAPI
UdpIoOnDgramSent(IN EFI_EVENT Event,IN VOID * Context)115 UdpIoOnDgramSent (
116   IN EFI_EVENT              Event,
117   IN VOID                   *Context
118   )
119 {
120   //
121   // Request UdpIoOnDgramSentDpc as a DPC at TPL_CALLBACK
122   //
123   QueueDpc (TPL_CALLBACK, UdpIoOnDgramSentDpc, Context);
124 }
125 
126 /**
127   Recycle the received UDP data.
128 
129   @param[in]  Context               The UDP_RX_TOKEN.
130 
131 **/
132 VOID
133 EFIAPI
UdpIoRecycleDgram(IN VOID * Context)134 UdpIoRecycleDgram (
135   IN VOID                   *Context
136   )
137 {
138   UDP_RX_TOKEN              *RxToken;
139 
140   RxToken = (UDP_RX_TOKEN *) Context;
141 
142   if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
143     gBS->SignalEvent (RxToken->Token.Udp4.Packet.RxData->RecycleSignal);
144   } else if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION) {
145     gBS->SignalEvent (RxToken->Token.Udp6.Packet.RxData->RecycleSignal);
146   } else {
147     ASSERT (FALSE);
148   }
149 
150   UdpIoFreeRxToken (RxToken);
151 }
152 
153 /**
154   The event handle for UDP receive request.
155 
156   It will build a NET_BUF from the recieved UDP data, then deliver it
157   to the receiver.
158 
159   @param[in]  Context               The UDP RX token.
160 
161 **/
162 VOID
163 EFIAPI
UdpIoOnDgramRcvdDpc(IN VOID * Context)164 UdpIoOnDgramRcvdDpc (
165   IN VOID                   *Context
166   )
167 {
168   EFI_STATUS                Status;
169   VOID                      *Token;
170   VOID                      *RxData;
171   VOID                      *Session;
172   UDP_RX_TOKEN              *RxToken;
173   UDP_END_POINT             EndPoint;
174   NET_BUF                   *Netbuf;
175 
176   RxToken = (UDP_RX_TOKEN *) Context;
177 
178   ZeroMem (&EndPoint, sizeof(UDP_END_POINT));
179 
180   ASSERT ((RxToken->Signature == UDP_IO_RX_SIGNATURE) &&
181           (RxToken == RxToken->UdpIo->RecvRequest));
182 
183   ASSERT ((RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
184           (RxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
185 
186   //
187   // Clear the receive request first in case that the caller
188   // wants to restart the receive in the callback.
189   //
190   RxToken->UdpIo->RecvRequest = NULL;
191 
192   if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
193     Token  = &RxToken->Token.Udp4;
194     RxData = ((EFI_UDP4_COMPLETION_TOKEN *) Token)->Packet.RxData;
195     Status = ((EFI_UDP4_COMPLETION_TOKEN *) Token)->Status;
196   } else {
197     Token  = &RxToken->Token.Udp6;
198     RxData = ((EFI_UDP6_COMPLETION_TOKEN *) Token)->Packet.RxData;
199     Status = ((EFI_UDP6_COMPLETION_TOKEN *) Token)->Status;
200   }
201 
202   if (EFI_ERROR (Status) || RxData == NULL) {
203     if (Status != EFI_ABORTED) {
204       //
205       // Invoke the CallBack only if the reception is not actively aborted.
206       //
207       RxToken->CallBack (NULL, NULL, Status, RxToken->Context);
208     }
209 
210     UdpIoFreeRxToken (RxToken);
211     return;
212   }
213 
214   //
215   // Build a NET_BUF from the UDP receive data, then deliver it up.
216   //
217   if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
218 
219     Netbuf = NetbufFromExt (
220                (NET_FRAGMENT *)((EFI_UDP4_RECEIVE_DATA *) RxData)->FragmentTable,
221                ((EFI_UDP4_RECEIVE_DATA *) RxData)->FragmentCount,
222                0,
223                (UINT32) RxToken->HeadLen,
224                UdpIoRecycleDgram,
225                RxToken
226                );
227 
228     if (Netbuf == NULL) {
229       gBS->SignalEvent (((EFI_UDP4_RECEIVE_DATA *) RxData)->RecycleSignal);
230       RxToken->CallBack (NULL, NULL, EFI_OUT_OF_RESOURCES, RxToken->Context);
231 
232       UdpIoFreeRxToken (RxToken);
233       return;
234     }
235 
236     Session             = &((EFI_UDP4_RECEIVE_DATA *) RxData)->UdpSession;
237     EndPoint.LocalPort  = ((EFI_UDP4_SESSION_DATA *) Session)->DestinationPort;
238     EndPoint.RemotePort = ((EFI_UDP4_SESSION_DATA *) Session)->SourcePort;
239 
240     CopyMem (
241       &EndPoint.LocalAddr,
242       &((EFI_UDP4_SESSION_DATA *) Session)->DestinationAddress,
243       sizeof (EFI_IPv4_ADDRESS)
244       );
245 
246     CopyMem (
247       &EndPoint.RemoteAddr,
248       &((EFI_UDP4_SESSION_DATA *) Session)->SourceAddress,
249       sizeof (EFI_IPv4_ADDRESS)
250       );
251 
252     EndPoint.LocalAddr.Addr[0]  = NTOHL (EndPoint.LocalAddr.Addr[0]);
253     EndPoint.RemoteAddr.Addr[0] = NTOHL (EndPoint.RemoteAddr.Addr[0]);
254   } else {
255 
256     Netbuf = NetbufFromExt (
257                (NET_FRAGMENT *)((EFI_UDP6_RECEIVE_DATA *) RxData)->FragmentTable,
258                ((EFI_UDP6_RECEIVE_DATA *) RxData)->FragmentCount,
259                0,
260                (UINT32) RxToken->HeadLen,
261                UdpIoRecycleDgram,
262                RxToken
263                );
264 
265     if (Netbuf == NULL) {
266       gBS->SignalEvent (((EFI_UDP6_RECEIVE_DATA *) RxData)->RecycleSignal);
267       RxToken->CallBack (NULL, NULL, EFI_OUT_OF_RESOURCES, RxToken->Context);
268 
269       UdpIoFreeRxToken (RxToken);
270       return;
271     }
272 
273     Session             = &((EFI_UDP6_RECEIVE_DATA *) RxData)->UdpSession;
274     EndPoint.LocalPort  = ((EFI_UDP6_SESSION_DATA *) Session)->DestinationPort;
275     EndPoint.RemotePort = ((EFI_UDP6_SESSION_DATA *) Session)->SourcePort;
276 
277     CopyMem (
278       &EndPoint.LocalAddr,
279       &((EFI_UDP6_SESSION_DATA *) Session)->DestinationAddress,
280       sizeof (EFI_IPv6_ADDRESS)
281       );
282 
283     CopyMem (
284       &EndPoint.RemoteAddr,
285       &((EFI_UDP6_SESSION_DATA *) Session)->SourceAddress,
286       sizeof (EFI_IPv6_ADDRESS)
287       );
288 
289     Ip6Swap128 (&EndPoint.LocalAddr.v6);
290     Ip6Swap128 (&EndPoint.RemoteAddr.v6);
291   }
292 
293   RxToken->CallBack (Netbuf, &EndPoint, EFI_SUCCESS, RxToken->Context);
294 }
295 
296 /**
297   Request UdpIoOnDgramRcvdDpc() as a DPC at TPL_CALLBACK.
298 
299   @param[in]  Event                 The UDP receive request event.
300   @param[in]  Context               The UDP RX token.
301 
302 **/
303 VOID
304 EFIAPI
UdpIoOnDgramRcvd(IN EFI_EVENT Event,IN VOID * Context)305 UdpIoOnDgramRcvd (
306   IN EFI_EVENT              Event,
307   IN VOID                   *Context
308   )
309 {
310   //
311   // Request UdpIoOnDgramRcvdDpc as a DPC at TPL_CALLBACK
312   //
313   QueueDpc (TPL_CALLBACK, UdpIoOnDgramRcvdDpc, Context);
314 }
315 
316 /**
317   Create a UDP_RX_TOKEN to wrap the request.
318 
319   @param[in]  UdpIo                 The UdpIo to receive packets from.
320   @param[in]  CallBack              The function to call when receive finished.
321   @param[in]  Context               The opaque parameter to the CallBack.
322   @param[in]  HeadLen               The head length to reserver for the packet.
323 
324   @return The Wrapped request or NULL if failed to allocate resources or some errors happened.
325 
326 **/
327 UDP_RX_TOKEN *
UdpIoCreateRxToken(IN UDP_IO * UdpIo,IN UDP_IO_CALLBACK CallBack,IN VOID * Context,IN UINT32 HeadLen)328 UdpIoCreateRxToken (
329   IN UDP_IO                 *UdpIo,
330   IN UDP_IO_CALLBACK        CallBack,
331   IN VOID                   *Context,
332   IN UINT32                 HeadLen
333   )
334 {
335   UDP_RX_TOKEN              *Token;
336   EFI_STATUS                Status;
337 
338   ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
339           (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
340 
341   Token = AllocatePool (sizeof (UDP_RX_TOKEN));
342 
343   if (Token == NULL) {
344     return NULL;
345   }
346 
347   Token->Signature              = UDP_IO_RX_SIGNATURE;
348   Token->UdpIo                  = UdpIo;
349   Token->CallBack               = CallBack;
350   Token->Context                = Context;
351   Token->HeadLen                = HeadLen;
352 
353   if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
354 
355     Token->Token.Udp4.Status        = EFI_NOT_READY;
356     Token->Token.Udp4.Packet.RxData = NULL;
357 
358     Status = gBS->CreateEvent (
359                     EVT_NOTIFY_SIGNAL,
360                     TPL_NOTIFY,
361                     UdpIoOnDgramRcvd,
362                     Token,
363                     &Token->Token.Udp4.Event
364                     );
365     } else {
366 
367     Token->Token.Udp6.Status        = EFI_NOT_READY;
368     Token->Token.Udp6.Packet.RxData = NULL;
369 
370     Status = gBS->CreateEvent (
371                     EVT_NOTIFY_SIGNAL,
372                     TPL_NOTIFY,
373                     UdpIoOnDgramRcvd,
374                     Token,
375                     &Token->Token.Udp6.Event
376                     );
377   }
378 
379 
380   if (EFI_ERROR (Status)) {
381     FreePool (Token);
382     return NULL;
383   }
384 
385   return Token;
386 }
387 
388 /**
389   Wrap a transmit request into a new created UDP_TX_TOKEN.
390 
391   @param[in]  UdpIo                 The UdpIo to send packet to.
392   @param[in]  Packet                The user's packet.
393   @param[in]  EndPoint              The local and remote access point.
394   @param[in]  Gateway               The overrided next hop.
395   @param[in]  CallBack              The function to call when transmission completed.
396   @param[in]  Context               The opaque parameter to the call back.
397 
398   @return The wrapped transmission request or NULL if failed to allocate resources
399           or for some errors.
400 
401 **/
402 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)403 UdpIoCreateTxToken (
404   IN UDP_IO                 *UdpIo,
405   IN NET_BUF                *Packet,
406   IN UDP_END_POINT          *EndPoint OPTIONAL,
407   IN EFI_IP_ADDRESS         *Gateway  OPTIONAL,
408   IN UDP_IO_CALLBACK        CallBack,
409   IN VOID                   *Context
410   )
411 {
412   UDP_TX_TOKEN              *TxToken;
413   VOID                      *Token;
414   VOID                      *Data;
415   EFI_STATUS                Status;
416   UINT32                    Count;
417   UINTN                     Size;
418   IP4_ADDR                  Ip;
419 
420   ASSERT (Packet != NULL);
421   ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
422           (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
423 
424   if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
425     Size = sizeof (UDP_TX_TOKEN) + sizeof (EFI_UDP4_FRAGMENT_DATA) * (Packet->BlockOpNum - 1);
426   } else {
427     Size = sizeof (UDP_TX_TOKEN) + sizeof (EFI_UDP6_FRAGMENT_DATA) * (Packet->BlockOpNum - 1);
428   }
429 
430   TxToken = AllocatePool (Size);
431 
432   if (TxToken == NULL) {
433     return NULL;
434   }
435 
436   TxToken->Signature = UDP_IO_TX_SIGNATURE;
437   InitializeListHead (&TxToken->Link);
438 
439   TxToken->UdpIo     = UdpIo;
440   TxToken->CallBack  = CallBack;
441   TxToken->Packet    = Packet;
442   TxToken->Context   = Context;
443 
444   Token              = &(TxToken->Token);
445   Count              = Packet->BlockOpNum;
446 
447   if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
448 
449     ((EFI_UDP4_COMPLETION_TOKEN *) Token)->Status = EFI_NOT_READY;
450 
451     Status = gBS->CreateEvent (
452                     EVT_NOTIFY_SIGNAL,
453                     TPL_NOTIFY,
454                     UdpIoOnDgramSent,
455                     TxToken,
456                     &((EFI_UDP4_COMPLETION_TOKEN *) Token)->Event
457                     );
458 
459     if (EFI_ERROR (Status)) {
460       FreePool (TxToken);
461       return NULL;
462     }
463 
464     Data = &(TxToken->Data.Udp4);
465     ((EFI_UDP4_COMPLETION_TOKEN *) Token)->Packet.TxData = Data;
466 
467     ((EFI_UDP4_TRANSMIT_DATA *) Data)->UdpSessionData    = NULL;
468     ((EFI_UDP4_TRANSMIT_DATA *) Data)->GatewayAddress    = NULL;
469     ((EFI_UDP4_TRANSMIT_DATA *) Data)->DataLength        = Packet->TotalSize;
470 
471     NetbufBuildExt (
472       Packet,
473       (NET_FRAGMENT *)((EFI_UDP4_TRANSMIT_DATA *) Data)->FragmentTable,
474       &Count
475       );
476 
477     ((EFI_UDP4_TRANSMIT_DATA *) Data)->FragmentCount     = Count;
478 
479     if (EndPoint != NULL) {
480       Ip = HTONL (EndPoint->LocalAddr.Addr[0]);
481       CopyMem (
482         &TxToken->Session.Udp4.SourceAddress,
483         &Ip,
484         sizeof (EFI_IPv4_ADDRESS)
485         );
486 
487       Ip = HTONL (EndPoint->RemoteAddr.Addr[0]);
488       CopyMem (
489         &TxToken->Session.Udp4.DestinationAddress,
490         &Ip,
491         sizeof (EFI_IPv4_ADDRESS)
492         );
493 
494       TxToken->Session.Udp4.SourcePort                   = EndPoint->LocalPort;
495       TxToken->Session.Udp4.DestinationPort              = EndPoint->RemotePort;
496       ((EFI_UDP4_TRANSMIT_DATA *) Data)->UdpSessionData  = &(TxToken->Session.Udp4);
497     }
498 
499     if (Gateway != NULL && (Gateway->Addr[0] != 0)) {
500       Ip = HTONL (Gateway->Addr[0]);
501       CopyMem (&TxToken->Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));
502       ((EFI_UDP4_TRANSMIT_DATA *) Data)->GatewayAddress = &TxToken->Gateway;
503     }
504 
505   } else {
506 
507     ((EFI_UDP6_COMPLETION_TOKEN *) Token)->Status = EFI_NOT_READY;
508 
509     Status = gBS->CreateEvent (
510                     EVT_NOTIFY_SIGNAL,
511                     TPL_NOTIFY,
512                     UdpIoOnDgramSent,
513                     TxToken,
514                     &((EFI_UDP6_COMPLETION_TOKEN *) Token)->Event
515                     );
516 
517     if (EFI_ERROR (Status)) {
518       FreePool (TxToken);
519       return NULL;
520     }
521 
522     Data = &(TxToken->Data.Udp6);
523     ((EFI_UDP6_COMPLETION_TOKEN *) Token)->Packet.TxData  = Data;
524     ((EFI_UDP6_TRANSMIT_DATA *) Data)->UdpSessionData     = NULL;
525     ((EFI_UDP6_TRANSMIT_DATA *) Data)->DataLength         = Packet->TotalSize;
526 
527     NetbufBuildExt (
528       Packet,
529       (NET_FRAGMENT *)((EFI_UDP6_TRANSMIT_DATA *) Data)->FragmentTable,
530       &Count
531       );
532 
533     ((EFI_UDP6_TRANSMIT_DATA *) Data)->FragmentCount      = Count;
534 
535     if (EndPoint != NULL) {
536       CopyMem (
537         &TxToken->Session.Udp6.SourceAddress,
538         &EndPoint->LocalAddr.v6,
539         sizeof(EFI_IPv6_ADDRESS)
540         );
541 
542       CopyMem (
543         &TxToken->Session.Udp6.DestinationAddress,
544         &EndPoint->RemoteAddr.v6,
545         sizeof(EFI_IPv6_ADDRESS)
546         );
547 
548       TxToken->Session.Udp6.SourcePort                   = EndPoint->LocalPort;
549       TxToken->Session.Udp6.DestinationPort              = EndPoint->RemotePort;
550       ((EFI_UDP6_TRANSMIT_DATA *) Data)->UdpSessionData  = &(TxToken->Session.Udp6);
551     }
552   }
553 
554   return TxToken;
555 }
556 
557 /**
558   Creates a UDP_IO to access the UDP service. It creates and configures
559   a UDP child.
560 
561   It locates the UDP service binding prototype on the Controller parameter
562   uses the UDP service binding prototype to create a UDP child (also known as
563   a UDP instance) configures the UDP child by calling Configure function prototype.
564   Any failures in creating or configuring the UDP child return NULL for failure.
565 
566   @param[in]  Controller            The controller that has the UDP service binding.
567                                     protocol installed.
568   @param[in]  ImageHandle           The image handle for the driver.
569   @param[in]  Configure             The function to configure the created UDP child.
570   @param[in]  UdpVersion            The UDP protocol version, UDP4 or UDP6.
571   @param[in]  Context               The opaque parameter for the Configure funtion.
572 
573   @return Newly-created UDP_IO or NULL if failed.
574 
575 **/
576 UDP_IO *
577 EFIAPI
UdpIoCreateIo(IN EFI_HANDLE Controller,IN EFI_HANDLE ImageHandle,IN UDP_IO_CONFIG Configure,IN UINT8 UdpVersion,IN VOID * Context)578 UdpIoCreateIo (
579   IN  EFI_HANDLE            Controller,
580   IN  EFI_HANDLE            ImageHandle,
581   IN  UDP_IO_CONFIG         Configure,
582   IN  UINT8                 UdpVersion,
583   IN  VOID                  *Context
584   )
585 {
586   UDP_IO                    *UdpIo;
587   EFI_STATUS                Status;
588 
589   ASSERT (Configure != NULL);
590   ASSERT ((UdpVersion == UDP_IO_UDP4_VERSION) || (UdpVersion == UDP_IO_UDP6_VERSION));
591 
592   UdpIo = AllocatePool (sizeof (UDP_IO));
593 
594   if (UdpIo == NULL) {
595     return NULL;
596   }
597 
598   UdpIo->UdpVersion   = UdpVersion;
599   UdpIo->Signature    = UDP_IO_SIGNATURE;
600   InitializeListHead (&UdpIo->Link);
601   UdpIo->RefCnt       = 1;
602 
603   UdpIo->Controller   = Controller;
604   UdpIo->Image        = ImageHandle;
605 
606   InitializeListHead (&UdpIo->SentDatagram);
607   UdpIo->RecvRequest  = NULL;
608   UdpIo->UdpHandle    = NULL;
609 
610   if (UdpVersion == UDP_IO_UDP4_VERSION) {
611     //
612     // Create a UDP child then open and configure it
613     //
614     Status = NetLibCreateServiceChild (
615                Controller,
616                ImageHandle,
617                &gEfiUdp4ServiceBindingProtocolGuid,
618                &UdpIo->UdpHandle
619                );
620 
621     if (EFI_ERROR (Status)) {
622       goto FREE_MEM;
623     }
624 
625     Status = gBS->OpenProtocol (
626                     UdpIo->UdpHandle,
627                     &gEfiUdp4ProtocolGuid,
628                     (VOID **) &UdpIo->Protocol.Udp4,
629                     ImageHandle,
630                     Controller,
631                     EFI_OPEN_PROTOCOL_BY_DRIVER
632                     );
633 
634     if (EFI_ERROR (Status)) {
635       goto FREE_CHILD;
636     }
637 
638     if (EFI_ERROR (Configure (UdpIo, Context))) {
639       goto CLOSE_PROTOCOL;
640     }
641 
642     Status = UdpIo->Protocol.Udp4->GetModeData (
643                                      UdpIo->Protocol.Udp4,
644                                      NULL,
645                                      NULL,
646                                      NULL,
647                                      &UdpIo->SnpMode
648                                      );
649 
650     if (EFI_ERROR (Status)) {
651       goto CLOSE_PROTOCOL;
652     }
653 
654   } else {
655 
656     Status = NetLibCreateServiceChild (
657                Controller,
658                ImageHandle,
659                &gEfiUdp6ServiceBindingProtocolGuid,
660                &UdpIo->UdpHandle
661                );
662 
663     if (EFI_ERROR (Status)) {
664       goto FREE_MEM;
665     }
666 
667     Status = gBS->OpenProtocol (
668                     UdpIo->UdpHandle,
669                     &gEfiUdp6ProtocolGuid,
670                     (VOID **) &UdpIo->Protocol.Udp6,
671                     ImageHandle,
672                     Controller,
673                     EFI_OPEN_PROTOCOL_BY_DRIVER
674                     );
675 
676     if (EFI_ERROR (Status)) {
677       goto FREE_CHILD;
678     }
679 
680     if (EFI_ERROR (Configure (UdpIo, Context))) {
681       goto CLOSE_PROTOCOL;
682     }
683 
684     Status = UdpIo->Protocol.Udp6->GetModeData (
685                                      UdpIo->Protocol.Udp6,
686                                      NULL,
687                                      NULL,
688                                      NULL,
689                                      &UdpIo->SnpMode
690                                      );
691 
692     if (EFI_ERROR (Status)) {
693       goto CLOSE_PROTOCOL;
694     }
695   }
696 
697   return UdpIo;
698 
699 CLOSE_PROTOCOL:
700   if (UdpVersion == UDP_IO_UDP4_VERSION) {
701     gBS->CloseProtocol (UdpIo->UdpHandle, &gEfiUdp4ProtocolGuid, ImageHandle, Controller);
702   } else {
703     gBS->CloseProtocol (UdpIo->UdpHandle, &gEfiUdp6ProtocolGuid, ImageHandle, Controller);
704   }
705 
706 FREE_CHILD:
707   if (UdpVersion == UDP_IO_UDP4_VERSION) {
708     NetLibDestroyServiceChild (
709       Controller,
710       ImageHandle,
711       &gEfiUdp4ServiceBindingProtocolGuid,
712       UdpIo->UdpHandle
713       );
714   } else {
715     NetLibDestroyServiceChild (
716       Controller,
717       ImageHandle,
718       &gEfiUdp6ServiceBindingProtocolGuid,
719       UdpIo->UdpHandle
720       );
721   }
722 
723 FREE_MEM:
724   FreePool (UdpIo);
725   return NULL;
726 }
727 
728 /**
729   Cancel all the sent datagram that pass the selection criteria of ToCancel.
730   If ToCancel is NULL, all the datagrams are cancelled.
731 
732   @param[in]  UdpIo                 The UDP_IO to cancel packet.
733   @param[in]  IoStatus              The IoStatus to return to the packet owners.
734   @param[in]  ToCancel              The select funtion to test whether to cancel this
735                                     packet or not.
736   @param[in]  Context               The opaque parameter to the ToCancel.
737 
738 **/
739 VOID
740 EFIAPI
UdpIoCancelDgrams(IN UDP_IO * UdpIo,IN EFI_STATUS IoStatus,IN UDP_IO_TO_CANCEL ToCancel,OPTIONAL IN VOID * Context)741 UdpIoCancelDgrams (
742   IN UDP_IO                 *UdpIo,
743   IN EFI_STATUS             IoStatus,
744   IN UDP_IO_TO_CANCEL       ToCancel,        OPTIONAL
745   IN VOID                   *Context
746   )
747 {
748   LIST_ENTRY                *Entry;
749   LIST_ENTRY                *Next;
750   UDP_TX_TOKEN              *TxToken;
751 
752   ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
753           (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
754 
755   NET_LIST_FOR_EACH_SAFE (Entry, Next, &UdpIo->SentDatagram) {
756     TxToken = NET_LIST_USER_STRUCT (Entry, UDP_TX_TOKEN, Link);
757 
758     if ((ToCancel == NULL) || (ToCancel (TxToken, Context))) {
759 
760       if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
761         UdpIo->Protocol.Udp4->Cancel (UdpIo->Protocol.Udp4, &TxToken->Token.Udp4);
762       } else {
763         UdpIo->Protocol.Udp6->Cancel (UdpIo->Protocol.Udp6, &TxToken->Token.Udp6);
764       }
765     }
766   }
767 }
768 
769 /**
770   Free the UDP_IO and all its related resources.
771 
772   The function will cancel all sent datagram and receive request.
773 
774   @param[in]  UdpIo             The UDP_IO to free.
775 
776   @retval EFI_SUCCESS           The UDP_IO is freed.
777 
778 **/
779 EFI_STATUS
780 EFIAPI
UdpIoFreeIo(IN UDP_IO * UdpIo)781 UdpIoFreeIo (
782   IN  UDP_IO           *UdpIo
783   )
784 {
785   UDP_RX_TOKEN         *RxToken;
786 
787   ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
788           (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
789 
790   //
791   // Cancel all the sent datagram and receive requests. The
792   // callbacks of transmit requests are executed to allow the
793   // caller to release the resource. The callback of receive
794   // request are NOT executed. This is because it is most
795   // likely that the current user of the UDP IO port is closing
796   // itself.
797   //
798   UdpIoCancelDgrams (UdpIo, EFI_ABORTED, NULL, NULL);
799 
800   if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
801 
802     if ((RxToken = UdpIo->RecvRequest) != NULL) {
803       UdpIo->Protocol.Udp4->Cancel (UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
804     }
805 
806     //
807     // Close then destroy the Udp4 child
808     //
809     gBS->CloseProtocol (
810            UdpIo->UdpHandle,
811            &gEfiUdp4ProtocolGuid,
812            UdpIo->Image,
813            UdpIo->Controller
814            );
815 
816     NetLibDestroyServiceChild (
817       UdpIo->Controller,
818       UdpIo->Image,
819       &gEfiUdp4ServiceBindingProtocolGuid,
820       UdpIo->UdpHandle
821       );
822 
823   } else {
824 
825     if ((RxToken = UdpIo->RecvRequest) != NULL) {
826       UdpIo->Protocol.Udp6->Cancel (UdpIo->Protocol.Udp6, &RxToken->Token.Udp6);
827     }
828 
829     //
830     // Close then destroy the Udp6 child
831     //
832     gBS->CloseProtocol (
833            UdpIo->UdpHandle,
834            &gEfiUdp6ProtocolGuid,
835            UdpIo->Image,
836            UdpIo->Controller
837            );
838 
839     NetLibDestroyServiceChild (
840       UdpIo->Controller,
841       UdpIo->Image,
842       &gEfiUdp6ServiceBindingProtocolGuid,
843       UdpIo->UdpHandle
844       );
845     }
846 
847   if (!IsListEmpty(&UdpIo->Link)) {
848     RemoveEntryList (&UdpIo->Link);
849   }
850 
851   FreePool (UdpIo);
852   return EFI_SUCCESS;
853 }
854 
855 
856 /**
857   Clean up the UDP_IO without freeing it. The function is called when
858   user wants to re-use the UDP_IO later.
859 
860   It will release all the transmitted datagrams and receive request. It will
861   also configure NULL for the UDP instance.
862 
863   @param[in]  UdpIo                 The UDP_IO to clean up.
864 
865 **/
866 VOID
867 EFIAPI
UdpIoCleanIo(IN UDP_IO * UdpIo)868 UdpIoCleanIo (
869   IN  UDP_IO                *UdpIo
870   )
871 {
872   UDP_RX_TOKEN              *RxToken;
873 
874   ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
875           (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
876 
877   //
878   // Cancel all the sent datagram and receive requests.
879   //
880   UdpIoCancelDgrams (UdpIo, EFI_ABORTED, NULL, NULL);
881 
882   if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
883     if ((RxToken = UdpIo->RecvRequest) != NULL) {
884       UdpIo->Protocol.Udp4->Cancel (UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
885     }
886 
887     UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, NULL);
888 
889   } else {
890     if ((RxToken = UdpIo->RecvRequest) != NULL) {
891       UdpIo->Protocol.Udp6->Cancel (UdpIo->Protocol.Udp6, &RxToken->Token.Udp6);
892     }
893 
894     UdpIo->Protocol.Udp6->Configure (UdpIo->Protocol.Udp6, NULL);
895   }
896 }
897 
898 /**
899   Send a packet through the UDP_IO.
900 
901   The packet will be wrapped in UDP_TX_TOKEN. Function Callback will be called
902   when the packet is sent. The optional parameter EndPoint overrides the default
903   address pair if specified.
904 
905   @param[in]  UdpIo                 The UDP_IO to send the packet through.
906   @param[in]  Packet                The packet to send.
907   @param[in]  EndPoint              The local and remote access point. Override the
908                                     default address pair set during configuration.
909   @param[in]  Gateway               The gateway to use.
910   @param[in]  CallBack              The function being called when packet is
911                                     transmitted or failed.
912   @param[in]  Context               The opaque parameter passed to CallBack.
913 
914   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource for the packet.
915   @retval EFI_SUCCESS           The packet is successfully delivered to UDP  for
916                                 transmission.
917 
918 **/
919 EFI_STATUS
920 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)921 UdpIoSendDatagram (
922   IN  UDP_IO                *UdpIo,
923   IN  NET_BUF               *Packet,
924   IN  UDP_END_POINT         *EndPoint OPTIONAL,
925   IN  EFI_IP_ADDRESS        *Gateway  OPTIONAL,
926   IN  UDP_IO_CALLBACK       CallBack,
927   IN  VOID                  *Context
928   )
929 {
930   UDP_TX_TOKEN              *TxToken;
931   EFI_STATUS                Status;
932 
933   ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
934           (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
935 
936   TxToken = UdpIoCreateTxToken (UdpIo, Packet, EndPoint, Gateway, CallBack, Context);
937 
938   if (TxToken == NULL) {
939     return EFI_OUT_OF_RESOURCES;
940   }
941 
942   //
943   // Insert the tx token into SendDatagram list before transmitting it. Remove
944   // it from the list if the returned status is not EFI_SUCCESS.
945   //
946   InsertHeadList (&UdpIo->SentDatagram, &TxToken->Link);
947 
948   if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
949     Status = UdpIo->Protocol.Udp4->Transmit (UdpIo->Protocol.Udp4, &TxToken->Token.Udp4);
950   } else {
951     Status = UdpIo->Protocol.Udp6->Transmit (UdpIo->Protocol.Udp6, &TxToken->Token.Udp6);
952   }
953 
954   if (EFI_ERROR (Status)) {
955     RemoveEntryList (&TxToken->Link);
956     UdpIoFreeTxToken (TxToken);
957     return Status;
958   }
959 
960   return EFI_SUCCESS;
961 }
962 
963 
964 /**
965   The select function to cancel a single sent datagram.
966 
967   @param[in]  Token                 The UDP_TX_TOKEN to test against
968   @param[in]  Context               The NET_BUF of the sent datagram
969 
970   @retval TRUE              The packet is to be cancelled.
971   @retval FALSE             The packet is not to be cancelled.
972 **/
973 BOOLEAN
974 EFIAPI
UdpIoCancelSingleDgram(IN UDP_TX_TOKEN * Token,IN VOID * Context)975 UdpIoCancelSingleDgram (
976   IN UDP_TX_TOKEN           *Token,
977   IN VOID                   *Context
978   )
979 {
980   NET_BUF                   *Packet;
981 
982   Packet = (NET_BUF *) Context;
983 
984   if (Token->Packet == Packet) {
985     return TRUE;
986   }
987 
988   return FALSE;
989 }
990 
991 /**
992   Cancel a single sent datagram.
993 
994   @param[in]  UdpIo                 The UDP_IO to cancel the packet from
995   @param[in]  Packet                The packet to cancel
996 
997 **/
998 VOID
999 EFIAPI
UdpIoCancelSentDatagram(IN UDP_IO * UdpIo,IN NET_BUF * Packet)1000 UdpIoCancelSentDatagram (
1001   IN  UDP_IO                *UdpIo,
1002   IN  NET_BUF               *Packet
1003   )
1004 {
1005   UdpIoCancelDgrams (UdpIo, EFI_ABORTED, UdpIoCancelSingleDgram, Packet);
1006 }
1007 
1008 /**
1009   Issue a receive request to the UDP_IO.
1010 
1011   This function is called when upper-layer needs packet from UDP for processing.
1012   Only one receive request is acceptable at a time so a common usage model is
1013   to invoke this function inside its Callback function when the former packet
1014   is processed.
1015 
1016   @param[in]  UdpIo                 The UDP_IO to receive the packet from.
1017   @param[in]  CallBack              The call back function to execute when the packet
1018                                     is received.
1019   @param[in]  Context               The opaque context passed to Callback.
1020   @param[in]  HeadLen               The length of the upper-layer's protocol header.
1021 
1022   @retval EFI_ALREADY_STARTED   There is already a pending receive request. Only
1023                                 one receive request is supported at a time.
1024   @retval EFI_OUT_OF_RESOURCES  Failed to allocate needed resources.
1025   @retval EFI_SUCCESS           The receive request is issued successfully.
1026   @retval EFI_UNSUPPORTED       The UDP version in UDP_IO is not supported.
1027 
1028 **/
1029 EFI_STATUS
1030 EFIAPI
UdpIoRecvDatagram(IN UDP_IO * UdpIo,IN UDP_IO_CALLBACK CallBack,IN VOID * Context,IN UINT32 HeadLen)1031 UdpIoRecvDatagram (
1032   IN  UDP_IO                *UdpIo,
1033   IN  UDP_IO_CALLBACK       CallBack,
1034   IN  VOID                  *Context,
1035   IN  UINT32                HeadLen
1036   )
1037 {
1038   UDP_RX_TOKEN              *RxToken;
1039   EFI_STATUS                Status;
1040 
1041   ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
1042           (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
1043 
1044   if (UdpIo->RecvRequest != NULL) {
1045     return EFI_ALREADY_STARTED;
1046   }
1047 
1048   RxToken = UdpIoCreateRxToken (UdpIo, CallBack, Context, HeadLen);
1049 
1050   if (RxToken == NULL) {
1051     return EFI_OUT_OF_RESOURCES;
1052   }
1053 
1054   UdpIo->RecvRequest = RxToken;
1055   if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
1056     Status = UdpIo->Protocol.Udp4->Receive (UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
1057   } else {
1058     Status = UdpIo->Protocol.Udp6->Receive (UdpIo->Protocol.Udp6, &RxToken->Token.Udp6);
1059   }
1060 
1061   if (EFI_ERROR (Status)) {
1062     UdpIo->RecvRequest = NULL;
1063     UdpIoFreeRxToken (RxToken);
1064   }
1065 
1066   return Status;
1067 }
1068