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