Scrubbed part of the code.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Dhcp4Dxe / Dhcp4Driver.c
1 /** @file\r
2 \r
3 Copyright (c) 2006 - 2008, Intel Corporation\r
4 All rights reserved. This program and the accompanying materials\r
5 are licensed and made available under the terms and conditions of the BSD License\r
6 which accompanies this distribution.  The full text of the license may be found at\r
7 http://opensource.org/licenses/bsd-license.php\r
8 \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11 \r
12 \r
13 Module Name:\r
14 \r
15   Dhcp4Driver.c\r
16 \r
17 Abstract:\r
18 \r
19 \r
20 **/\r
21 \r
22 #include "Dhcp4Impl.h"\r
23 #include "Dhcp4Driver.h"\r
24 \r
25 EFI_DRIVER_BINDING_PROTOCOL gDhcp4DriverBinding = {\r
26   Dhcp4DriverBindingSupported,\r
27   Dhcp4DriverBindingStart,\r
28   Dhcp4DriverBindingStop,\r
29   0xa,\r
30   NULL,\r
31   NULL\r
32 };\r
33 \r
34 EFI_SERVICE_BINDING_PROTOCOL mDhcp4ServiceBindingTemplate = {\r
35   Dhcp4ServiceBindingCreateChild,\r
36   Dhcp4ServiceBindingDestroyChild\r
37 };\r
38 \r
39 /**\r
40   This is the declaration of an EFI image entry point. This entry point is\r
41   the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including\r
42   both device drivers and bus drivers.\r
43   \r
44   Entry point of the DHCP driver to install various protocols.\r
45 \r
46   @param  ImageHandle           The firmware allocated handle for the UEFI image.\r
47   @param  SystemTable           A pointer to the EFI System Table.\r
48 \r
49   @retval EFI_SUCCESS           The operation completed successfully.\r
50   @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.\r
51 \r
52 **/\r
53 EFI_STATUS\r
54 EFIAPI\r
55 Dhcp4DriverEntryPoint (\r
56   IN EFI_HANDLE             ImageHandle,\r
57   IN EFI_SYSTEM_TABLE       *SystemTable\r
58   )\r
59 {\r
60   return EfiLibInstallDriverBindingComponentName2 (\r
61            ImageHandle,\r
62            SystemTable,\r
63            &gDhcp4DriverBinding,\r
64            ImageHandle,\r
65            &gDhcp4ComponentName,\r
66            &gDhcp4ComponentName2\r
67            );\r
68 }\r
69 \r
70 \r
71 /**\r
72   Test to see if this driver supports ControllerHandle. This service\r
73   is called by the EFI boot service ConnectController(). In\r
74   order to make drivers as small as possible, there are a few calling\r
75   restrictions for this service. ConnectController() must\r
76   follow these calling restrictions. If any other agent wishes to call\r
77   Supported() it must also follow these calling restrictions.\r
78 \r
79   @param  This                Protocol instance pointer.\r
80   @param  ControllerHandle    Handle of device to test\r
81   @param  RemainingDevicePath Optional parameter use to pick a specific child\r
82                               device to start.\r
83 \r
84   @retval EFI_SUCCESS         This driver supports this device\r
85   @retval EFI_ALREADY_STARTED This driver is already running on this device\r
86   @retval other               This driver does not support this device\r
87 \r
88 **/\r
89 EFI_STATUS\r
90 EFIAPI\r
91 Dhcp4DriverBindingSupported (\r
92   IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
93   IN EFI_HANDLE                   ControllerHandle,\r
94   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
95   )\r
96 {\r
97   EFI_STATUS  Status;\r
98 \r
99   Status = gBS->OpenProtocol (\r
100                   ControllerHandle,\r
101                   &gEfiUdp4ServiceBindingProtocolGuid,\r
102                   NULL,\r
103                   This->DriverBindingHandle,\r
104                   ControllerHandle,\r
105                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
106                   );\r
107 \r
108   return Status;\r
109 }\r
110 \r
111 \r
112 \r
113 /**\r
114   Configure the default UDP child to receive all the DHCP traffics\r
115   on this network interface.\r
116 \r
117   @param  UdpIo                  The UDP IO port to configure\r
118   @param  Context                The context to the function\r
119 \r
120   @retval EFI_SUCCESS            The UDP IO port is successfully configured.\r
121   @retval Others                 Failed to configure the UDP child.\r
122 \r
123 **/\r
124 EFI_STATUS\r
125 DhcpConfigUdpIo (\r
126   IN UDP_IO_PORT            *UdpIo,\r
127   IN VOID                   *Context\r
128   )\r
129 {\r
130   EFI_UDP4_CONFIG_DATA      UdpConfigData;\r
131 \r
132   UdpConfigData.AcceptBroadcast           = TRUE;\r
133   UdpConfigData.AcceptPromiscuous         = FALSE;\r
134   UdpConfigData.AcceptAnyPort             = FALSE;\r
135   UdpConfigData.AllowDuplicatePort        = TRUE;\r
136   UdpConfigData.TypeOfService             = 0;\r
137   UdpConfigData.TimeToLive                = 64;\r
138   UdpConfigData.DoNotFragment             = FALSE;\r
139   UdpConfigData.ReceiveTimeout            = 0;\r
140   UdpConfigData.TransmitTimeout           = 0;\r
141 \r
142   UdpConfigData.UseDefaultAddress         = FALSE;\r
143   UdpConfigData.StationPort               = DHCP_CLIENT_PORT;\r
144   UdpConfigData.RemotePort                = DHCP_SERVER_PORT;\r
145 \r
146   ZeroMem (&UdpConfigData.StationAddress, sizeof (EFI_IPv4_ADDRESS));\r
147   ZeroMem (&UdpConfigData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
148   ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));\r
149 \r
150   return UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfigData);;\r
151 }\r
152 \r
153 \r
154 \r
155 /**\r
156   Destory the DHCP service. The Dhcp4 service may be partly initialized,\r
157   or partly destroyed. If a resource is destroyed, it is marked as so in\r
158   case the destroy failed and being called again later.\r
159 \r
160   @param  DhcpSb                 The DHCP service instance to destory.\r
161 \r
162   @retval EFI_SUCCESS            Always return success.\r
163 \r
164 **/\r
165 EFI_STATUS\r
166 Dhcp4CloseService (\r
167   IN DHCP_SERVICE           *DhcpSb\r
168   )\r
169 {\r
170   DhcpCleanLease (DhcpSb);\r
171 \r
172   if (DhcpSb->UdpIo != NULL) {\r
173     UdpIoFreePort (DhcpSb->UdpIo);\r
174     DhcpSb->UdpIo = NULL;\r
175   }\r
176 \r
177   if (DhcpSb->Timer != NULL) {\r
178     gBS->SetTimer (DhcpSb->Timer, TimerCancel, 0);\r
179     gBS->CloseEvent (DhcpSb->Timer);\r
180 \r
181     DhcpSb->Timer = NULL;\r
182   }\r
183 \r
184   return EFI_SUCCESS;\r
185 }\r
186 \r
187 \r
188 \r
189 /**\r
190   Create a new DHCP service binding instance for the controller.\r
191 \r
192   @param  Controller             The controller to install DHCP service binding\r
193                                  protocol onto\r
194   @param  ImageHandle            The driver's image handle\r
195   @param  Service                The variable to receive the created DHCP service\r
196                                  instance.\r
197 \r
198   @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource .\r
199   @retval EFI_SUCCESS            The DHCP service instance is created.\r
200   @retval other                  Other error occurs.\r
201 \r
202 **/\r
203 EFI_STATUS\r
204 Dhcp4CreateService (\r
205   IN  EFI_HANDLE            Controller,\r
206   IN  EFI_HANDLE            ImageHandle,\r
207   OUT DHCP_SERVICE          **Service\r
208   )\r
209 {\r
210   DHCP_SERVICE              *DhcpSb;\r
211   EFI_STATUS                Status;\r
212 \r
213   *Service  = NULL;\r
214   DhcpSb    = AllocateZeroPool (sizeof (DHCP_SERVICE));\r
215 \r
216   if (DhcpSb == NULL) {\r
217     return EFI_OUT_OF_RESOURCES;\r
218   }\r
219 \r
220   DhcpSb->Signature       = DHCP_SERVICE_SIGNATURE;\r
221   DhcpSb->ServiceState    = DHCP_UNCONFIGED;\r
222   DhcpSb->InDestory       = FALSE;\r
223   DhcpSb->Controller      = Controller;\r
224   DhcpSb->Image           = ImageHandle;\r
225   InitializeListHead (&DhcpSb->Children);\r
226   DhcpSb->DhcpState       = Dhcp4Stopped;\r
227   DhcpSb->Xid             = NET_RANDOM (NetRandomInitSeed ());\r
228   CopyMem (\r
229     &DhcpSb->ServiceBinding,\r
230     &mDhcp4ServiceBindingTemplate,\r
231     sizeof (EFI_SERVICE_BINDING_PROTOCOL)\r
232     );\r
233   //\r
234   // Create various resources, UdpIo, Timer, and get Mac address\r
235   //\r
236   Status = gBS->CreateEvent (\r
237                   EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
238                   TPL_CALLBACK,\r
239                   DhcpOnTimerTick,\r
240                   DhcpSb,\r
241                   &DhcpSb->Timer\r
242                   );\r
243 \r
244   if (EFI_ERROR (Status)) {\r
245     goto ON_ERROR;\r
246   }\r
247 \r
248   DhcpSb->UdpIo = UdpIoCreatePort (Controller, ImageHandle, DhcpConfigUdpIo, NULL);\r
249 \r
250   if (DhcpSb->UdpIo == NULL) {\r
251     Status = EFI_OUT_OF_RESOURCES;\r
252     goto ON_ERROR;\r
253   }\r
254 \r
255   DhcpSb->HwLen  = (UINT8) DhcpSb->UdpIo->SnpMode.HwAddressSize;\r
256   DhcpSb->HwType = DhcpSb->UdpIo->SnpMode.IfType;\r
257   CopyMem (&DhcpSb->Mac, &DhcpSb->UdpIo->SnpMode.CurrentAddress, sizeof (DhcpSb->Mac));\r
258 \r
259   *Service       = DhcpSb;\r
260   return EFI_SUCCESS;\r
261 \r
262 ON_ERROR:\r
263   Dhcp4CloseService (DhcpSb);\r
264   gBS->FreePool (DhcpSb);\r
265 \r
266   return Status;\r
267 }\r
268 \r
269 \r
270 /**\r
271   Start this driver on ControllerHandle. This service is called by the\r
272   EFI boot service ConnectController(). In order to make\r
273   drivers as small as possible, there are a few calling restrictions for\r
274   this service. ConnectController() must follow these\r
275   calling restrictions. If any other agent wishes to call Start() it\r
276   must also follow these calling restrictions.\r
277 \r
278   @param  This                 Protocol instance pointer.\r
279   @param  ControllerHandle     Handle of device to bind driver to\r
280   @param  RemainingDevicePath  Optional parameter use to pick a specific child\r
281                                device to start.\r
282 \r
283   @retval EFI_SUCCESS          This driver is added to ControllerHandle\r
284   @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle\r
285   @retval other                This driver does not support this device\r
286 \r
287 **/\r
288 EFI_STATUS\r
289 EFIAPI\r
290 Dhcp4DriverBindingStart (\r
291   IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
292   IN EFI_HANDLE                   ControllerHandle,\r
293   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
294   )\r
295 {\r
296   DHCP_SERVICE              *DhcpSb;\r
297   EFI_STATUS                Status;\r
298 \r
299   //\r
300   // First: test for the DHCP4 Protocol\r
301   //\r
302   Status = gBS->OpenProtocol (\r
303                   ControllerHandle,\r
304                   &gEfiDhcp4ServiceBindingProtocolGuid,\r
305                   NULL,\r
306                   This->DriverBindingHandle,\r
307                   ControllerHandle,\r
308                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
309                   );\r
310 \r
311   if (Status == EFI_SUCCESS) {\r
312     return EFI_ALREADY_STARTED;\r
313   }\r
314 \r
315   Status = Dhcp4CreateService (ControllerHandle, This->DriverBindingHandle, &DhcpSb);\r
316 \r
317   if (EFI_ERROR (Status)) {\r
318     return Status;\r
319   }\r
320 \r
321   Status = gBS->SetTimer (DhcpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);\r
322 \r
323   if (EFI_ERROR (Status)) {\r
324     goto ON_ERROR;\r
325   }\r
326 \r
327   //\r
328   // Install the Dhcp4ServiceBinding Protocol onto ControlerHandle\r
329   //\r
330   Status = gBS->InstallMultipleProtocolInterfaces (\r
331                   &ControllerHandle,\r
332                   &gEfiDhcp4ServiceBindingProtocolGuid,\r
333                   &DhcpSb->ServiceBinding,\r
334                   NULL\r
335                   );\r
336 \r
337   if (EFI_ERROR (Status)) {\r
338     goto ON_ERROR;\r
339   }\r
340 \r
341   return Status;\r
342 \r
343 ON_ERROR:\r
344   Dhcp4CloseService (DhcpSb);\r
345   gBS->FreePool (DhcpSb);\r
346   return Status;\r
347 }\r
348 \r
349 \r
350 /**\r
351   Stop this driver on ControllerHandle. This service is called by the\r
352   EFI boot service DisconnectController(). In order to\r
353   make drivers as small as possible, there are a few calling\r
354   restrictions for this service. DisconnectController()\r
355   must follow these calling restrictions. If any other agent wishes\r
356   to call Stop() it must also follow these calling restrictions.\r
357   \r
358   @param  This              Protocol instance pointer.\r
359   @param  ControllerHandle  Handle of device to stop driver on\r
360   @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of\r
361                             children is zero stop the entire bus driver.\r
362   @param  ChildHandleBuffer List of Child Handles to Stop.\r
363 \r
364   @retval EFI_SUCCESS       This driver is removed ControllerHandle\r
365   @retval other             This driver was not removed from this device\r
366 \r
367 **/\r
368 EFI_STATUS\r
369 EFIAPI\r
370 Dhcp4DriverBindingStop (\r
371   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
372   IN  EFI_HANDLE                   ControllerHandle,\r
373   IN  UINTN                        NumberOfChildren,\r
374   IN  EFI_HANDLE                   *ChildHandleBuffer\r
375   )\r
376 {\r
377   EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
378   DHCP_SERVICE                  *DhcpSb;\r
379   DHCP_PROTOCOL                 *Instance;\r
380   EFI_HANDLE                    NicHandle;\r
381   EFI_STATUS                    Status;\r
382   EFI_TPL                       OldTpl;\r
383 \r
384   //\r
385   // DHCP driver opens UDP child, So, the ControllerHandle is the\r
386   // UDP child handle. locate the Nic handle first.\r
387   //\r
388   NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);\r
389 \r
390   if (NicHandle == NULL) {\r
391     return EFI_DEVICE_ERROR;\r
392   }\r
393 \r
394    Status = gBS->OpenProtocol (\r
395                   NicHandle,\r
396                   &gEfiDhcp4ServiceBindingProtocolGuid,\r
397                   (VOID **) &ServiceBinding,\r
398                   This->DriverBindingHandle,\r
399                   NicHandle,\r
400                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
401                   );\r
402 \r
403   if (EFI_ERROR (Status)) {\r
404     return EFI_DEVICE_ERROR;\r
405   }\r
406 \r
407   DhcpSb = DHCP_SERVICE_FROM_THIS (ServiceBinding);\r
408 \r
409   if (DhcpSb->InDestory) {\r
410     return EFI_SUCCESS;\r
411   }\r
412 \r
413   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
414 \r
415   if (NumberOfChildren == 0) {\r
416 \r
417     DhcpSb->InDestory    = TRUE;\r
418     DhcpSb->ServiceState = DHCP_DESTORY;\r
419 \r
420     gBS->UninstallProtocolInterface (\r
421            NicHandle,\r
422            &gEfiDhcp4ServiceBindingProtocolGuid,\r
423            ServiceBinding\r
424            );\r
425 \r
426     Dhcp4CloseService (DhcpSb);\r
427 \r
428     gBS->FreePool (DhcpSb);\r
429   } else {\r
430     //\r
431     // Don't use NET_LIST_FOR_EACH_SAFE here, Dhcp4ServiceBindingDestoryChild\r
432     // may cause other child to be deleted.\r
433     //\r
434     while (!IsListEmpty (&DhcpSb->Children)) {\r
435       Instance = NET_LIST_HEAD (&DhcpSb->Children, DHCP_PROTOCOL, Link);\r
436       ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);\r
437     }\r
438 \r
439     if (DhcpSb->NumChildren != 0) {\r
440       Status = EFI_DEVICE_ERROR;\r
441     }\r
442   }\r
443 \r
444   gBS->RestoreTPL (OldTpl);\r
445 \r
446   return Status;\r
447 }\r
448 \r
449 \r
450 /**\r
451   Initialize a new DHCP instance.\r
452 \r
453   @param  DhcpSb                 The dhcp service instance\r
454   @param  Instance               The dhcp instance to initialize\r
455 \r
456   @return None\r
457 \r
458 **/\r
459 VOID\r
460 DhcpInitProtocol (\r
461   IN     DHCP_SERVICE           *DhcpSb,\r
462   IN OUT DHCP_PROTOCOL          *Instance\r
463   )\r
464 {\r
465   Instance->Signature         = DHCP_PROTOCOL_SIGNATURE;\r
466   CopyMem (&Instance->Dhcp4Protocol, &mDhcp4ProtocolTemplate, sizeof (Instance->Dhcp4Protocol));\r
467   InitializeListHead (&Instance->Link);\r
468   Instance->Handle            = NULL;\r
469   Instance->Service           = DhcpSb;\r
470   Instance->InDestory         = FALSE;\r
471   Instance->CompletionEvent   = NULL;\r
472   Instance->RenewRebindEvent  = NULL;\r
473   Instance->Token             = NULL;\r
474   Instance->UdpIo             = NULL;\r
475   NetbufQueInit (&Instance->ResponseQueue);\r
476 }\r
477 \r
478 \r
479 /**\r
480   Creates a child handle with a set of I/O services.\r
481 \r
482   @param  This        Protocol instance pointer.\r
483   @param  ChildHandle Pointer to the handle of the child to create.  If it is NULL,\r
484                       then a new handle is created.  If it is not NULL, then the\r
485                       I/O services are added to the existing child handle.\r
486 \r
487   @retval EFI_SUCCES            The child handle was created with the I/O services\r
488   @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
489   @retval EFI_OUT_OF_RESOURCES  There are not enough resources availabe to create\r
490                                 the child\r
491   @retval other                 The child handle was not created\r
492 \r
493 **/\r
494 EFI_STATUS\r
495 EFIAPI\r
496 Dhcp4ServiceBindingCreateChild (\r
497   IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
498   IN EFI_HANDLE                    *ChildHandle\r
499   )\r
500 {\r
501   DHCP_SERVICE              *DhcpSb;\r
502   DHCP_PROTOCOL             *Instance;\r
503   EFI_STATUS                Status;\r
504   EFI_TPL                   OldTpl;\r
505   VOID                      *Udp4;\r
506 \r
507   if ((This == NULL) || (ChildHandle == NULL)) {\r
508     return EFI_INVALID_PARAMETER;\r
509   }\r
510 \r
511   Instance = AllocatePool (sizeof (*Instance));\r
512 \r
513   if (Instance == NULL) {\r
514     return EFI_OUT_OF_RESOURCES;\r
515   }\r
516 \r
517   DhcpSb = DHCP_SERVICE_FROM_THIS (This);\r
518   DhcpInitProtocol (DhcpSb, Instance);\r
519 \r
520   //\r
521   // Install DHCP4 onto ChildHandle\r
522   //\r
523   Status = gBS->InstallMultipleProtocolInterfaces (\r
524                   ChildHandle,\r
525                   &gEfiDhcp4ProtocolGuid,\r
526                   &Instance->Dhcp4Protocol,\r
527                   NULL\r
528                   );\r
529 \r
530   if (EFI_ERROR (Status)) {\r
531     gBS->FreePool (Instance);\r
532     return Status;\r
533   }\r
534 \r
535   Instance->Handle  = *ChildHandle;\r
536 \r
537   //\r
538   // Open the Udp4 protocol BY_CHILD.\r
539   //\r
540   Status = gBS->OpenProtocol (\r
541                   DhcpSb->UdpIo->UdpHandle,\r
542                   &gEfiUdp4ProtocolGuid,\r
543                   (VOID **) &Udp4,\r
544                   gDhcp4DriverBinding.DriverBindingHandle,\r
545                   Instance->Handle,\r
546                   EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
547                   );\r
548   if (EFI_ERROR (Status)) {\r
549     gBS->UninstallMultipleProtocolInterfaces (\r
550            Instance->Handle,\r
551            &gEfiDhcp4ProtocolGuid,\r
552            &Instance->Dhcp4Protocol,\r
553            NULL\r
554            );\r
555 \r
556     gBS->FreePool (Instance);\r
557     return Status;\r
558   }\r
559 \r
560   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
561 \r
562   InsertTailList (&DhcpSb->Children, &Instance->Link);\r
563   DhcpSb->NumChildren++;\r
564 \r
565   gBS->RestoreTPL (OldTpl);\r
566 \r
567   return EFI_SUCCESS;\r
568 }\r
569 \r
570 \r
571 /**\r
572   Destroys a child handle with a set of I/O services.\r
573 \r
574   @param  This        Protocol instance pointer.\r
575   @param  ChildHandle Handle of the child to destroy\r
576 \r
577   @retval EFI_SUCCES            The I/O services were removed from the child handle\r
578   @retval EFI_UNSUPPORTED       The child handle does not support the I/O services\r
579                                 that are being removed.\r
580   @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.\r
581   @retval EFI_ACCESS_DENIED     The child handle could not be destroyed because its\r
582                                 I/O services are being used.\r
583   @retval other                 The child handle was not destroyed\r
584 \r
585 **/\r
586 EFI_STATUS\r
587 EFIAPI\r
588 Dhcp4ServiceBindingDestroyChild (\r
589   IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
590   IN EFI_HANDLE                    ChildHandle\r
591   )\r
592 {\r
593   DHCP_SERVICE              *DhcpSb;\r
594   DHCP_PROTOCOL             *Instance;\r
595   EFI_DHCP4_PROTOCOL        *Dhcp;\r
596   EFI_TPL                   OldTpl;\r
597   EFI_STATUS                Status;\r
598 \r
599   if ((This == NULL) || (ChildHandle == NULL)) {\r
600     return EFI_INVALID_PARAMETER;\r
601   }\r
602 \r
603   //\r
604   // Retrieve the private context data structures\r
605   //\r
606   Status = gBS->OpenProtocol (\r
607                   ChildHandle,\r
608                   &gEfiDhcp4ProtocolGuid,\r
609                   (VOID **) &Dhcp,\r
610                   gDhcp4DriverBinding.DriverBindingHandle,\r
611                   ChildHandle,\r
612                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
613                   );\r
614 \r
615   if (EFI_ERROR (Status)) {\r
616     return EFI_UNSUPPORTED;\r
617   }\r
618 \r
619   Instance  = DHCP_INSTANCE_FROM_THIS (Dhcp);\r
620   DhcpSb    = DHCP_SERVICE_FROM_THIS (This);\r
621 \r
622   if (Instance->Service != DhcpSb) {\r
623     return EFI_INVALID_PARAMETER;\r
624   }\r
625 \r
626   //\r
627   // A child can be destroyed more than once. For example,\r
628   // Dhcp4DriverBindingStop will destroy all of its children.\r
629   // when caller driver is being stopped, it will destory the\r
630   // dhcp child it opens.\r
631   //\r
632   if (Instance->InDestory) {\r
633     return EFI_SUCCESS;\r
634   }\r
635 \r
636   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
637   Instance->InDestory = TRUE;\r
638 \r
639   //\r
640   // Close the Udp4 protocol.\r
641   //\r
642   gBS->CloseProtocol (\r
643          DhcpSb->UdpIo->UdpHandle,\r
644          &gEfiUdp4ProtocolGuid,\r
645          gDhcp4DriverBinding.DriverBindingHandle,\r
646          ChildHandle\r
647          );\r
648 \r
649   //\r
650   // Uninstall the DHCP4 protocol first to enable a top down destruction.\r
651   //\r
652   Status = gBS->UninstallProtocolInterface (\r
653                   ChildHandle,\r
654                   &gEfiDhcp4ProtocolGuid,\r
655                   Dhcp\r
656                   );\r
657 \r
658   if (EFI_ERROR (Status)) {\r
659     Instance->InDestory = FALSE;\r
660 \r
661     gBS->RestoreTPL (OldTpl);\r
662     return Status;\r
663   }\r
664 \r
665   if (DhcpSb->ActiveChild == Instance) {\r
666     DhcpYieldControl (DhcpSb);\r
667   }\r
668 \r
669   RemoveEntryList (&Instance->Link);\r
670   DhcpSb->NumChildren--;\r
671 \r
672   gBS->RestoreTPL (OldTpl);\r
673 \r
674   gBS->FreePool (Instance);\r
675   return EFI_SUCCESS;\r
676 }\r