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