1. Add EFI_COMPONENT_NAME2_PROTOCOL.GetControllerName() support.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Dhcp4Dxe / Dhcp4Driver.c
1 /** @file\r
2 \r
3 Copyright (c) 2006 - 2012, 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   Destroy 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 destroy.\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->Controller      = Controller;\r
216   DhcpSb->Image           = ImageHandle;\r
217   InitializeListHead (&DhcpSb->Children);\r
218   DhcpSb->DhcpState       = Dhcp4Stopped;\r
219   DhcpSb->Xid             = NET_RANDOM (NetRandomInitSeed ());\r
220   CopyMem (\r
221     &DhcpSb->ServiceBinding,\r
222     &mDhcp4ServiceBindingTemplate,\r
223     sizeof (EFI_SERVICE_BINDING_PROTOCOL)\r
224     );\r
225   //\r
226   // Create various resources, UdpIo, Timer, and get Mac address\r
227   //\r
228   Status = gBS->CreateEvent (\r
229                   EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
230                   TPL_CALLBACK,\r
231                   DhcpOnTimerTick,\r
232                   DhcpSb,\r
233                   &DhcpSb->Timer\r
234                   );\r
235 \r
236   if (EFI_ERROR (Status)) {\r
237     goto ON_ERROR;\r
238   }\r
239 \r
240   DhcpSb->UdpIo = UdpIoCreateIo (\r
241                     Controller,\r
242                     ImageHandle,\r
243                     DhcpConfigUdpIo,\r
244                     UDP_IO_UDP4_VERSION,\r
245                     NULL\r
246                     );\r
247 \r
248   if (DhcpSb->UdpIo == NULL) {\r
249     Status = EFI_OUT_OF_RESOURCES;\r
250     goto ON_ERROR;\r
251   }\r
252 \r
253   DhcpSb->HwLen  = (UINT8) DhcpSb->UdpIo->SnpMode.HwAddressSize;\r
254   DhcpSb->HwType = DhcpSb->UdpIo->SnpMode.IfType;\r
255   CopyMem (&DhcpSb->Mac, &DhcpSb->UdpIo->SnpMode.CurrentAddress, sizeof (DhcpSb->Mac));\r
256 \r
257   *Service       = DhcpSb;\r
258   return EFI_SUCCESS;\r
259 \r
260 ON_ERROR:\r
261   Dhcp4CloseService (DhcpSb);\r
262   FreePool (DhcpSb);\r
263 \r
264   return Status;\r
265 }\r
266 \r
267 \r
268 /**\r
269   Start this driver on ControllerHandle. This service is called by the\r
270   EFI boot service ConnectController(). In order to make\r
271   drivers as small as possible, there are a few calling restrictions for\r
272   this service. ConnectController() must follow these\r
273   calling restrictions. If any other agent wishes to call Start() it\r
274   must also follow these calling restrictions.\r
275 \r
276   @param[in]  This                 Protocol instance pointer.\r
277   @param[in]  ControllerHandle     Handle of device to bind driver to\r
278   @param[in]  RemainingDevicePath  Optional parameter use to pick a specific child\r
279                                    device to start.\r
280 \r
281   @retval EFI_SUCCESS          This driver is added to ControllerHandle\r
282   @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle\r
283   @retval other                This driver does not support this device\r
284 \r
285 **/\r
286 EFI_STATUS\r
287 EFIAPI\r
288 Dhcp4DriverBindingStart (\r
289   IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
290   IN EFI_HANDLE                   ControllerHandle,\r
291   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
292   )\r
293 {\r
294   DHCP_SERVICE              *DhcpSb;\r
295   EFI_STATUS                Status;\r
296 \r
297   //\r
298   // First: test for the DHCP4 Protocol\r
299   //\r
300   Status = gBS->OpenProtocol (\r
301                   ControllerHandle,\r
302                   &gEfiDhcp4ServiceBindingProtocolGuid,\r
303                   NULL,\r
304                   This->DriverBindingHandle,\r
305                   ControllerHandle,\r
306                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
307                   );\r
308 \r
309   if (Status == EFI_SUCCESS) {\r
310     return EFI_ALREADY_STARTED;\r
311   }\r
312 \r
313   Status = Dhcp4CreateService (ControllerHandle, This->DriverBindingHandle, &DhcpSb);\r
314 \r
315   if (EFI_ERROR (Status)) {\r
316     return Status;\r
317   }\r
318   ASSERT (DhcpSb != NULL);\r
319 \r
320   //\r
321   // Start the receiving\r
322   //\r
323   Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);\r
324 \r
325   if (EFI_ERROR (Status)) {\r
326     goto ON_ERROR;\r
327   }\r
328   Status = gBS->SetTimer (DhcpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);\r
329 \r
330   if (EFI_ERROR (Status)) {\r
331     goto ON_ERROR;\r
332   }\r
333 \r
334   //\r
335   // Install the Dhcp4ServiceBinding Protocol onto ControlerHandle\r
336   //\r
337   Status = gBS->InstallMultipleProtocolInterfaces (\r
338                   &ControllerHandle,\r
339                   &gEfiDhcp4ServiceBindingProtocolGuid,\r
340                   &DhcpSb->ServiceBinding,\r
341                   NULL\r
342                   );\r
343 \r
344   if (EFI_ERROR (Status)) {\r
345     goto ON_ERROR;\r
346   }\r
347 \r
348   return Status;\r
349 \r
350 ON_ERROR:\r
351   Dhcp4CloseService (DhcpSb);\r
352   FreePool (DhcpSb);\r
353   return Status;\r
354 }\r
355 \r
356 /**\r
357   Callback function which provided by user to remove one node in NetDestroyLinkList process.\r
358   \r
359   @param[in]    Entry           The entry to be removed.\r
360   @param[in]    Context         Pointer to the callback context corresponds to the Context in NetDestroyLinkList.\r
361 \r
362   @retval EFI_SUCCESS           The entry has been removed successfully.\r
363   @retval Others                Fail to remove the entry.\r
364 \r
365 **/\r
366 EFI_STATUS\r
367 Dhcp4DestroyChildEntry (\r
368   IN LIST_ENTRY         *Entry,\r
369   IN VOID               *Context\r
370 )\r
371 {\r
372   DHCP_PROTOCOL                    *Instance;\r
373   EFI_SERVICE_BINDING_PROTOCOL     *ServiceBinding;\r
374 \r
375   if (Entry == NULL || Context == NULL) {\r
376     return EFI_INVALID_PARAMETER;\r
377   }\r
378 \r
379   Instance = NET_LIST_USER_STRUCT_S (Entry, DHCP_PROTOCOL, Link, DHCP_PROTOCOL_SIGNATURE);\r
380   ServiceBinding = (EFI_SERVICE_BINDING_PROTOCOL *) Context;\r
381 \r
382   return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);\r
383 }\r
384 \r
385 \r
386 /**\r
387   Stop this driver on ControllerHandle. This service is called by the\r
388   EFI boot service DisconnectController(). In order to\r
389   make drivers as small as possible, there are a few calling\r
390   restrictions for this service. DisconnectController()\r
391   must follow these calling restrictions. If any other agent wishes\r
392   to call Stop() it must also follow these calling restrictions.\r
393 \r
394   @param[in]  This              Protocol instance pointer.\r
395   @param[in]  ControllerHandle  Handle of device to stop driver on\r
396   @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of\r
397                                 children is zero stop the entire bus driver.\r
398   @param[in]  ChildHandleBuffer List of Child Handles to Stop.\r
399 \r
400   @retval EFI_SUCCESS       This driver is removed ControllerHandle\r
401   @retval other             This driver was not removed from this device\r
402 \r
403 **/\r
404 EFI_STATUS\r
405 EFIAPI\r
406 Dhcp4DriverBindingStop (\r
407   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
408   IN  EFI_HANDLE                   ControllerHandle,\r
409   IN  UINTN                        NumberOfChildren,\r
410   IN  EFI_HANDLE                   *ChildHandleBuffer\r
411   )\r
412 {\r
413   EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
414   DHCP_SERVICE                  *DhcpSb;\r
415   EFI_HANDLE                    NicHandle;\r
416   EFI_STATUS                    Status;\r
417   LIST_ENTRY                    *List;\r
418   UINTN                         ListLength;\r
419 \r
420   //\r
421   // DHCP driver opens UDP child, So, the ControllerHandle is the\r
422   // UDP child handle. locate the Nic handle first.\r
423   //\r
424   NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);\r
425 \r
426   if (NicHandle == NULL) {\r
427     return EFI_SUCCESS;\r
428   }\r
429 \r
430    Status = gBS->OpenProtocol (\r
431                   NicHandle,\r
432                   &gEfiDhcp4ServiceBindingProtocolGuid,\r
433                   (VOID **) &ServiceBinding,\r
434                   This->DriverBindingHandle,\r
435                   NicHandle,\r
436                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
437                   );\r
438 \r
439   if (EFI_ERROR (Status)) {\r
440     return EFI_DEVICE_ERROR;\r
441   }\r
442 \r
443   DhcpSb = DHCP_SERVICE_FROM_THIS (ServiceBinding);\r
444   if (!IsListEmpty (&DhcpSb->Children)) {\r
445     //\r
446     // Destroy all the children instances before destory the service.\r
447     //  \r
448     List = &DhcpSb->Children;\r
449     Status = NetDestroyLinkList (\r
450                List,\r
451                Dhcp4DestroyChildEntry,\r
452                ServiceBinding,\r
453                &ListLength\r
454                );\r
455     if (EFI_ERROR (Status) || ListLength != 0) {\r
456       Status = EFI_DEVICE_ERROR;\r
457     }\r
458   }\r
459 \r
460   if (NumberOfChildren == 0 && !IsListEmpty (&DhcpSb->Children)) {\r
461     Status = EFI_DEVICE_ERROR;\r
462   }\r
463 \r
464   if (NumberOfChildren == 0 && IsListEmpty (&DhcpSb->Children)) {\r
465     //\r
466     // Destroy the service itself if no child instance left.\r
467     //\r
468     DhcpSb->ServiceState = DHCP_DESTROY;\r
469 \r
470     gBS->UninstallProtocolInterface (\r
471            NicHandle,\r
472            &gEfiDhcp4ServiceBindingProtocolGuid,\r
473            ServiceBinding\r
474            );\r
475 \r
476     Dhcp4CloseService (DhcpSb);\r
477 \r
478     if (gDhcpControllerNameTable != NULL) {\r
479       FreeUnicodeStringTable (gDhcpControllerNameTable);\r
480       gDhcpControllerNameTable = NULL;\r
481     }\r
482     FreePool (DhcpSb);\r
483     \r
484     Status = EFI_SUCCESS;\r
485   }\r
486 \r
487   return Status;\r
488 }\r
489 \r
490 \r
491 /**\r
492   Initialize a new DHCP instance.\r
493 \r
494   @param  DhcpSb                 The dhcp service instance\r
495   @param  Instance               The dhcp instance to initialize\r
496 \r
497 **/\r
498 VOID\r
499 DhcpInitProtocol (\r
500   IN     DHCP_SERVICE           *DhcpSb,\r
501   IN OUT DHCP_PROTOCOL          *Instance\r
502   )\r
503 {\r
504   Instance->Signature         = DHCP_PROTOCOL_SIGNATURE;\r
505   CopyMem (&Instance->Dhcp4Protocol, &mDhcp4ProtocolTemplate, sizeof (Instance->Dhcp4Protocol));\r
506   InitializeListHead (&Instance->Link);\r
507   Instance->Handle            = NULL;\r
508   Instance->Service           = DhcpSb;\r
509   Instance->InDestroy         = FALSE;\r
510   Instance->CompletionEvent   = NULL;\r
511   Instance->RenewRebindEvent  = NULL;\r
512   Instance->Token             = NULL;\r
513   Instance->UdpIo             = NULL;\r
514   Instance->ElaspedTime       = 0;\r
515   NetbufQueInit (&Instance->ResponseQueue);\r
516 }\r
517 \r
518 \r
519 /**\r
520   Creates a child handle and installs a protocol.\r
521 \r
522   The CreateChild() function installs a protocol on ChildHandle.\r
523   If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
524   If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
525 \r
526   @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
527   @param  ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
528                       then a new handle is created. If it is a pointer to an existing UEFI handle,\r
529                       then the protocol is added to the existing UEFI handle.\r
530 \r
531   @retval EFI_SUCCES            The protocol was added to ChildHandle.\r
532   @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
533   @retval EFI_OUT_OF_RESOURCES  There are not enough resources availabe to create\r
534                                 the child\r
535   @retval other                 The child handle was not created\r
536 \r
537 **/\r
538 EFI_STATUS\r
539 EFIAPI\r
540 Dhcp4ServiceBindingCreateChild (\r
541   IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
542   IN EFI_HANDLE                    *ChildHandle\r
543   )\r
544 {\r
545   DHCP_SERVICE              *DhcpSb;\r
546   DHCP_PROTOCOL             *Instance;\r
547   EFI_STATUS                Status;\r
548   EFI_TPL                   OldTpl;\r
549   VOID                      *Udp4;\r
550 \r
551   if ((This == NULL) || (ChildHandle == NULL)) {\r
552     return EFI_INVALID_PARAMETER;\r
553   }\r
554 \r
555   Instance = AllocatePool (sizeof (*Instance));\r
556 \r
557   if (Instance == NULL) {\r
558     return EFI_OUT_OF_RESOURCES;\r
559   }\r
560 \r
561   DhcpSb = DHCP_SERVICE_FROM_THIS (This);\r
562   DhcpInitProtocol (DhcpSb, Instance);\r
563 \r
564   //\r
565   // Install DHCP4 onto ChildHandle\r
566   //\r
567   Status = gBS->InstallMultipleProtocolInterfaces (\r
568                   ChildHandle,\r
569                   &gEfiDhcp4ProtocolGuid,\r
570                   &Instance->Dhcp4Protocol,\r
571                   NULL\r
572                   );\r
573 \r
574   if (EFI_ERROR (Status)) {\r
575     FreePool (Instance);\r
576     return Status;\r
577   }\r
578 \r
579   Instance->Handle  = *ChildHandle;\r
580 \r
581   //\r
582   // Open the Udp4 protocol BY_CHILD.\r
583   //\r
584   Status = gBS->OpenProtocol (\r
585                   DhcpSb->UdpIo->UdpHandle,\r
586                   &gEfiUdp4ProtocolGuid,\r
587                   (VOID **) &Udp4,\r
588                   gDhcp4DriverBinding.DriverBindingHandle,\r
589                   Instance->Handle,\r
590                   EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
591                   );\r
592   if (EFI_ERROR (Status)) {\r
593     gBS->UninstallMultipleProtocolInterfaces (\r
594            Instance->Handle,\r
595            &gEfiDhcp4ProtocolGuid,\r
596            &Instance->Dhcp4Protocol,\r
597            NULL\r
598            );\r
599 \r
600     FreePool (Instance);\r
601     return Status;\r
602   }\r
603 \r
604   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
605 \r
606   InsertTailList (&DhcpSb->Children, &Instance->Link);\r
607   DhcpSb->NumChildren++;\r
608 \r
609   gBS->RestoreTPL (OldTpl);\r
610 \r
611   return EFI_SUCCESS;\r
612 }\r
613 \r
614 \r
615 /**\r
616   Destroys a child handle with a protocol installed on it.\r
617 \r
618   The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
619   that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
620   last protocol on ChildHandle, then ChildHandle is destroyed.\r
621 \r
622   @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
623   @param  ChildHandle Handle of the child to destroy\r
624 \r
625   @retval EFI_SUCCES            The protocol was removed from ChildHandle.\r
626   @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.\r
627   @retval EFI_INVALID_PARAMETER Child handle is NULL.\r
628   @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle\r
629                                 because its services are being used.\r
630   @retval other                 The child handle was not destroyed\r
631 \r
632 **/\r
633 EFI_STATUS\r
634 EFIAPI\r
635 Dhcp4ServiceBindingDestroyChild (\r
636   IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
637   IN EFI_HANDLE                    ChildHandle\r
638   )\r
639 {\r
640   DHCP_SERVICE              *DhcpSb;\r
641   DHCP_PROTOCOL             *Instance;\r
642   EFI_DHCP4_PROTOCOL        *Dhcp;\r
643   EFI_TPL                   OldTpl;\r
644   EFI_STATUS                Status;\r
645 \r
646   if ((This == NULL) || (ChildHandle == NULL)) {\r
647     return EFI_INVALID_PARAMETER;\r
648   }\r
649 \r
650   //\r
651   // Retrieve the private context data structures\r
652   //\r
653   Status = gBS->OpenProtocol (\r
654                   ChildHandle,\r
655                   &gEfiDhcp4ProtocolGuid,\r
656                   (VOID **) &Dhcp,\r
657                   gDhcp4DriverBinding.DriverBindingHandle,\r
658                   ChildHandle,\r
659                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
660                   );\r
661 \r
662   if (EFI_ERROR (Status)) {\r
663     return EFI_UNSUPPORTED;\r
664   }\r
665 \r
666   Instance  = DHCP_INSTANCE_FROM_THIS (Dhcp);\r
667   DhcpSb    = DHCP_SERVICE_FROM_THIS (This);\r
668 \r
669   if (Instance->Service != DhcpSb) {\r
670     return EFI_INVALID_PARAMETER;\r
671   }\r
672 \r
673   //\r
674   // A child can be destroyed more than once. For example,\r
675   // Dhcp4DriverBindingStop will destroy all of its children.\r
676   // when caller driver is being stopped, it will destroy the\r
677   // dhcp child it opens.\r
678   //\r
679   if (Instance->InDestroy) {\r
680     return EFI_SUCCESS;\r
681   }\r
682 \r
683   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
684   Instance->InDestroy = TRUE;\r
685 \r
686   //\r
687   // Close the Udp4 protocol.\r
688   //\r
689   gBS->CloseProtocol (\r
690          DhcpSb->UdpIo->UdpHandle,\r
691          &gEfiUdp4ProtocolGuid,\r
692          gDhcp4DriverBinding.DriverBindingHandle,\r
693          ChildHandle\r
694          );\r
695 \r
696   //\r
697   // Uninstall the DHCP4 protocol first to enable a top down destruction.\r
698   //\r
699   gBS->RestoreTPL (OldTpl);\r
700   Status = gBS->UninstallProtocolInterface (\r
701                   ChildHandle,\r
702                   &gEfiDhcp4ProtocolGuid,\r
703                   Dhcp\r
704                   );\r
705   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
706   if (EFI_ERROR (Status)) {\r
707     Instance->InDestroy = FALSE;\r
708 \r
709     gBS->RestoreTPL (OldTpl);\r
710     return Status;\r
711   }\r
712 \r
713   if (DhcpSb->ActiveChild == Instance) {\r
714     DhcpYieldControl (DhcpSb);\r
715   }\r
716 \r
717   RemoveEntryList (&Instance->Link);\r
718   DhcpSb->NumChildren--;\r
719 \r
720   gBS->RestoreTPL (OldTpl);\r
721 \r
722   FreePool (Instance);\r
723   return EFI_SUCCESS;\r
724 }\r