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