8d527e061bf1640583a2201a896ec393b532aedd
[people/mcb30/edk2.git] / edk2 / MdeModulePkg / Bus / Usb / UsbBusDxe / UsbEnumer.c
1 /** @file\r
2 \r
3 Copyright (c) 2007, Intel Corporation\r
4 All rights reserved. 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   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
37 USB_ENDPOINT_DESC *\r
38 UsbGetEndpointDesc (\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
66 STATIC\r
67 VOID\r
68 UsbFreeInterface (\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
101 STATIC\r
102 USB_INTERFACE *\r
103 UsbCreateInterface (\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
123 \r
124   CopyMem (\r
125     &(UsbIf->UsbIo),\r
126     &mUsbIoProtocol,\r
127     sizeof (EFI_USB_IO_PROTOCOL)\r
128     );\r
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
146     DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to create device path\n"));\r
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
162     DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to install UsbIo - %r\n", Status));\r
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
181     DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to open host for child - %r\n", Status));\r
182     goto ON_ERROR;\r
183   }\r
184 \r
185   return UsbIf;\r
186 \r
187 ON_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
205 STATIC\r
206 VOID\r
207 UsbFreeDevice (\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
228 STATIC\r
229 USB_DEVICE *\r
230 UsbCreateDevice (\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
264 STATIC\r
265 EFI_STATUS\r
266 UsbConnectDriver (\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
280     DEBUG ((EFI_D_INFO, "UsbConnectDriver: found a hub device\n"));\r
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
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
298 \r
299       gBS->RestoreTPL (TPL_CALLBACK);\r
300 \r
301       Status            = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE);\r
302       UsbIf->IsManaged  = (BOOLEAN)!EFI_ERROR (Status);\r
303 \r
304       DEBUG ((EFI_D_INFO, "UsbConnectDriver: TPL after connect is %d\n", UsbGetCurrentTpl()));\r
305       ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);\r
306 \r
307       gBS->RaiseTPL (OldTpl);\r
308     }\r
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
328 EFI_STATUS\r
329 UsbSelectSetting (\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
356   DEBUG ((EFI_D_INFO, "UsbSelectSetting: setting %d selected for interface %d\n",\r
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
382 EFI_STATUS\r
383 UsbSelectConfig (\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
415   DEBUG ((EFI_D_INFO, "UsbSelectConfig: config %d selected for device %d\n",\r
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
448       DEBUG ((EFI_D_ERROR, "UsbSelectConfig: failed to connect driver %r, ignored\n", Status));\r
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
467 STATIC\r
468 VOID\r
469 UsbDisconnectDriver (\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
491     DEBUG ((EFI_D_INFO, "UsbDisconnectDriver: old TPL is %d\n", OldTpl));\r
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
498     DEBUG (( EFI_D_INFO, "UsbDisconnectDriver: TPL after disconnect is %d\n", UsbGetCurrentTpl()));\r
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
515 VOID\r
516 UsbRemoveConfig (\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
552 EFI_STATUS\r
553 UsbRemoveDevice (\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
578       DEBUG ((EFI_D_ERROR, "UsbRemoveDevice: failed to remove child, ignore error\n"));\r
579       Bus->Devices[Index] = NULL;\r
580     }\r
581   }\r
582 \r
583   UsbRemoveConfig (Device);\r
584 \r
585   DEBUG (( EFI_D_INFO, "UsbRemoveDevice: device %d removed\n", Device->Address));\r
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
603 STATIC\r
604 USB_DEVICE *\r
605 UsbFindChild (\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
645 STATIC\r
646 EFI_STATUS\r
647 UsbEnumerateNewDev (\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
665   \r
666   gBS->Stall (USB_WAIT_PORT_STABLE_STALL);\r
667   \r
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
677     DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status));\r
678 \r
679     return Status;\r
680   }\r
681 \r
682   DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: hub port %d is reset\n", Port));\r
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
697     DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get speed of port %d\n", Port));\r
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
711   DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device is of %d speed\n", Child->Speed));\r
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
716     // set the transaction translator. This is quite simple:\r
717     //  1. if parent is of high speed, then parent is our translator\r
718     //  2. otherwise use parent's translator.\r
719     //\r
720     if (Parent->Speed == EFI_USB_SPEED_HIGH) {\r
721       Child->Translator.TranslatorHubAddress  = Parent->Address;\r
722       Child->Translator.TranslatorPortNumber  = Port;\r
723 \r
724     } else {\r
725       Child->Translator = Parent->Translator;\r
726     }\r
727 \r
728     DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device uses translator (%d, %d)\n",\r
729                 Child->Translator.TranslatorHubAddress,\r
730                 Child->Translator.TranslatorPortNumber));\r
731   }\r
732 \r
733   //\r
734   // After port is reset, hub establishes a signal path between\r
735   // the device and host (DEFALUT state). Device's registers are\r
736   // reset, use default address 0 (host enumerates one device at\r
737   // a time) , and ready to respond to control transfer at EP 0.\r
738   //\r
739 \r
740   //\r
741   // Host sends a Get_Descriptor request to learn the max packet\r
742   // size of default pipe (only part of the device's descriptor).\r
743   //\r
744   Status = UsbGetMaxPacketSize0 (Child);\r
745 \r
746   if (EFI_ERROR (Status)) {\r
747     DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status));\r
748     goto ON_ERROR;\r
749   }\r
750 \r
751   DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0));\r
752 \r
753   //\r
754   // Host assigns an address to the device. Device completes the\r
755   // status stage with default address, then switches to new address.\r
756   // ADDRESS state. Address zero is reserved for root hub.\r
757   //\r
758   for (Address = 1; Address < USB_MAX_DEVICES; Address++) {\r
759     if (Bus->Devices[Address] == NULL) {\r
760       break;\r
761     }\r
762   }\r
763 \r
764   if (Address == USB_MAX_DEVICES) {\r
765     DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: address pool is full for port %d\n", Port));\r
766 \r
767     Status = EFI_ACCESS_DENIED;\r
768     goto ON_ERROR;\r
769   }\r
770 \r
771   Bus->Devices[Address] = Child;\r
772   Status                = UsbSetAddress (Child, Address);\r
773   Child->Address        = Address;\r
774 \r
775   if (EFI_ERROR (Status)) {\r
776     DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set device address - %r\n", Status));\r
777     goto ON_ERROR;\r
778   }\r
779   \r
780   gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL);\r
781 \r
782   DEBUG ((EFI_D_INFO, "UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address));\r
783 \r
784   //\r
785   // Host learns about the device's abilities by requesting device's\r
786   // entire descriptions.\r
787   //\r
788   Status = UsbBuildDescTable (Child);\r
789 \r
790   if (EFI_ERROR (Status)) {\r
791     DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status));\r
792     goto ON_ERROR;\r
793   }\r
794 \r
795   //\r
796   // Select a default configuration: UEFI must set the configuration\r
797   // before the driver can connect to the device.\r
798   //\r
799   Config = Child->DevDesc->Configs[0]->Desc.ConfigurationValue;\r
800   Status = UsbSetConfig (Child, Config);\r
801 \r
802   if (EFI_ERROR (Status)) {\r
803     DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status));\r
804     goto ON_ERROR;\r
805   }\r
806 \r
807   DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address));\r
808 \r
809   //\r
810   // Host assigns and loads a device driver.\r
811   //\r
812   Status = UsbSelectConfig (Child, Config);\r
813 \r
814   if (EFI_ERROR (Status)) {\r
815     DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to create interfaces - %r\n", Status));\r
816     goto ON_ERROR;\r
817   }\r
818 \r
819   return EFI_SUCCESS;\r
820 \r
821 ON_ERROR:\r
822   if (Address != USB_MAX_DEVICES) {\r
823     Bus->Devices[Address] = NULL;\r
824   }\r
825 \r
826   if (Child != NULL) {\r
827     UsbFreeDevice (Child);\r
828   }\r
829 \r
830   return Status;\r
831 }\r
832 \r
833 \r
834 \r
835 /**\r
836   Process the events on the port.\r
837 \r
838   @param  HubIf                 The HUB that has the device connected\r
839   @param  Port                  The port index of the hub (started with zero)\r
840 \r
841   @retval EFI_SUCCESS           The device is enumerated (added or removed)\r
842   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource for the device\r
843   @retval Others                Failed to enumerate the device\r
844 \r
845 **/\r
846 STATIC\r
847 EFI_STATUS\r
848 UsbEnumeratePort (\r
849   IN USB_INTERFACE        *HubIf,\r
850   IN UINT8                Port\r
851   )\r
852 {\r
853   USB_HUB_API             *HubApi;\r
854   USB_DEVICE              *Child;\r
855   EFI_USB_PORT_STATUS     PortState;\r
856   EFI_STATUS              Status;\r
857 \r
858   Child   = NULL;\r
859   HubApi  = HubIf->HubApi;\r
860 \r
861   //\r
862   // Host learns of the new device by polling the hub for port changes.\r
863   //\r
864   Status = HubApi->GetPortStatus (HubIf, Port, &PortState);\r
865 \r
866   if (EFI_ERROR (Status)) {\r
867     DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: failed to get state of port %d\n", Port));\r
868     return Status;\r
869   }\r
870 \r
871   if (PortState.PortChangeStatus == 0) {\r
872     return EFI_SUCCESS;\r
873   }\r
874 \r
875   DEBUG (( EFI_D_INFO, "UsbEnumeratePort: port %d state - %x, change - %x\n",\r
876               Port, PortState.PortStatus, PortState.PortChangeStatus));\r
877 \r
878   //\r
879   // This driver only process two kinds of events now: over current and\r
880   // connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET.\r
881   // ENABLE/RESET is used to reset port. SUSPEND isn't supported.\r
882   //\r
883   \r
884   if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) {     \r
885 \r
886     if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) {\r
887       //\r
888       // Case1:\r
889       //   Both OverCurrent and OverCurrentChange set, means over current occurs, \r
890       //   which probably is caused by short circuit. It has to wait system hardware\r
891       //   to perform recovery.\r
892       //\r
893       DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: Critical Over Current\n", Port));\r
894       return EFI_DEVICE_ERROR;\r
895       \r
896     } \r
897     //\r
898     // Case2:\r
899     //   Only OverCurrentChange set, means system has been recoveried from \r
900     //   over current. As a result, all ports are nearly power-off, so\r
901     //   it's necessary to detach and enumerate all ports again. \r
902     //\r
903     DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 2.0 device Recovery Over Current\n", Port)); \r
904   }\r
905 \r
906   if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) {  \r
907     //\r
908     // Case3:\r
909     //   1.1 roothub port reg doesn't reflect over-current state, while its counterpart\r
910     //   on 2.0 roothub does. When over-current has influence on 1.1 device, the port \r
911     //   would be disabled, so it's also necessary to detach and enumerate again.\r
912     //\r
913     DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 1.1 device Recovery Over Current\n", Port));\r
914   }\r
915   \r
916   if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) {\r
917     //\r
918     // Case4:\r
919     //   Device connected or disconnected normally. \r
920     //\r
921     DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: Device Connect/Discount Normally\n", Port));\r
922   }\r
923 \r
924   // \r
925   // Following as the above cases, it's safety to remove and create again.\r
926   //\r
927   Child = UsbFindChild (HubIf, Port);\r
928   \r
929   if (Child != NULL) {\r
930     DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device at port %d removed from system\n", Port));\r
931     UsbRemoveDevice (Child);\r
932   }\r
933   \r
934   if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) {\r
935     //\r
936     // Now, new device connected, enumerate and configure the device \r
937     //\r
938     DEBUG (( EFI_D_INFO, "UsbEnumeratePort: new device connected at port %d\n", Port));\r
939     Status = UsbEnumerateNewDev (HubIf, Port);\r
940   \r
941   } else {\r
942     DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device disconnected event on port %d\n", Port));\r
943   }\r
944   \r
945   HubApi->ClearPortChange (HubIf, Port);\r
946   return Status;\r
947 }\r
948 \r
949 \r
950 /**\r
951   Enumerate all the changed hub ports\r
952 \r
953   @param  Event                 The event that is triggered\r
954   @param  Context               The context to the event\r
955 \r
956   @return None\r
957 \r
958 **/\r
959 VOID\r
960 UsbHubEnumeration (\r
961   IN EFI_EVENT            Event,\r
962   IN VOID                 *Context\r
963   )\r
964 {\r
965   USB_INTERFACE           *HubIf;\r
966   UINT8                   Byte;\r
967   UINT8                   Bit;\r
968   UINT8                   Index;\r
969 \r
970   ASSERT (Context);\r
971 \r
972   HubIf = (USB_INTERFACE *) Context;\r
973 \r
974   if (HubIf->ChangeMap == NULL) {\r
975     return ;\r
976   }\r
977 \r
978   //\r
979   // HUB starts its port index with 1.\r
980   //\r
981   Byte  = 0;\r
982   Bit   = 1;\r
983 \r
984   for (Index = 0; Index < HubIf->NumOfPort; Index++) {\r
985     if (USB_BIT_IS_SET (HubIf->ChangeMap[Byte], USB_BIT (Bit))) {\r
986       UsbEnumeratePort (HubIf, Index);\r
987     }\r
988 \r
989     USB_NEXT_BIT (Byte, Bit);\r
990   }\r
991 \r
992   UsbHubAckHubStatus (HubIf->Device);\r
993 \r
994   gBS->FreePool (HubIf->ChangeMap);\r
995   HubIf->ChangeMap = NULL;\r
996   return ;\r
997 }\r
998 \r
999 \r
1000 /**\r
1001   Enumerate all the changed hub ports\r
1002 \r
1003   @param  Event                 The event that is triggered\r
1004   @param  Context               The context to the event\r
1005 \r
1006   @return None\r
1007 \r
1008 **/\r
1009 VOID\r
1010 EFIAPI\r
1011 UsbRootHubEnumeration (\r
1012   IN EFI_EVENT            Event,\r
1013   IN VOID                 *Context\r
1014   )\r
1015 {\r
1016   USB_INTERFACE           *RootHub;\r
1017   UINT8                   Index;\r
1018 \r
1019   RootHub = (USB_INTERFACE *) Context;\r
1020 \r
1021   for (Index = 0; Index < RootHub->NumOfPort; Index++) {\r
1022     UsbEnumeratePort (RootHub, Index);\r
1023   }\r
1024 }\r