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