]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c
ADD EFIAPI for library function.
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbBusDxe / UsbEnumer.c
CommitLineData
e237e7ae 1/** @file\r
2\r
b4c24e2d 3Copyright (c) 2007 - 2008, Intel Corporation\r
e237e7ae 4All rights reserved. This program and the accompanying materials\r
5are licensed and made available under the terms and conditions of the BSD License\r
6which accompanies this distribution. The full text of the license may be found at\r
7http://opensource.org/licenses/bsd-license.php\r
8\r
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11\r
12 Module Name:\r
13\r
14 UsbEnumer.c\r
15\r
16 Abstract:\r
17\r
18 Usb bus enumeration support\r
19\r
20 Revision History\r
21\r
22\r
23**/\r
24\r
25#include "UsbBus.h"\r
26\r
27\r
28/**\r
29 Return the endpoint descriptor in this interface\r
30\r
31 @param UsbIf The interface to search in\r
32 @param EpAddr The address of the endpoint to return\r
33\r
34 @return The endpoint descriptor or NULL\r
35\r
36**/\r
37USB_ENDPOINT_DESC *\r
38UsbGetEndpointDesc (\r
39 IN USB_INTERFACE *UsbIf,\r
40 IN UINT8 EpAddr\r
41 )\r
42{\r
43 USB_ENDPOINT_DESC *EpDesc;\r
44 UINTN Index;\r
45\r
46 for (Index = 0; Index < UsbIf->IfSetting->Desc.NumEndpoints; Index++) {\r
47 EpDesc = UsbIf->IfSetting->Endpoints[Index];\r
48\r
49 if (EpDesc->Desc.EndpointAddress == EpAddr) {\r
50 return EpDesc;\r
51 }\r
52 }\r
53\r
54 return NULL;\r
55}\r
56\r
57\r
58/**\r
59 Free the resource used by USB interface\r
60\r
61 @param UsbIf The USB interface to free\r
62\r
63 @return None\r
64\r
65**/\r
66STATIC\r
67VOID\r
68UsbFreeInterface (\r
69 IN USB_INTERFACE *UsbIf\r
70 )\r
71{\r
72 UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);\r
73\r
74 gBS->UninstallMultipleProtocolInterfaces (\r
75 UsbIf->Handle,\r
76 &gEfiDevicePathProtocolGuid,\r
77 UsbIf->DevicePath,\r
78 &gEfiUsbIoProtocolGuid,\r
79 &UsbIf->UsbIo,\r
80 NULL\r
81 );\r
82\r
83 if (UsbIf->DevicePath != NULL) {\r
84 gBS->FreePool (UsbIf->DevicePath);\r
85 }\r
86\r
87 gBS->FreePool (UsbIf);\r
88}\r
89\r
90\r
91/**\r
92 Create an interface for the descriptor IfDesc. Each\r
93 device's configuration can have several interfaces.\r
94\r
95 @param Device The device has the interface descriptor\r
96 @param IfDesc The interface descriptor\r
97\r
98 @return The created USB interface for the descriptor, or NULL.\r
99\r
100**/\r
101STATIC\r
102USB_INTERFACE *\r
103UsbCreateInterface (\r
104 IN USB_DEVICE *Device,\r
105 IN USB_INTERFACE_DESC *IfDesc\r
106 )\r
107{\r
108 USB_DEVICE_PATH UsbNode;\r
109 USB_INTERFACE *UsbIf;\r
110 USB_INTERFACE *HubIf;\r
111 EFI_STATUS Status;\r
112\r
113 UsbIf = AllocateZeroPool (sizeof (USB_INTERFACE));\r
114\r
115 if (UsbIf == NULL) {\r
116 return NULL;\r
117 }\r
118\r
119 UsbIf->Signature = USB_INTERFACE_SIGNATURE;\r
120 UsbIf->Device = Device;\r
121 UsbIf->IfDesc = IfDesc;\r
122 UsbIf->IfSetting = IfDesc->Settings[IfDesc->ActiveIndex];\r
e61d30b0 123\r
124 CopyMem (\r
125 &(UsbIf->UsbIo),\r
126 &mUsbIoProtocol,\r
127 sizeof (EFI_USB_IO_PROTOCOL)\r
128 );\r
e237e7ae 129\r
130 //\r
131 // Install protocols for USBIO and device path\r
132 //\r
133 UsbNode.Header.Type = MESSAGING_DEVICE_PATH;\r
134 UsbNode.Header.SubType = MSG_USB_DP;\r
135 UsbNode.ParentPortNumber = Device->ParentPort;\r
136 UsbNode.InterfaceNumber = UsbIf->IfSetting->Desc.InterfaceNumber;\r
137\r
138 SetDevicePathNodeLength (&UsbNode.Header, sizeof (UsbNode));\r
139\r
140 HubIf = Device->ParentIf;\r
141 ASSERT (HubIf != NULL);\r
142\r
143 UsbIf->DevicePath = AppendDevicePathNode (HubIf->DevicePath, &UsbNode.Header);\r
144\r
145 if (UsbIf->DevicePath == NULL) {\r
d2577026 146 DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to create device path\n"));\r
e237e7ae 147\r
148 Status = EFI_OUT_OF_RESOURCES;\r
149 goto ON_ERROR;\r
150 }\r
151\r
152 Status = gBS->InstallMultipleProtocolInterfaces (\r
153 &UsbIf->Handle,\r
154 &gEfiDevicePathProtocolGuid,\r
155 UsbIf->DevicePath,\r
156 &gEfiUsbIoProtocolGuid,\r
157 &UsbIf->UsbIo,\r
158 NULL\r
159 );\r
160\r
161 if (EFI_ERROR (Status)) {\r
d2577026 162 DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to install UsbIo - %r\n", Status));\r
e237e7ae 163 goto ON_ERROR;\r
164 }\r
165\r
166 //\r
167 // Open USB Host Controller Protocol by Child\r
168 //\r
169 Status = UsbOpenHostProtoByChild (Device->Bus, UsbIf->Handle);\r
170\r
171 if (EFI_ERROR (Status)) {\r
172 gBS->UninstallMultipleProtocolInterfaces (\r
173 &UsbIf->Handle,\r
174 &gEfiDevicePathProtocolGuid,\r
175 UsbIf->DevicePath,\r
176 &gEfiUsbIoProtocolGuid,\r
177 &UsbIf->UsbIo,\r
178 NULL\r
179 );\r
180\r
d2577026 181 DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to open host for child - %r\n", Status));\r
e237e7ae 182 goto ON_ERROR;\r
183 }\r
184\r
185 return UsbIf;\r
186\r
187ON_ERROR:\r
188 if (UsbIf->DevicePath) {\r
189 gBS->FreePool (UsbIf->DevicePath);\r
190 }\r
191\r
192 gBS->FreePool (UsbIf);\r
193 return NULL;\r
194}\r
195\r
196\r
197/**\r
198 Free the resource used by this USB device\r
199\r
200 @param Device The USB device to free\r
201\r
202 @return None\r
203\r
204**/\r
205STATIC\r
206VOID\r
207UsbFreeDevice (\r
208 IN USB_DEVICE *Device\r
209 )\r
210{\r
211 if (Device->DevDesc != NULL) {\r
212 UsbFreeDevDesc (Device->DevDesc);\r
213 }\r
214\r
215 gBS->FreePool (Device);\r
216}\r
217\r
218\r
219/**\r
220 Create a device which is on the parent's ParentPort port.\r
221\r
222 @param ParentIf The parent HUB interface\r
223 @param ParentPort The port on the HUB this device is connected to\r
224\r
225 @return Created USB device\r
226\r
227**/\r
228STATIC\r
229USB_DEVICE *\r
230UsbCreateDevice (\r
231 IN USB_INTERFACE *ParentIf,\r
232 IN UINT8 ParentPort\r
233 )\r
234{\r
235 USB_DEVICE *Device;\r
236\r
237 ASSERT (ParentIf != NULL);\r
238\r
239 Device = AllocateZeroPool (sizeof (USB_DEVICE));\r
240\r
241 if (Device == NULL) {\r
242 return NULL;\r
243 }\r
244\r
245 Device->Bus = ParentIf->Device->Bus;\r
246 Device->MaxPacket0 = 8;\r
247 Device->ParentAddr = ParentIf->Device->Address;\r
248 Device->ParentIf = ParentIf;\r
249 Device->ParentPort = ParentPort;\r
250 return Device;\r
251}\r
252\r
253\r
254/**\r
255 Connect the USB interface with its driver. EFI USB bus will\r
256 create a USB interface for each seperate interface descriptor.\r
257\r
258 @param UsbIf The interface to connect driver to\r
259\r
260 @return EFI_SUCCESS : Interface is managed by some driver\r
261 @return Others : Failed to locate a driver for this interface\r
262\r
263**/\r
264STATIC\r
265EFI_STATUS\r
266UsbConnectDriver (\r
267 IN USB_INTERFACE *UsbIf\r
268 )\r
269{\r
270 EFI_STATUS Status;\r
271 EFI_TPL OldTpl;\r
272\r
273 Status = EFI_SUCCESS;\r
274\r
275 //\r
276 // Hub is maintained by the USB bus driver. Otherwise try to\r
277 // connect drivers with this interface\r
278 //\r
279 if (UsbIsHubInterface (UsbIf)) {\r
d2577026 280 DEBUG ((EFI_D_INFO, "UsbConnectDriver: found a hub device\n"));\r
e237e7ae 281 Status = mUsbHubApi.Init (UsbIf);\r
282\r
283 } else {\r
284 //\r
285 // This function is called in both UsbIoControlTransfer and\r
286 // the timer callback in hub enumeration. So, at least it is\r
287 // called at TPL_CALLBACK. Some driver sitting on USB has\r
288 // twisted TPL used. It should be no problem for us to connect\r
289 // or disconnect at CALLBACK.\r
290 //\r
ecb575d9 291 \r
292 //\r
293 // Only recursively wanted usb child device\r
294 //\r
295 if (UsbBusIsWantedUsbIO (UsbIf->Device->Bus, UsbIf)) {\r
296 OldTpl = UsbGetCurrentTpl ();\r
297 DEBUG ((EFI_D_INFO, "UsbConnectDriver: TPL before connect is %d\n", OldTpl));\r
e237e7ae 298\r
ecb575d9 299 gBS->RestoreTPL (TPL_CALLBACK);\r
e237e7ae 300\r
ecb575d9 301 Status = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE);\r
302 UsbIf->IsManaged = (BOOLEAN)!EFI_ERROR (Status);\r
e237e7ae 303\r
ecb575d9 304 DEBUG ((EFI_D_INFO, "UsbConnectDriver: TPL after connect is %d\n", UsbGetCurrentTpl()));\r
305 ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);\r
e237e7ae 306\r
ecb575d9 307 gBS->RaiseTPL (OldTpl);\r
308 }\r
e237e7ae 309 }\r
310\r
311 return Status;\r
312}\r
313\r
314\r
315/**\r
316 Select an alternate setting for the interface.\r
317 Each interface can have several mutually exclusive\r
318 settings. Only one setting is active. It will\r
319 also reset its endpoints' toggle to zero.\r
320\r
321 @param IfDesc The interface descriptor to set\r
322 @param Alternate The alternate setting number to locate\r
323\r
324 @retval EFI_NOT_FOUND There is no setting with this alternate index\r
325 @retval EFI_SUCCESS The interface is set to Alternate setting.\r
326\r
327**/\r
328EFI_STATUS\r
329UsbSelectSetting (\r
330 IN USB_INTERFACE_DESC *IfDesc,\r
331 IN UINT8 Alternate\r
332 )\r
333{\r
334 USB_INTERFACE_SETTING *Setting;\r
335 UINT8 Index;\r
336\r
337 //\r
338 // Locate the active alternate setting\r
339 //\r
340 Setting = NULL;\r
341\r
342 for (Index = 0; Index < IfDesc->NumOfSetting; Index++) {\r
343 Setting = IfDesc->Settings[Index];\r
344\r
345 if (Setting->Desc.AlternateSetting == Alternate) {\r
346 break;\r
347 }\r
348 }\r
349\r
350 if (Index == IfDesc->NumOfSetting) {\r
351 return EFI_NOT_FOUND;\r
352 }\r
353\r
354 IfDesc->ActiveIndex = Index;\r
355\r
d2577026 356 DEBUG ((EFI_D_INFO, "UsbSelectSetting: setting %d selected for interface %d\n",\r
e237e7ae 357 Alternate, Setting->Desc.InterfaceNumber));\r
358\r
359 //\r
360 // Reset the endpoint toggle to zero\r
361 //\r
362 for (Index = 0; Index < Setting->Desc.NumEndpoints; Index++) {\r
363 Setting->Endpoints[Index]->Toggle = 0;\r
364 }\r
365\r
366 return EFI_SUCCESS;\r
367}\r
368\r
369\r
370/**\r
371 Select a new configuration for the device. Each\r
372 device may support several configurations.\r
373\r
374 @param Device The device to select configuration\r
375 @param ConfigValue The index of the configuration ( != 0)\r
376\r
377 @retval EFI_NOT_FOUND There is no configuration with the index\r
378 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource\r
379 @retval EFI_SUCCESS The configuration is selected.\r
380\r
381**/\r
382EFI_STATUS\r
383UsbSelectConfig (\r
384 IN USB_DEVICE *Device,\r
385 IN UINT8 ConfigValue\r
386 )\r
387{\r
388 USB_DEVICE_DESC *DevDesc;\r
389 USB_CONFIG_DESC *ConfigDesc;\r
390 USB_INTERFACE_DESC *IfDesc;\r
391 USB_INTERFACE *UsbIf;\r
392 EFI_STATUS Status;\r
393 UINT8 Index;\r
394\r
395 //\r
396 // Locate the active config, then set the device's pointer\r
397 //\r
398 DevDesc = Device->DevDesc;\r
399 ConfigDesc = NULL;\r
400\r
401 for (Index = 0; Index < DevDesc->Desc.NumConfigurations; Index++) {\r
402 ConfigDesc = DevDesc->Configs[Index];\r
403\r
404 if (ConfigDesc->Desc.ConfigurationValue == ConfigValue) {\r
405 break;\r
406 }\r
407 }\r
408\r
409 if (Index == DevDesc->Desc.NumConfigurations) {\r
410 return EFI_NOT_FOUND;\r
411 }\r
412\r
413 Device->ActiveConfig = ConfigDesc;\r
414\r
d2577026 415 DEBUG ((EFI_D_INFO, "UsbSelectConfig: config %d selected for device %d\n",\r
e237e7ae 416 ConfigValue, Device->Address));\r
417\r
418 //\r
419 // Create interfaces for each USB interface descriptor.\r
420 //\r
421 for (Index = 0; Index < ConfigDesc->Desc.NumInterfaces; Index++) {\r
422 //\r
423 // First select the default interface setting, and reset\r
424 // the endpoint toggles to zero for its endpoints.\r
425 //\r
426 IfDesc = ConfigDesc->Interfaces[Index];\r
427 UsbSelectSetting (IfDesc, IfDesc->Settings[0]->Desc.AlternateSetting);\r
428\r
429 //\r
430 // Create a USB_INTERFACE and install USB_IO and other protocols\r
431 //\r
432 UsbIf = UsbCreateInterface (Device, ConfigDesc->Interfaces[Index]);\r
433\r
434 if (UsbIf == NULL) {\r
435 return EFI_OUT_OF_RESOURCES;\r
436 }\r
437\r
438 Device->Interfaces[Index] = UsbIf;\r
439\r
440 //\r
441 // Connect the device to drivers, if it failed, ignore\r
442 // the error. Don't let the unsupported interfaces to block\r
443 // the supported interfaces.\r
444 //\r
445 Status = UsbConnectDriver (UsbIf);\r
446\r
447 if (EFI_ERROR (Status)) {\r
d2577026 448 DEBUG ((EFI_D_ERROR, "UsbSelectConfig: failed to connect driver %r, ignored\n", Status));\r
e237e7ae 449 }\r
450 }\r
451\r
452 Device->NumOfInterface = Index;\r
453\r
454 return EFI_SUCCESS;\r
455}\r
456\r
457\r
458\r
459/**\r
460 Disconnect the USB interface with its driver.\r
461\r
462 @param UsbIf The interface to disconnect driver from\r
463\r
464 @return None\r
465\r
466**/\r
467STATIC\r
468VOID\r
469UsbDisconnectDriver (\r
470 IN USB_INTERFACE *UsbIf\r
471 )\r
472{\r
473 EFI_TPL OldTpl;\r
474\r
475 //\r
476 // Release the hub if it's a hub controller, otherwise\r
477 // disconnect the driver if it is managed by other drivers.\r
478 //\r
479 if (UsbIf->IsHub) {\r
480 UsbIf->HubApi->Release (UsbIf);\r
481\r
482 } else if (UsbIf->IsManaged) {\r
483 //\r
484 // This function is called in both UsbIoControlTransfer and\r
485 // the timer callback in hub enumeration. So, at least it is\r
486 // called at TPL_CALLBACK. Some driver sitting on USB has\r
487 // twisted TPL used. It should be no problem for us to connect\r
488 // or disconnect at CALLBACK.\r
489 //\r
490 OldTpl = UsbGetCurrentTpl ();\r
d2577026 491 DEBUG ((EFI_D_INFO, "UsbDisconnectDriver: old TPL is %d\n", OldTpl));\r
e237e7ae 492\r
493 gBS->RestoreTPL (TPL_CALLBACK);\r
494\r
495 gBS->DisconnectController (UsbIf->Handle, NULL, NULL);\r
496 UsbIf->IsManaged = FALSE;\r
497\r
d2577026 498 DEBUG (( EFI_D_INFO, "UsbDisconnectDriver: TPL after disconnect is %d\n", UsbGetCurrentTpl()));\r
e237e7ae 499 ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);\r
500\r
501 gBS->RaiseTPL (OldTpl);\r
502 }\r
503}\r
504\r
505\r
506\r
507/**\r
508 Remove the current device configuration\r
509\r
510 @param Device The USB device to remove configuration from\r
511\r
512 @return None\r
513\r
514**/\r
515VOID\r
516UsbRemoveConfig (\r
517 IN USB_DEVICE *Device\r
518 )\r
519{\r
520 USB_INTERFACE *UsbIf;\r
521 UINTN Index;\r
522\r
523 //\r
524 // Remove each interface of the device\r
525 //\r
526 for (Index = 0; Index < Device->NumOfInterface; Index++) {\r
527 UsbIf = Device->Interfaces[Index];\r
528\r
529 if (UsbIf == NULL) {\r
530 continue;\r
531 }\r
532\r
533 UsbDisconnectDriver (UsbIf);\r
534 UsbFreeInterface (UsbIf);\r
535 Device->Interfaces[Index] = NULL;\r
536 }\r
537\r
538 Device->ActiveConfig = NULL;\r
539 Device->NumOfInterface = 0;\r
540}\r
541\r
542\r
543\r
544/**\r
545 Remove the device and all its children from the bus.\r
546\r
547 @param Device The device to remove\r
548\r
549 @retval EFI_SUCCESS The device is removed\r
550\r
551**/\r
552EFI_STATUS\r
553UsbRemoveDevice (\r
554 IN USB_DEVICE *Device\r
555 )\r
556{\r
557 USB_BUS *Bus;\r
558 USB_DEVICE *Child;\r
559 EFI_STATUS Status;\r
560 UINT8 Index;\r
561\r
562 Bus = Device->Bus;\r
563\r
564 //\r
565 // Remove all the devices on its downstream ports. Search from devices[1].\r
566 // Devices[0] is the root hub.\r
567 //\r
568 for (Index = 1; Index < USB_MAX_DEVICES; Index++) {\r
569 Child = Bus->Devices[Index];\r
570\r
571 if ((Child == NULL) || (Child->ParentAddr != Device->Address)) {\r
572 continue;\r
573 }\r
574\r
575 Status = UsbRemoveDevice (Child);\r
576\r
577 if (EFI_ERROR (Status)) {\r
d2577026 578 DEBUG ((EFI_D_ERROR, "UsbRemoveDevice: failed to remove child, ignore error\n"));\r
e237e7ae 579 Bus->Devices[Index] = NULL;\r
580 }\r
581 }\r
582\r
583 UsbRemoveConfig (Device);\r
584\r
d2577026 585 DEBUG (( EFI_D_INFO, "UsbRemoveDevice: device %d removed\n", Device->Address));\r
e237e7ae 586\r
587 Bus->Devices[Device->Address] = NULL;\r
588 UsbFreeDevice (Device);\r
589\r
590 return EFI_SUCCESS;\r
591}\r
592\r
593\r
594/**\r
595 Find the child device on the hub's port\r
596\r
597 @param HubIf The hub interface\r
598 @param Port The port of the hub this child is connected to\r
599\r
600 @return The device on the hub's port, or NULL if there is none\r
601\r
602**/\r
603STATIC\r
604USB_DEVICE *\r
605UsbFindChild (\r
606 IN USB_INTERFACE *HubIf,\r
607 IN UINT8 Port\r
608 )\r
609{\r
610 USB_DEVICE *Device;\r
611 USB_BUS *Bus;\r
612 UINTN Index;\r
613\r
614 Bus = HubIf->Device->Bus;\r
615\r
616 //\r
617 // Start checking from device 1, device 0 is the root hub\r
618 //\r
619 for (Index = 1; Index < USB_MAX_DEVICES; Index++) {\r
620 Device = Bus->Devices[Index];\r
621\r
622 if ((Device != NULL) && (Device->ParentAddr == HubIf->Device->Address) &&\r
623 (Device->ParentPort == Port)) {\r
624\r
625 return Device;\r
626 }\r
627 }\r
628\r
629 return NULL;\r
630}\r
631\r
632\r
633\r
634/**\r
635 Enumerate and configure the new device on the port of this HUB interface.\r
636\r
637 @param HubIf The HUB that has the device connected\r
638 @param Port The port index of the hub (started with zero)\r
639\r
640 @retval EFI_SUCCESS The device is enumerated (added or removed)\r
641 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device\r
642 @retval Others Failed to enumerate the device\r
643\r
644**/\r
645STATIC\r
646EFI_STATUS\r
647UsbEnumerateNewDev (\r
648 IN USB_INTERFACE *HubIf,\r
649 IN UINT8 Port\r
650 )\r
651{\r
652 USB_BUS *Bus;\r
653 USB_HUB_API *HubApi;\r
654 USB_DEVICE *Child;\r
655 USB_DEVICE *Parent;\r
656 EFI_USB_PORT_STATUS PortState;\r
657 UINT8 Address;\r
658 UINT8 Config;\r
659 EFI_STATUS Status;\r
660\r
661 Address = USB_MAX_DEVICES;\r
662 Parent = HubIf->Device;\r
663 Bus = Parent->Bus;\r
664 HubApi = HubIf->HubApi;\r
41e8ff27 665 \r
666 gBS->Stall (USB_WAIT_PORT_STABLE_STALL);\r
667 \r
e237e7ae 668 //\r
669 // Hub resets the device for at least 10 milliseconds.\r
670 // Host learns device speed. If device is of low/full speed\r
671 // and the hub is a EHCI root hub, ResetPort will release\r
672 // the device to its companion UHCI and return an error.\r
673 //\r
674 Status = HubApi->ResetPort (HubIf, Port);\r
675\r
676 if (EFI_ERROR (Status)) {\r
d2577026 677 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status));\r
e237e7ae 678\r
679 return Status;\r
680 }\r
681\r
d2577026 682 DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: hub port %d is reset\n", Port));\r
e237e7ae 683\r
684 Child = UsbCreateDevice (HubIf, Port);\r
685\r
686 if (Child == NULL) {\r
687 return EFI_OUT_OF_RESOURCES;\r
688 }\r
689\r
690 //\r
691 // OK, now identify the device speed. After reset, hub\r
692 // fully knows the actual device speed.\r
693 //\r
694 Status = HubApi->GetPortStatus (HubIf, Port, &PortState);\r
695\r
696 if (EFI_ERROR (Status)) {\r
d2577026 697 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get speed of port %d\n", Port));\r
e237e7ae 698 goto ON_ERROR;\r
699 }\r
700\r
701 if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) {\r
702 Child->Speed = EFI_USB_SPEED_LOW;\r
703\r
704 } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) {\r
705 Child->Speed = EFI_USB_SPEED_HIGH;\r
706\r
707 } else {\r
708 Child->Speed = EFI_USB_SPEED_FULL;\r
709 }\r
710\r
d2577026 711 DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device is of %d speed\n", Child->Speed));\r
e237e7ae 712\r
713 if (Child->Speed != EFI_USB_SPEED_HIGH) {\r
714 //\r
715 // If the child isn't a high speed device, it is necessary to\r
b4c24e2d 716 // set the transaction translator. Port TT is 1-based.\r
717 // This is quite simple:\r
e237e7ae 718 // 1. if parent is of high speed, then parent is our translator\r
719 // 2. otherwise use parent's translator.\r
720 //\r
721 if (Parent->Speed == EFI_USB_SPEED_HIGH) {\r
722 Child->Translator.TranslatorHubAddress = Parent->Address;\r
faff3b47 723 Child->Translator.TranslatorPortNumber = (UINT8) (Port + 1);\r
e237e7ae 724\r
725 } else {\r
726 Child->Translator = Parent->Translator;\r
727 }\r
728\r
d2577026 729 DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device uses translator (%d, %d)\n",\r
e237e7ae 730 Child->Translator.TranslatorHubAddress,\r
731 Child->Translator.TranslatorPortNumber));\r
732 }\r
733\r
734 //\r
735 // After port is reset, hub establishes a signal path between\r
ac644614 736 // the device and host (DEFALUT state). Device's registers are\r
e237e7ae 737 // reset, use default address 0 (host enumerates one device at\r
738 // a time) , and ready to respond to control transfer at EP 0.\r
739 //\r
740\r
741 //\r
742 // Host sends a Get_Descriptor request to learn the max packet\r
ac644614 743 // size of default pipe (only part of the device's descriptor).\r
e237e7ae 744 //\r
745 Status = UsbGetMaxPacketSize0 (Child);\r
746\r
747 if (EFI_ERROR (Status)) {\r
d2577026 748 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status));\r
e237e7ae 749 goto ON_ERROR;\r
750 }\r
751\r
d2577026 752 DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0));\r
e237e7ae 753\r
754 //\r
755 // Host assigns an address to the device. Device completes the\r
756 // status stage with default address, then switches to new address.\r
757 // ADDRESS state. Address zero is reserved for root hub.\r
758 //\r
759 for (Address = 1; Address < USB_MAX_DEVICES; Address++) {\r
760 if (Bus->Devices[Address] == NULL) {\r
761 break;\r
762 }\r
763 }\r
764\r
765 if (Address == USB_MAX_DEVICES) {\r
d2577026 766 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: address pool is full for port %d\n", Port));\r
e237e7ae 767\r
768 Status = EFI_ACCESS_DENIED;\r
769 goto ON_ERROR;\r
770 }\r
771\r
772 Bus->Devices[Address] = Child;\r
773 Status = UsbSetAddress (Child, Address);\r
774 Child->Address = Address;\r
775\r
776 if (EFI_ERROR (Status)) {\r
d2577026 777 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set device address - %r\n", Status));\r
e237e7ae 778 goto ON_ERROR;\r
779 }\r
41e8ff27 780 \r
781 gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL);\r
e237e7ae 782\r
d2577026 783 DEBUG ((EFI_D_INFO, "UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address));\r
e237e7ae 784\r
785 //\r
ac644614 786 // Host learns about the device's abilities by requesting device's\r
e237e7ae 787 // entire descriptions.\r
788 //\r
789 Status = UsbBuildDescTable (Child);\r
790\r
791 if (EFI_ERROR (Status)) {\r
d2577026 792 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status));\r
e237e7ae 793 goto ON_ERROR;\r
794 }\r
795\r
796 //\r
797 // Select a default configuration: UEFI must set the configuration\r
798 // before the driver can connect to the device.\r
799 //\r
800 Config = Child->DevDesc->Configs[0]->Desc.ConfigurationValue;\r
801 Status = UsbSetConfig (Child, Config);\r
802\r
803 if (EFI_ERROR (Status)) {\r
d2577026 804 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status));\r
e237e7ae 805 goto ON_ERROR;\r
806 }\r
807\r
d2577026 808 DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address));\r
e237e7ae 809\r
810 //\r
811 // Host assigns and loads a device driver.\r
812 //\r
813 Status = UsbSelectConfig (Child, Config);\r
814\r
815 if (EFI_ERROR (Status)) {\r
d2577026 816 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to create interfaces - %r\n", Status));\r
e237e7ae 817 goto ON_ERROR;\r
818 }\r
819\r
820 return EFI_SUCCESS;\r
821\r
822ON_ERROR:\r
823 if (Address != USB_MAX_DEVICES) {\r
824 Bus->Devices[Address] = NULL;\r
825 }\r
826\r
827 if (Child != NULL) {\r
828 UsbFreeDevice (Child);\r
829 }\r
830\r
831 return Status;\r
832}\r
833\r
834\r
835\r
836/**\r
837 Process the events on the port.\r
838\r
839 @param HubIf The HUB that has the device connected\r
840 @param Port The port index of the hub (started with zero)\r
841\r
842 @retval EFI_SUCCESS The device is enumerated (added or removed)\r
843 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device\r
844 @retval Others Failed to enumerate the device\r
845\r
846**/\r
847STATIC\r
848EFI_STATUS\r
849UsbEnumeratePort (\r
850 IN USB_INTERFACE *HubIf,\r
851 IN UINT8 Port\r
852 )\r
853{\r
854 USB_HUB_API *HubApi;\r
855 USB_DEVICE *Child;\r
856 EFI_USB_PORT_STATUS PortState;\r
857 EFI_STATUS Status;\r
858\r
859 Child = NULL;\r
860 HubApi = HubIf->HubApi;\r
861\r
862 //\r
863 // Host learns of the new device by polling the hub for port changes.\r
864 //\r
865 Status = HubApi->GetPortStatus (HubIf, Port, &PortState);\r
866\r
867 if (EFI_ERROR (Status)) {\r
d2577026 868 DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: failed to get state of port %d\n", Port));\r
e237e7ae 869 return Status;\r
870 }\r
871\r
872 if (PortState.PortChangeStatus == 0) {\r
873 return EFI_SUCCESS;\r
874 }\r
875\r
d2577026 876 DEBUG (( EFI_D_INFO, "UsbEnumeratePort: port %d state - %x, change - %x\n",\r
e237e7ae 877 Port, PortState.PortStatus, PortState.PortChangeStatus));\r
878\r
879 //\r
880 // This driver only process two kinds of events now: over current and\r
881 // connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET.\r
882 // ENABLE/RESET is used to reset port. SUSPEND isn't supported.\r
883 //\r
50fa1b3a 884 \r
885 if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) { \r
e237e7ae 886\r
50fa1b3a 887 if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) {\r
888 //\r
41e8ff27 889 // Case1:\r
890 // Both OverCurrent and OverCurrentChange set, means over current occurs, \r
891 // which probably is caused by short circuit. It has to wait system hardware\r
892 // to perform recovery.\r
50fa1b3a 893 //\r
d2577026 894 DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: Critical Over Current\n", Port));\r
50fa1b3a 895 return EFI_DEVICE_ERROR;\r
896 \r
41e8ff27 897 } \r
898 //\r
899 // Case2:\r
900 // Only OverCurrentChange set, means system has been recoveried from \r
901 // over current. As a result, all ports are nearly power-off, so\r
902 // it's necessary to detach and enumerate all ports again. \r
903 //\r
904 DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 2.0 device Recovery Over Current\n", Port)); \r
50fa1b3a 905 }\r
e237e7ae 906\r
50fa1b3a 907 if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) { \r
e237e7ae 908 //\r
41e8ff27 909 // Case3:\r
910 // 1.1 roothub port reg doesn't reflect over-current state, while its counterpart\r
911 // on 2.0 roothub does. When over-current has influence on 1.1 device, the port \r
912 // would be disabled, so it's also necessary to detach and enumerate again.\r
e237e7ae 913 //\r
d2577026 914 DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 1.1 device Recovery Over Current\n", Port));\r
50fa1b3a 915 }\r
916 \r
917 if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) {\r
918 //\r
41e8ff27 919 // Case4:\r
920 // Device connected or disconnected normally. \r
50fa1b3a 921 //\r
41e8ff27 922 DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: Device Connect/Discount Normally\n", Port));\r
50fa1b3a 923 }\r
e237e7ae 924\r
50fa1b3a 925 // \r
41e8ff27 926 // Following as the above cases, it's safety to remove and create again.\r
50fa1b3a 927 //\r
928 Child = UsbFindChild (HubIf, Port);\r
929 \r
930 if (Child != NULL) {\r
d2577026 931 DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device at port %d removed from system\n", Port));\r
50fa1b3a 932 UsbRemoveDevice (Child);\r
e237e7ae 933 }\r
50fa1b3a 934 \r
935 if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) {\r
936 //\r
937 // Now, new device connected, enumerate and configure the device \r
938 //\r
d2577026 939 DEBUG (( EFI_D_INFO, "UsbEnumeratePort: new device connected at port %d\n", Port));\r
50fa1b3a 940 Status = UsbEnumerateNewDev (HubIf, Port);\r
941 \r
942 } else {\r
d2577026 943 DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device disconnected event on port %d\n", Port));\r
50fa1b3a 944 }\r
945 \r
e237e7ae 946 HubApi->ClearPortChange (HubIf, Port);\r
947 return Status;\r
948}\r
949\r
950\r
951/**\r
952 Enumerate all the changed hub ports\r
953\r
954 @param Event The event that is triggered\r
955 @param Context The context to the event\r
956\r
957 @return None\r
958\r
959**/\r
960VOID\r
961UsbHubEnumeration (\r
962 IN EFI_EVENT Event,\r
963 IN VOID *Context\r
964 )\r
965{\r
966 USB_INTERFACE *HubIf;\r
967 UINT8 Byte;\r
968 UINT8 Bit;\r
969 UINT8 Index;\r
970\r
971 ASSERT (Context);\r
972\r
973 HubIf = (USB_INTERFACE *) Context;\r
974\r
975 if (HubIf->ChangeMap == NULL) {\r
976 return ;\r
977 }\r
978\r
979 //\r
980 // HUB starts its port index with 1.\r
981 //\r
982 Byte = 0;\r
983 Bit = 1;\r
984\r
985 for (Index = 0; Index < HubIf->NumOfPort; Index++) {\r
986 if (USB_BIT_IS_SET (HubIf->ChangeMap[Byte], USB_BIT (Bit))) {\r
987 UsbEnumeratePort (HubIf, Index);\r
988 }\r
989\r
990 USB_NEXT_BIT (Byte, Bit);\r
991 }\r
992\r
993 UsbHubAckHubStatus (HubIf->Device);\r
994\r
995 gBS->FreePool (HubIf->ChangeMap);\r
996 HubIf->ChangeMap = NULL;\r
997 return ;\r
998}\r
999\r
1000\r
1001/**\r
1002 Enumerate all the changed hub ports\r
1003\r
1004 @param Event The event that is triggered\r
1005 @param Context The context to the event\r
1006\r
1007 @return None\r
1008\r
1009**/\r
1010VOID\r
eb1f5ab3 1011EFIAPI\r
e237e7ae 1012UsbRootHubEnumeration (\r
1013 IN EFI_EVENT Event,\r
1014 IN VOID *Context\r
1015 )\r
1016{\r
1017 USB_INTERFACE *RootHub;\r
1018 UINT8 Index;\r
1019\r
1020 RootHub = (USB_INTERFACE *) Context;\r
1021\r
1022 for (Index = 0; Index < RootHub->NumOfPort; Index++) {\r
1023 UsbEnumeratePort (RootHub, Index);\r
1024 }\r
1025}\r