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