9253df2b3f08eb1ddf1e2473f5cdc008692da70e
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Dhcp4Dxe / Dhcp4Driver.c
1 /** @file\r
2 \r
3 Copyright (c) 2006 - 2015, 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 EFIAPI\r
368 Dhcp4DestroyChildEntry (\r
369   IN LIST_ENTRY         *Entry,\r
370   IN VOID               *Context\r
371   )\r
372 {\r
373   DHCP_PROTOCOL                    *Instance;\r
374   EFI_SERVICE_BINDING_PROTOCOL     *ServiceBinding;\r
375 \r
376   if (Entry == NULL || Context == NULL) {\r
377     return EFI_INVALID_PARAMETER;\r
378   }\r
379 \r
380   Instance = NET_LIST_USER_STRUCT_S (Entry, DHCP_PROTOCOL, Link, DHCP_PROTOCOL_SIGNATURE);\r
381   ServiceBinding = (EFI_SERVICE_BINDING_PROTOCOL *) Context;\r
382 \r
383   return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);\r
384 }\r
385 \r
386 \r
387 /**\r
388   Stop this driver on ControllerHandle. This service is called by the\r
389   EFI boot service DisconnectController(). In order to\r
390   make drivers as small as possible, there are a few calling\r
391   restrictions for this service. DisconnectController()\r
392   must follow these calling restrictions. If any other agent wishes\r
393   to call Stop() it must also follow these calling restrictions.\r
394 \r
395   @param[in]  This              Protocol instance pointer.\r
396   @param[in]  ControllerHandle  Handle of device to stop driver on\r
397   @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of\r
398                                 children is zero stop the entire bus driver.\r
399   @param[in]  ChildHandleBuffer List of Child Handles to Stop.\r
400 \r
401   @retval EFI_SUCCESS       This driver is removed ControllerHandle\r
402   @retval other             This driver was not removed from this device\r
403 \r
404 **/\r
405 EFI_STATUS\r
406 EFIAPI\r
407 Dhcp4DriverBindingStop (\r
408   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
409   IN  EFI_HANDLE                   ControllerHandle,\r
410   IN  UINTN                        NumberOfChildren,\r
411   IN  EFI_HANDLE                   *ChildHandleBuffer\r
412   )\r
413 {\r
414   EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
415   DHCP_SERVICE                  *DhcpSb;\r
416   EFI_HANDLE                    NicHandle;\r
417   EFI_STATUS                    Status;\r
418   LIST_ENTRY                    *List;\r
419   UINTN                         ListLength;\r
420 \r
421   //\r
422   // DHCP driver opens UDP child, So, the ControllerHandle is the\r
423   // UDP child handle. locate the Nic handle first.\r
424   //\r
425   NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);\r
426 \r
427   if (NicHandle == NULL) {\r
428     return EFI_SUCCESS;\r
429   }\r
430 \r
431    Status = gBS->OpenProtocol (\r
432                   NicHandle,\r
433                   &gEfiDhcp4ServiceBindingProtocolGuid,\r
434                   (VOID **) &ServiceBinding,\r
435                   This->DriverBindingHandle,\r
436                   NicHandle,\r
437                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
438                   );\r
439 \r
440   if (EFI_ERROR (Status)) {\r
441     return EFI_DEVICE_ERROR;\r
442   }\r
443 \r
444   DhcpSb = DHCP_SERVICE_FROM_THIS (ServiceBinding);\r
445   if (!IsListEmpty (&DhcpSb->Children)) {\r
446     //\r
447     // Destroy all the children instances before destory the service.\r
448     //  \r
449     List = &DhcpSb->Children;\r
450     Status = NetDestroyLinkList (\r
451                List,\r
452                Dhcp4DestroyChildEntry,\r
453                ServiceBinding,\r
454                &ListLength\r
455                );\r
456     if (EFI_ERROR (Status) || ListLength != 0) {\r
457       Status = EFI_DEVICE_ERROR;\r
458     }\r
459   }\r
460 \r
461   if (NumberOfChildren == 0 && !IsListEmpty (&DhcpSb->Children)) {\r
462     Status = EFI_DEVICE_ERROR;\r
463   }\r
464 \r
465   if (NumberOfChildren == 0 && IsListEmpty (&DhcpSb->Children)) {\r
466     //\r
467     // Destroy the service itself if no child instance left.\r
468     //\r
469     DhcpSb->ServiceState = DHCP_DESTROY;\r
470 \r
471     gBS->UninstallProtocolInterface (\r
472            NicHandle,\r
473            &gEfiDhcp4ServiceBindingProtocolGuid,\r
474            ServiceBinding\r
475            );\r
476 \r
477     Dhcp4CloseService (DhcpSb);\r
478 \r
479     if (gDhcpControllerNameTable != NULL) {\r
480       FreeUnicodeStringTable (gDhcpControllerNameTable);\r
481       gDhcpControllerNameTable = NULL;\r
482     }\r
483     FreePool (DhcpSb);\r
484     \r
485     Status = EFI_SUCCESS;\r
486   }\r
487 \r
488   return Status;\r
489 }\r
490 \r
491 \r
492 /**\r
493   Initialize a new DHCP instance.\r
494 \r
495   @param  DhcpSb                 The dhcp service instance\r
496   @param  Instance               The dhcp instance to initialize\r
497 \r
498 **/\r
499 VOID\r
500 DhcpInitProtocol (\r
501   IN     DHCP_SERVICE           *DhcpSb,\r
502   IN OUT DHCP_PROTOCOL          *Instance\r
503   )\r
504 {\r
505   Instance->Signature         = DHCP_PROTOCOL_SIGNATURE;\r
506   CopyMem (&Instance->Dhcp4Protocol, &mDhcp4ProtocolTemplate, sizeof (Instance->Dhcp4Protocol));\r
507   InitializeListHead (&Instance->Link);\r
508   Instance->Handle            = NULL;\r
509   Instance->Service           = DhcpSb;\r
510   Instance->InDestroy         = FALSE;\r
511   Instance->CompletionEvent   = NULL;\r
512   Instance->RenewRebindEvent  = NULL;\r
513   Instance->Token             = NULL;\r
514   Instance->UdpIo             = NULL;\r
515   Instance->ElaspedTime       = 0;\r
516   NetbufQueInit (&Instance->ResponseQueue);\r
517 }\r
518 \r
519 \r
520 /**\r
521   Creates a child handle and installs a protocol.\r
522 \r
523   The CreateChild() function installs a protocol on ChildHandle.\r
524   If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
525   If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
526 \r
527   @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
528   @param  ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
529                       then a new handle is created. If it is a pointer to an existing UEFI handle,\r
530                       then the protocol is added to the existing UEFI handle.\r
531 \r
532   @retval EFI_SUCCES            The protocol was added to ChildHandle.\r
533   @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
534   @retval EFI_OUT_OF_RESOURCES  There are not enough resources availabe to create\r
535                                 the child\r
536   @retval other                 The child handle was not created\r
537 \r
538 **/\r
539 EFI_STATUS\r
540 EFIAPI\r
541 Dhcp4ServiceBindingCreateChild (\r
542   IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
543   IN EFI_HANDLE                    *ChildHandle\r
544   )\r
545 {\r
546   DHCP_SERVICE              *DhcpSb;\r
547   DHCP_PROTOCOL             *Instance;\r
548   EFI_STATUS                Status;\r
549   EFI_TPL                   OldTpl;\r
550   VOID                      *Udp4;\r
551 \r
552   if ((This == NULL) || (ChildHandle == NULL)) {\r
553     return EFI_INVALID_PARAMETER;\r
554   }\r
555 \r
556   Instance = AllocatePool (sizeof (*Instance));\r
557 \r
558   if (Instance == NULL) {\r
559     return EFI_OUT_OF_RESOURCES;\r
560   }\r
561 \r
562   DhcpSb = DHCP_SERVICE_FROM_THIS (This);\r
563   DhcpInitProtocol (DhcpSb, Instance);\r
564 \r
565   //\r
566   // Install DHCP4 onto ChildHandle\r
567   //\r
568   Status = gBS->InstallMultipleProtocolInterfaces (\r
569                   ChildHandle,\r
570                   &gEfiDhcp4ProtocolGuid,\r
571                   &Instance->Dhcp4Protocol,\r
572                   NULL\r
573                   );\r
574 \r
575   if (EFI_ERROR (Status)) {\r
576     FreePool (Instance);\r
577     return Status;\r
578   }\r
579 \r
580   Instance->Handle  = *ChildHandle;\r
581 \r
582   //\r
583   // Open the Udp4 protocol BY_CHILD.\r
584   //\r
585   Status = gBS->OpenProtocol (\r
586                   DhcpSb->UdpIo->UdpHandle,\r
587                   &gEfiUdp4ProtocolGuid,\r
588                   (VOID **) &Udp4,\r
589                   gDhcp4DriverBinding.DriverBindingHandle,\r
590                   Instance->Handle,\r
591                   EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
592                   );\r
593   if (EFI_ERROR (Status)) {\r
594     gBS->UninstallMultipleProtocolInterfaces (\r
595            Instance->Handle,\r
596            &gEfiDhcp4ProtocolGuid,\r
597            &Instance->Dhcp4Protocol,\r
598            NULL\r
599            );\r
600 \r
601     FreePool (Instance);\r
602     return Status;\r
603   }\r
604 \r
605   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
606 \r
607   InsertTailList (&DhcpSb->Children, &Instance->Link);\r
608   DhcpSb->NumChildren++;\r
609 \r
610   gBS->RestoreTPL (OldTpl);\r
611 \r
612   return EFI_SUCCESS;\r
613 }\r
614 \r
615 \r
616 /**\r
617   Destroys a child handle with a protocol installed on it.\r
618 \r
619   The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
620   that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
621   last protocol on ChildHandle, then ChildHandle is destroyed.\r
622 \r
623   @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
624   @param  ChildHandle Handle of the child to destroy\r
625 \r
626   @retval EFI_SUCCES            The protocol was removed from ChildHandle.\r
627   @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.\r
628   @retval EFI_INVALID_PARAMETER Child handle is NULL.\r
629   @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle\r
630                                 because its services are being used.\r
631   @retval other                 The child handle was not destroyed\r
632 \r
633 **/\r
634 EFI_STATUS\r
635 EFIAPI\r
636 Dhcp4ServiceBindingDestroyChild (\r
637   IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
638   IN EFI_HANDLE                    ChildHandle\r
639   )\r
640 {\r
641   DHCP_SERVICE              *DhcpSb;\r
642   DHCP_PROTOCOL             *Instance;\r
643   EFI_DHCP4_PROTOCOL        *Dhcp;\r
644   EFI_TPL                   OldTpl;\r
645   EFI_STATUS                Status;\r
646 \r
647   if ((This == NULL) || (ChildHandle == NULL)) {\r
648     return EFI_INVALID_PARAMETER;\r
649   }\r
650 \r
651   //\r
652   // Retrieve the private context data structures\r
653   //\r
654   Status = gBS->OpenProtocol (\r
655                   ChildHandle,\r
656                   &gEfiDhcp4ProtocolGuid,\r
657                   (VOID **) &Dhcp,\r
658                   gDhcp4DriverBinding.DriverBindingHandle,\r
659                   ChildHandle,\r
660                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
661                   );\r
662 \r
663   if (EFI_ERROR (Status)) {\r
664     return EFI_UNSUPPORTED;\r
665   }\r
666 \r
667   Instance  = DHCP_INSTANCE_FROM_THIS (Dhcp);\r
668   DhcpSb    = DHCP_SERVICE_FROM_THIS (This);\r
669 \r
670   if (Instance->Service != DhcpSb) {\r
671     return EFI_INVALID_PARAMETER;\r
672   }\r
673 \r
674   //\r
675   // A child can be destroyed more than once. For example,\r
676   // Dhcp4DriverBindingStop will destroy all of its children.\r
677   // when caller driver is being stopped, it will destroy the\r
678   // dhcp child it opens.\r
679   //\r
680   if (Instance->InDestroy) {\r
681     return EFI_SUCCESS;\r
682   }\r
683 \r
684   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
685   Instance->InDestroy = TRUE;\r
686 \r
687   //\r
688   // Close the Udp4 protocol.\r
689   //\r
690   gBS->CloseProtocol (\r
691          DhcpSb->UdpIo->UdpHandle,\r
692          &gEfiUdp4ProtocolGuid,\r
693          gDhcp4DriverBinding.DriverBindingHandle,\r
694          ChildHandle\r
695          );\r
696 \r
697   //\r
698   // Uninstall the DHCP4 protocol first to enable a top down destruction.\r
699   //\r
700   gBS->RestoreTPL (OldTpl);\r
701   Status = gBS->UninstallProtocolInterface (\r
702                   ChildHandle,\r
703                   &gEfiDhcp4ProtocolGuid,\r
704                   Dhcp\r
705                   );\r
706   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
707   if (EFI_ERROR (Status)) {\r
708     Instance->InDestroy = FALSE;\r
709 \r
710     gBS->RestoreTPL (OldTpl);\r
711     return Status;\r
712   }\r
713 \r
714   if (DhcpSb->ActiveChild == Instance) {\r
715     DhcpYieldControl (DhcpSb);\r
716   }\r
717 \r
718   RemoveEntryList (&Instance->Link);\r
719   DhcpSb->NumChildren--;\r
720 \r
721   if (Instance->UdpIo != NULL) {\r
722     UdpIoCleanIo (Instance->UdpIo);\r
723     gBS->CloseProtocol (\r
724            Instance->UdpIo->UdpHandle,\r
725            &gEfiUdp4ProtocolGuid,\r
726            Instance->Service->Image,\r
727            Instance->Handle\r
728            );\r
729     UdpIoFreeIo (Instance->UdpIo);\r
730     Instance->UdpIo = NULL;\r
731     Instance->Token = NULL;\r
732   }\r
733 \r
734   gBS->RestoreTPL (OldTpl);\r
735 \r
736   FreePool (Instance);\r
737   return EFI_SUCCESS;\r
738 }\r