]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.c
Use Mde library and definition instead of some native definitions in NetLib, to simpl...
[mirror_edk2.git] / MdeModulePkg / Library / DxeUdpIoLib / DxeUdpIoLib.c
1 /** @file
2
3 Copyright (c) 2006 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12
13 Module Name:
14
15 Udp4Io.c
16
17 Abstract:
18
19 Help functions to access UDP service, it is used by both the DHCP and MTFTP.
20
21
22 **/
23
24 #include <PiDxe.h>
25
26 #include <Protocol/Udp4.h>
27
28 #include <Library/UdpIoLib.h>
29 #include <Library/BaseLib.h>
30 #include <Library/DebugLib.h>
31 #include <Library/UefiBootServicesTableLib.h>
32 #include <Library/MemoryAllocationLib.h>
33 #include <Library/BaseMemoryLib.h>
34
35 STATIC
36 VOID
37 EFIAPI
38 UdpIoOnDgramSentDpc (
39 IN VOID *Context
40 );
41
42 STATIC
43 VOID
44 EFIAPI
45 UdpIoOnDgramSent (
46 IN EFI_EVENT Event,
47 IN VOID *Context
48 );
49
50 STATIC
51 VOID
52 EFIAPI
53 UdpIoOnDgramRcvd (
54 IN EFI_EVENT Event,
55 IN VOID *Context
56 );
57
58
59 /**
60 Wrap a transmit request into a UDP_TX_TOKEN.
61
62 @param UdpIo The UdpIo port to send packet to
63 @param Packet The user's packet
64 @param EndPoint The local and remote access point
65 @param Gateway The overrided next hop
66 @param CallBack The function to call when transmission completed.
67 @param Context The opaque parameter to the call back
68
69 @return The wrapped transmission request or NULL if failed to allocate resources.
70
71 **/
72 STATIC
73 UDP_TX_TOKEN *
74 UdpIoWrapTx (
75 IN UDP_IO_PORT *UdpIo,
76 IN NET_BUF *Packet,
77 IN UDP_POINTS *EndPoint, OPTIONAL
78 IN IP4_ADDR Gateway,
79 IN UDP_IO_CALLBACK CallBack,
80 IN VOID *Context
81 )
82 {
83 UDP_TX_TOKEN *Token;
84 EFI_UDP4_COMPLETION_TOKEN *UdpToken;
85 EFI_UDP4_TRANSMIT_DATA *UdpTxData;
86 EFI_STATUS Status;
87 UINT32 Count;
88 IP4_ADDR Ip;
89
90 Token = AllocatePool (sizeof (UDP_TX_TOKEN) +
91 sizeof (EFI_UDP4_FRAGMENT_DATA) * (Packet->BlockOpNum - 1));
92
93 if (Token == NULL) {
94 return NULL;
95 }
96
97 Token->Signature = UDP_IO_TX_SIGNATURE;
98 InitializeListHead (&Token->Link);
99
100 Token->UdpIo = UdpIo;
101 Token->CallBack = CallBack;
102 Token->Packet = Packet;
103 Token->Context = Context;
104
105 UdpToken = &(Token->UdpToken);
106 UdpToken->Status = EFI_NOT_READY;
107
108 Status = gBS->CreateEvent (
109 EVT_NOTIFY_SIGNAL,
110 TPL_NOTIFY,
111 UdpIoOnDgramSent,
112 Token,
113 &UdpToken->Event
114 );
115
116 if (EFI_ERROR (Status)) {
117 gBS->FreePool (Token);
118 return NULL;
119 }
120
121 UdpTxData = &Token->UdpTxData;
122 UdpToken->Packet.TxData = UdpTxData;
123
124 UdpTxData->UdpSessionData = NULL;
125 UdpTxData->GatewayAddress = NULL;
126
127 if (EndPoint != NULL) {
128 Ip = HTONL (EndPoint->LocalAddr);
129 CopyMem (&Token->UdpSession.SourceAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
130
131 Ip = HTONL (EndPoint->RemoteAddr);
132 CopyMem (&Token->UdpSession.DestinationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
133
134 Token->UdpSession.SourcePort = EndPoint->LocalPort;
135 Token->UdpSession.DestinationPort = EndPoint->RemotePort;
136 UdpTxData->UdpSessionData = &Token->UdpSession;
137 }
138
139 if (Gateway != 0) {
140 Ip = HTONL (Gateway);
141 CopyMem (&Token->Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));
142
143 UdpTxData->GatewayAddress = &Token->Gateway;
144 }
145
146 UdpTxData->DataLength = Packet->TotalSize;
147 Count = Packet->BlockOpNum;
148 NetbufBuildExt (Packet, (NET_FRAGMENT *) UdpTxData->FragmentTable, &Count);
149 UdpTxData->FragmentCount = Count;
150
151 return Token;
152 }
153
154
155 /**
156 Free a UDP_TX_TOKEN. The event is closed and memory released.
157
158 @param Token The UDP_TX_TOKEN to release.
159
160 @return None
161
162 **/
163 VOID
164 UdpIoFreeTxToken (
165 IN UDP_TX_TOKEN *Token
166 )
167 {
168 gBS->CloseEvent (Token->UdpToken.Event);
169 gBS->FreePool (Token);
170 }
171
172
173 /**
174 Create a UDP_RX_TOKEN to wrap the request.
175
176 @param UdpIo The UdpIo to receive packets from
177 @param CallBack The function to call when receive finished.
178 @param Context The opaque parameter to the CallBack
179 @param HeadLen The head length to reserver for the packet.
180
181 @return The Wrapped request or NULL if failed to allocate resources.
182
183 **/
184 UDP_RX_TOKEN *
185 UdpIoCreateRxToken (
186 IN UDP_IO_PORT *UdpIo,
187 IN UDP_IO_CALLBACK CallBack,
188 IN VOID *Context,
189 IN UINT32 HeadLen
190 )
191 {
192 UDP_RX_TOKEN *Token;
193 EFI_STATUS Status;
194
195 Token = AllocatePool (sizeof (UDP_RX_TOKEN));
196
197 if (Token == NULL) {
198 return NULL;
199 }
200
201 Token->Signature = UDP_IO_RX_SIGNATURE;
202 Token->UdpIo = UdpIo;
203 Token->CallBack = CallBack;
204 Token->Context = Context;
205 Token->HeadLen = HeadLen;
206
207 Token->UdpToken.Status = EFI_NOT_READY;
208 Token->UdpToken.Packet.RxData = NULL;
209
210 Status = gBS->CreateEvent (
211 EVT_NOTIFY_SIGNAL,
212 TPL_NOTIFY,
213 UdpIoOnDgramRcvd,
214 Token,
215 &Token->UdpToken.Event
216 );
217
218 if (EFI_ERROR (Status)) {
219 gBS->FreePool (Token);
220 return NULL;
221 }
222
223 return Token;
224 }
225
226
227 /**
228 Free a receive request wrap.
229
230 @param Token The receive request to release.
231
232 @return None
233
234 **/
235 VOID
236 UdpIoFreeRxToken (
237 IN UDP_RX_TOKEN *Token
238 )
239 {
240 gBS->CloseEvent (Token->UdpToken.Event);
241 gBS->FreePool (Token);
242 }
243
244
245 /**
246 Create a UDP IO port to access the UDP service. It will
247 create and configure a UDP child.
248
249 @param Controller The controller that has the UDP service binding
250 protocol installed.
251 @param Image The image handle for the driver.
252 @param Configure The function to configure the created UDP child
253 @param Context The opaque parameter for the Configure funtion.
254
255 @return A point to just created UDP IO port or NULL if failed.
256
257 **/
258 UDP_IO_PORT *
259 UdpIoCreatePort (
260 IN EFI_HANDLE Controller,
261 IN EFI_HANDLE Image,
262 IN UDP_IO_CONFIG Configure,
263 IN VOID *Context
264 )
265 {
266 UDP_IO_PORT *UdpIo;
267 EFI_STATUS Status;
268
269 ASSERT (Configure != NULL);
270
271 UdpIo = AllocatePool (sizeof (UDP_IO_PORT));
272
273 if (UdpIo == NULL) {
274 return NULL;
275 }
276
277 UdpIo->Signature = UDP_IO_SIGNATURE;
278 InitializeListHead (&UdpIo->Link);
279 UdpIo->RefCnt = 1;
280
281 UdpIo->Controller = Controller;
282 UdpIo->Image = Image;
283
284 InitializeListHead (&UdpIo->SentDatagram);
285 UdpIo->RecvRequest = NULL;
286 UdpIo->UdpHandle = NULL;
287
288 //
289 // Create a UDP child then open and configure it
290 //
291 Status = NetLibCreateServiceChild (
292 Controller,
293 Image,
294 &gEfiUdp4ServiceBindingProtocolGuid,
295 &UdpIo->UdpHandle
296 );
297
298 if (EFI_ERROR (Status)) {
299 goto FREE_MEM;
300 }
301
302 Status = gBS->OpenProtocol (
303 UdpIo->UdpHandle,
304 &gEfiUdp4ProtocolGuid,
305 (VOID **) &UdpIo->Udp,
306 Image,
307 Controller,
308 EFI_OPEN_PROTOCOL_BY_DRIVER
309 );
310
311 if (EFI_ERROR (Status)) {
312 goto FREE_CHILD;
313 }
314
315 if (EFI_ERROR (Configure (UdpIo, Context))) {
316 goto CLOSE_PROTOCOL;
317 }
318
319 Status = UdpIo->Udp->GetModeData (UdpIo->Udp, NULL, NULL, NULL, &UdpIo->SnpMode);
320
321 if (EFI_ERROR (Status)) {
322 goto CLOSE_PROTOCOL;
323 }
324
325 return UdpIo;
326
327 CLOSE_PROTOCOL:
328 gBS->CloseProtocol (UdpIo->UdpHandle, &gEfiUdp4ProtocolGuid, Image, Controller);
329
330 FREE_CHILD:
331 NetLibDestroyServiceChild (
332 Controller,
333 Image,
334 &gEfiUdp4ServiceBindingProtocolGuid,
335 UdpIo->UdpHandle
336 );
337
338 FREE_MEM:
339 gBS->FreePool (UdpIo);
340 return NULL;
341 }
342
343
344 /**
345 Cancel all the sent datagram that pass the selection of ToCancel.
346 If ToCancel is NULL, all the datagrams are cancelled.
347
348 @param UdpIo The UDP IO port to cancel packet
349 @param IoStatus The IoStatus to return to the packet owners.
350 @param ToCancel The select funtion to test whether to cancel this
351 packet or not.
352 @param Context The opaque parameter to the ToCancel.
353
354 @return None
355
356 **/
357 STATIC
358 VOID
359 UdpIoCancelDgrams (
360 IN UDP_IO_PORT *UdpIo,
361 IN EFI_STATUS IoStatus,
362 IN UDP_IO_TO_CANCEL ToCancel, OPTIONAL
363 IN VOID *Context
364 )
365 {
366 LIST_ENTRY *Entry;
367 LIST_ENTRY *Next;
368 UDP_TX_TOKEN *Token;
369
370 NET_LIST_FOR_EACH_SAFE (Entry, Next, &UdpIo->SentDatagram) {
371 Token = NET_LIST_USER_STRUCT (Entry, UDP_TX_TOKEN, Link);
372
373 if ((ToCancel == NULL) || (ToCancel (Token, Context))) {
374 UdpIo->Udp->Cancel (UdpIo->Udp, &Token->UdpToken);
375 }
376 }
377 }
378
379
380 /**
381 Free the UDP IO port and all its related resources including
382 all the transmitted packet.
383
384 @param UdpIo The UDP IO port to free.
385
386 @retval EFI_SUCCESS The UDP IO port is freed.
387
388 **/
389 EFI_STATUS
390 UdpIoFreePort (
391 IN UDP_IO_PORT *UdpIo
392 )
393 {
394 UDP_RX_TOKEN *RxToken;
395
396 //
397 // Cancel all the sent datagram and receive requests. The
398 // callbacks of transmit requests are executed to allow the
399 // caller to release the resource. The callback of receive
400 // request are NOT executed. This is because it is most
401 // likely that the current user of the UDP IO port is closing
402 // itself.
403 //
404 UdpIoCancelDgrams (UdpIo, EFI_ABORTED, NULL, NULL);
405
406 if ((RxToken = UdpIo->RecvRequest) != NULL) {
407 UdpIo->Udp->Cancel (UdpIo->Udp, &RxToken->UdpToken);
408 }
409
410 //
411 // Close then destory the UDP child
412 //
413 gBS->CloseProtocol (
414 UdpIo->UdpHandle,
415 &gEfiUdp4ProtocolGuid,
416 UdpIo->Image,
417 UdpIo->Controller
418 );
419
420 NetLibDestroyServiceChild (
421 UdpIo->Controller,
422 UdpIo->Image,
423 &gEfiUdp4ServiceBindingProtocolGuid,
424 UdpIo->UdpHandle
425 );
426
427 if (!IsListEmpty(&UdpIo->Link)) {
428 RemoveEntryList (&UdpIo->Link);
429 }
430
431 gBS->FreePool (UdpIo);
432 return EFI_SUCCESS;
433 }
434
435
436 /**
437 Clean up the UDP IO port. It will release all the transmitted
438 datagrams and receive request. It will also configure NULL the
439 UDP child.
440
441 @param UdpIo UDP IO port to clean up.
442
443 @return None
444
445 **/
446 VOID
447 UdpIoCleanPort (
448 IN UDP_IO_PORT *UdpIo
449 )
450 {
451 UDP_RX_TOKEN *RxToken;
452
453 //
454 // Cancel all the sent datagram and receive requests.
455 //
456 UdpIoCancelDgrams (UdpIo, EFI_ABORTED, NULL, NULL);
457
458 if ((RxToken = UdpIo->RecvRequest) != NULL) {
459 UdpIo->Udp->Cancel (UdpIo->Udp, &RxToken->UdpToken);
460 }
461
462 UdpIo->Udp->Configure (UdpIo->Udp, NULL);
463 }
464
465
466 /**
467 The callback function when the packet is sent by UDP.
468 It will remove the packet from the local list then call
469 the packet owner's callback function.
470
471 @param Context The UDP TX Token.
472
473 @return None
474
475 **/
476 STATIC
477 VOID
478 EFIAPI
479 UdpIoOnDgramSentDpc (
480 IN VOID *Context
481 )
482 {
483 UDP_TX_TOKEN *Token;
484
485 Token = (UDP_TX_TOKEN *) Context;
486 ASSERT (Token->Signature == UDP_IO_TX_SIGNATURE);
487
488 RemoveEntryList (&Token->Link);
489 Token->CallBack (Token->Packet, NULL, Token->UdpToken.Status, Token->Context);
490
491 UdpIoFreeTxToken (Token);
492 }
493
494 /**
495 Request UdpIoOnDgramSentDpc as a DPC at TPL_CALLBACK.
496
497 @param Event The event signalled.
498 @param Context The UDP TX Token.
499
500 @return None
501
502 **/
503 STATIC
504 VOID
505 EFIAPI
506 UdpIoOnDgramSent (
507 IN EFI_EVENT Event,
508 IN VOID *Context
509 )
510 {
511 //
512 // Request UdpIoOnDgramSentDpc as a DPC at TPL_CALLBACK
513 //
514 NetLibQueueDpc (TPL_CALLBACK, UdpIoOnDgramSentDpc, Context);
515 }
516
517
518 /**
519 Send a packet through the UDP IO port.
520
521 @param UdpIo The UDP IO Port to send the packet through
522 @param Packet The packet to send
523 @param EndPoint The local and remote access point
524 @param Gateway The gateway to use
525 @param CallBack The call back function to call when packet is
526 transmitted or failed.
527 @param Context The opque parameter to the CallBack
528
529 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the packet
530 @retval EFI_SUCCESS The packet is successfully delivered to UDP for
531 transmission.
532
533 **/
534 EFI_STATUS
535 UdpIoSendDatagram (
536 IN UDP_IO_PORT *UdpIo,
537 IN NET_BUF *Packet,
538 IN UDP_POINTS *EndPoint, OPTIONAL
539 IN IP4_ADDR Gateway,
540 IN UDP_IO_CALLBACK CallBack,
541 IN VOID *Context
542 )
543 {
544 UDP_TX_TOKEN *Token;
545 EFI_STATUS Status;
546
547 Token = UdpIoWrapTx (UdpIo, Packet, EndPoint, Gateway, CallBack, Context);
548
549 if (Token == NULL) {
550 return EFI_OUT_OF_RESOURCES;
551 }
552
553 //
554 // Insert the tx token into SendDatagram list before transmitting it. Remove
555 // it from the list if the returned status is not EFI_SUCCESS.
556 //
557 InsertHeadList (&UdpIo->SentDatagram, &Token->Link);
558 Status = UdpIo->Udp->Transmit (UdpIo->Udp, &Token->UdpToken);
559 if (EFI_ERROR (Status)) {
560 RemoveEntryList (&Token->Link);
561 UdpIoFreeTxToken (Token);
562 return Status;
563 }
564
565 return EFI_SUCCESS;
566 }
567
568
569 /**
570 The selection function to cancel a single sent datagram.
571
572 @param Token The UDP TX token to test againist.
573 @param Context The context
574
575 @return TRUE if the packet is to be cancelled, otherwise FALSE.
576
577 **/
578 STATIC
579 BOOLEAN
580 UdpIoCancelSingleDgram (
581 IN UDP_TX_TOKEN *Token,
582 IN VOID *Context
583 )
584 {
585 NET_BUF *Packet;
586
587 Packet = (NET_BUF *) Context;
588
589 if (Token->Packet == Packet) {
590 return TRUE;
591 }
592
593 return FALSE;
594 }
595
596
597 /**
598 Cancel a single sent datagram.
599
600 @param UdpIo The UDP IO port to cancel the packet from
601 @param Packet The packet to cancel
602
603 @return None
604
605 **/
606 VOID
607 UdpIoCancelSentDatagram (
608 IN UDP_IO_PORT *UdpIo,
609 IN NET_BUF *Packet
610 )
611 {
612 UdpIoCancelDgrams (UdpIo, EFI_ABORTED, UdpIoCancelSingleDgram, Packet);
613 }
614
615
616 /**
617 Recycle the received UDP data.
618
619 @param Context The UDP_RX_TOKEN
620
621 @return None
622
623 **/
624 STATIC
625 VOID
626 UdpIoRecycleDgram (
627 IN VOID *Context
628 )
629 {
630 UDP_RX_TOKEN *Token;
631
632 Token = (UDP_RX_TOKEN *) Context;
633 gBS->SignalEvent (Token->UdpToken.Packet.RxData->RecycleSignal);
634 UdpIoFreeRxToken (Token);
635 }
636
637 /**
638 The event handle for UDP receive request. It will build
639 a NET_BUF from the recieved UDP data, then deliver it
640 to the receiver.
641
642 @param Context The UDP RX token.
643
644 @return None
645
646 **/
647 STATIC
648 VOID
649 EFIAPI
650 UdpIoOnDgramRcvdDpc (
651 IN VOID *Context
652 )
653 {
654 EFI_UDP4_COMPLETION_TOKEN *UdpToken;
655 EFI_UDP4_RECEIVE_DATA *UdpRxData;
656 EFI_UDP4_SESSION_DATA *UdpSession;
657 UDP_RX_TOKEN *Token;
658 UDP_POINTS Points;
659 NET_BUF *Netbuf;
660
661 Token = (UDP_RX_TOKEN *) Context;
662
663 ASSERT ((Token->Signature == UDP_IO_RX_SIGNATURE) &&
664 (Token == Token->UdpIo->RecvRequest));
665
666 //
667 // Clear the receive request first in case that the caller
668 // wants to restart the receive in the callback.
669 //
670 Token->UdpIo->RecvRequest = NULL;
671
672 UdpToken = &Token->UdpToken;
673 UdpRxData = UdpToken->Packet.RxData;
674
675 if (EFI_ERROR (UdpToken->Status) || (UdpRxData == NULL)) {
676 if (UdpToken->Status != EFI_ABORTED) {
677 //
678 // Invoke the CallBack only if the reception is not actively aborted.
679 //
680 Token->CallBack (NULL, NULL, UdpToken->Status, Token->Context);
681 }
682
683 UdpIoFreeRxToken (Token);
684 return;
685 }
686
687 //
688 // Build a NET_BUF from the UDP receive data, then deliver it up.
689 //
690 Netbuf = NetbufFromExt (
691 (NET_FRAGMENT *) UdpRxData->FragmentTable,
692 UdpRxData->FragmentCount,
693 0,
694 (UINT32) Token->HeadLen,
695 UdpIoRecycleDgram,
696 Token
697 );
698
699 if (Netbuf == NULL) {
700 gBS->SignalEvent (UdpRxData->RecycleSignal);
701 Token->CallBack (NULL, NULL, EFI_OUT_OF_RESOURCES, Token->Context);
702
703 UdpIoFreeRxToken (Token);
704 return;
705 }
706
707 UdpSession = &UdpRxData->UdpSession;
708 Points.LocalPort = UdpSession->DestinationPort;
709 Points.RemotePort = UdpSession->SourcePort;
710
711 CopyMem (&Points.LocalAddr, &UdpSession->DestinationAddress, sizeof (IP4_ADDR));
712 CopyMem (&Points.RemoteAddr, &UdpSession->SourceAddress, sizeof (IP4_ADDR));
713 Points.LocalAddr = NTOHL (Points.LocalAddr);
714 Points.RemoteAddr = NTOHL (Points.RemoteAddr);
715
716 Token->CallBack (Netbuf, &Points, EFI_SUCCESS, Token->Context);
717 }
718
719 /**
720 Request UdpIoOnDgramRcvdDpc as a DPC at TPL_CALLBACK.
721
722 @param Event The UDP receive request event.
723 @param Context The UDP RX token.
724
725 @return None
726
727 **/
728 STATIC
729 VOID
730 EFIAPI
731 UdpIoOnDgramRcvd (
732 IN EFI_EVENT Event,
733 IN VOID *Context
734 )
735 /*++
736
737 Routine Description:
738
739 Request UdpIoOnDgramRcvdDpc as a DPC at TPL_CALLBACK
740
741 Arguments:
742
743 Event - The UDP receive request event
744 Context - The UDP RX token.
745
746 Returns:
747
748 None
749
750 --*/
751 {
752 //
753 // Request UdpIoOnDgramRcvdDpc as a DPC at TPL_CALLBACK
754 //
755 NetLibQueueDpc (TPL_CALLBACK, UdpIoOnDgramRcvdDpc, Context);
756 }
757
758
759 /**
760 Issue a receive request to the UDP IO port.
761
762 @param UdpIo The UDP IO port to recieve the packet from.
763 @param CallBack The call back function to execute when receive
764 finished.
765 @param Context The opque context to the call back
766 @param HeadLen The lenght of the application's header
767
768 @retval EFI_ALREADY_STARTED There is already a pending receive request. Only
769 one receive request is supported.
770 @retval EFI_OUT_OF_RESOURCES Failed to allocate some resource.
771 @retval EFI_SUCCESS The receive request is issued successfully.
772
773 **/
774 EFI_STATUS
775 UdpIoRecvDatagram (
776 IN UDP_IO_PORT *UdpIo,
777 IN UDP_IO_CALLBACK CallBack,
778 IN VOID *Context,
779 IN UINT32 HeadLen
780 )
781 {
782 UDP_RX_TOKEN *Token;
783 EFI_STATUS Status;
784
785 if (UdpIo->RecvRequest != NULL) {
786 return EFI_ALREADY_STARTED;
787 }
788
789 Token = UdpIoCreateRxToken (UdpIo, CallBack, Context, HeadLen);
790
791 if (Token == NULL) {
792 return EFI_OUT_OF_RESOURCES;
793 }
794
795 UdpIo->RecvRequest = Token;
796 Status = UdpIo->Udp->Receive (UdpIo->Udp, &Token->UdpToken);
797
798 if (EFI_ERROR (Status)) {
799 UdpIo->RecvRequest = NULL;
800 UdpIoFreeRxToken (Token);
801 }
802
803 return Status;
804 }