Import Usb/UsbBusDxe and Usb/UsbMassStorageDxe into MdeModulePkg.
[people/mcb30/edk2.git] / edk2 / MdeModulePkg / Bus / Usb / UsbBusDxe / UsbHub.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     UsbHub.c\r
15 \r
16   Abstract:\r
17 \r
18     Unified interface for RootHub and Hub\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 // USB hub class specific requests. Although USB hub\r
29 // is related to an interface, these requests are sent\r
30 // to the control endpoint of the device.\r
31 //\r
32 \r
33 \r
34 /**\r
35   USB hub control transfer to clear the hub feature\r
36 \r
37   @param  HubDev                The device of the hub\r
38   @param  Feature               The feature to clear\r
39 \r
40   @retval EFI_SUCCESS           Feature of the hub is cleared\r
41   @retval Others                Failed to clear the feature\r
42 \r
43 **/\r
44 STATIC\r
45 EFI_STATUS\r
46 UsbHubCtrlClearHubFeature (\r
47   IN USB_DEVICE           *HubDev,\r
48   IN UINT16               Feature\r
49   )\r
50 {\r
51   EFI_STATUS              Status;\r
52 \r
53   Status = UsbCtrlRequest (\r
54              HubDev,\r
55              EfiUsbNoData,\r
56              USB_REQ_TYPE_CLASS,\r
57              USB_HUB_TARGET_HUB,\r
58              USB_HUB_REQ_CLEAR_FEATURE,\r
59              Feature,\r
60              0,\r
61              NULL,\r
62              0\r
63              );\r
64 \r
65   return Status;\r
66 }\r
67 \r
68 \r
69 /**\r
70   Clear the feature of the device's port\r
71 \r
72   @param  HubDev                The hub device\r
73   @param  Port                  The port to clear feature\r
74   @param  Feature               The feature to clear\r
75 \r
76   @retval EFI_SUCCESS           The feature of the port is cleared.\r
77   @retval Others                Failed to clear the feature.\r
78 \r
79 **/\r
80 STATIC\r
81 EFI_STATUS\r
82 UsbHubCtrlClearPortFeature (\r
83   IN USB_DEVICE           *HubDev,\r
84   IN UINT8                Port,\r
85   IN UINT16               Feature\r
86   )\r
87 {\r
88   EFI_STATUS              Status;\r
89 \r
90   //\r
91   // In USB bus, all the port index starts from 0. But HUB\r
92   // indexes its port from 1. So, port number is added one.\r
93   //\r
94   Status = UsbCtrlRequest (\r
95              HubDev,\r
96              EfiUsbNoData,\r
97              USB_REQ_TYPE_CLASS,\r
98              USB_HUB_TARGET_PORT,\r
99              USB_HUB_REQ_CLEAR_FEATURE,\r
100              Feature,\r
101              Port + 1,\r
102              NULL,\r
103              0\r
104              );\r
105 \r
106   return Status;\r
107 }\r
108 \r
109 \r
110 \r
111 /**\r
112   Clear the transaction translate buffer if full/low\r
113   speed control/bulk transfer failed and the transfer\r
114   uses this hub as translator.Remember to clear the TT\r
115   buffer of transaction translator, not that of the\r
116   parent.\r
117 \r
118   @param  HubDev                The hub device\r
119   @param  Port                  The port of the hub\r
120   @param  DevAddr               Address of the failed transaction\r
121   @param  EpNum                 The endpoint number of the failed transaction\r
122   @param  EpType                The type of failed transaction\r
123 \r
124   @retval EFI_SUCCESS           The TT buffer is cleared\r
125   @retval Others                Failed to clear the TT buffer\r
126 \r
127 **/\r
128 EFI_STATUS\r
129 UsbHubCtrlClearTTBuffer (\r
130   IN USB_DEVICE           *HubDev,\r
131   IN UINT8                Port,\r
132   IN UINT16               DevAddr,\r
133   IN UINT16               EpNum,\r
134   IN UINT16               EpType\r
135   )\r
136 {\r
137   EFI_STATUS              Status;\r
138   UINT16                  Value;\r
139 \r
140   //\r
141   // Check USB2.0 spec page 424 for wValue's encoding\r
142   //\r
143   Value = (EpNum & 0x0F) | (DevAddr << 4) |\r
144           ((EpType & 0x03) << 11) | ((EpNum & 0x80) << 15);\r
145 \r
146   Status = UsbCtrlRequest (\r
147              HubDev,\r
148              EfiUsbNoData,\r
149              USB_REQ_TYPE_CLASS,\r
150              USB_HUB_TARGET_PORT,\r
151              USB_HUB_REQ_CLEAR_TT,\r
152              Value,\r
153              Port + 1,\r
154              NULL,\r
155              0\r
156              );\r
157 \r
158   return Status;\r
159 }\r
160 \r
161 \r
162 /**\r
163   Usb hub control transfer to get the hub descriptor\r
164 \r
165   @param  HubDev                The hub device\r
166   @param  Buf                   The buffer to hold the descriptor\r
167   @param  Len                   The length to retrieve\r
168 \r
169   @retval EFI_SUCCESS           The hub descriptor is retrieved\r
170   @retval Others                Failed to retrieve the hub descriptor\r
171 \r
172 **/\r
173 STATIC\r
174 EFI_STATUS\r
175 UsbHubCtrlGetHubDesc (\r
176   IN  USB_DEVICE          *HubDev,\r
177   OUT VOID                *Buf,\r
178   IN  UINTN               Len\r
179   )\r
180 {\r
181   EFI_STATUS              Status;\r
182 \r
183   Status = UsbCtrlRequest (\r
184              HubDev,\r
185              EfiUsbDataIn,\r
186              USB_REQ_TYPE_CLASS,\r
187              USB_HUB_TARGET_HUB,\r
188              USB_HUB_REQ_GET_DESC,\r
189              (UINT16) (USB_DESC_TYPE_HUB << 8),\r
190              0,\r
191              Buf,\r
192              Len\r
193              );\r
194 \r
195   return Status;\r
196 }\r
197 \r
198 \r
199 /**\r
200   Usb hub control transfer to get the hub status\r
201 \r
202   @param  HubDev                The hub device\r
203   @param  State                 The variable to return the status\r
204 \r
205   @retval EFI_SUCCESS           The hub status is returned in State\r
206   @retval Others                Failed to get the hub status\r
207 \r
208 **/\r
209 STATIC\r
210 EFI_STATUS\r
211 UsbHubCtrlGetHubStatus (\r
212   IN  USB_DEVICE          *HubDev,\r
213   OUT UINT32              *State\r
214   )\r
215 {\r
216   EFI_STATUS              Status;\r
217 \r
218   Status = UsbCtrlRequest (\r
219              HubDev,\r
220              EfiUsbDataIn,\r
221              USB_REQ_TYPE_CLASS,\r
222              USB_HUB_TARGET_HUB,\r
223              USB_HUB_REQ_GET_STATUS,\r
224              0,\r
225              0,\r
226              State,\r
227              4\r
228              );\r
229 \r
230   return Status;\r
231 }\r
232 \r
233 \r
234 /**\r
235   Usb hub control transfer to get the port status\r
236 \r
237   @param  HubDev                The hub device\r
238   @param  Port                  The port of the hub\r
239   @param  State                 Variable to return the hub port state\r
240 \r
241   @retval EFI_SUCCESS           The port state is returned in State\r
242   @retval Others                Failed to retrive the port state\r
243 \r
244 **/\r
245 STATIC\r
246 EFI_STATUS\r
247 UsbHubCtrlGetPortStatus (\r
248   IN  USB_DEVICE          *HubDev,\r
249   IN  UINT8               Port,\r
250   OUT VOID                *State\r
251   )\r
252 {\r
253   EFI_STATUS              Status;\r
254 \r
255   //\r
256   // In USB bus, all the port index starts from 0. But HUB\r
257   // indexes its port from 1. So, port number is added one.\r
258   // No need to convert the hub bit to UEFI definition, they\r
259   // are the same\r
260   //\r
261   Status = UsbCtrlRequest (\r
262              HubDev,\r
263              EfiUsbDataIn,\r
264              USB_REQ_TYPE_CLASS,\r
265              USB_HUB_TARGET_PORT,\r
266              USB_HUB_REQ_GET_STATUS,\r
267              0,\r
268              Port + 1,\r
269              State,\r
270              4\r
271              );\r
272 \r
273   return Status;\r
274 }\r
275 \r
276 \r
277 /**\r
278   Usb hub control transfer to reset the TT (Transaction Transaltor)\r
279 \r
280   @param  HubDev                The hub device\r
281   @param  Port                  The port of the hub\r
282 \r
283   @retval EFI_SUCCESS           The TT of the hub is reset\r
284   @retval Others                Failed to reset the port\r
285 \r
286 **/\r
287 STATIC\r
288 EFI_STATUS\r
289 UsbHubCtrlResetTT (\r
290   IN  USB_DEVICE          *HubDev,\r
291   IN  UINT8               Port\r
292   )\r
293 {\r
294   EFI_STATUS              Status;\r
295 \r
296   Status = UsbCtrlRequest (\r
297              HubDev,\r
298              EfiUsbNoData,\r
299              USB_REQ_TYPE_CLASS,\r
300              USB_HUB_TARGET_HUB,\r
301              USB_HUB_REQ_RESET_TT,\r
302              0,\r
303              Port + 1,\r
304              NULL,\r
305              0\r
306              );\r
307 \r
308   return Status;\r
309 }\r
310 \r
311 \r
312 /**\r
313   Usb hub control transfer to set the hub feature\r
314 \r
315   @param  HubDev                The hub device\r
316   @param  Feature               The feature to set\r
317 \r
318   @retval EFI_SUCESS            The feature is set for the hub\r
319   @retval Others                Failed to set the feature\r
320 \r
321 **/\r
322 STATIC\r
323 EFI_STATUS\r
324 UsbHubCtrlSetHubFeature (\r
325   IN  USB_DEVICE          *HubDev,\r
326   IN  UINT8               Feature\r
327   )\r
328 {\r
329   EFI_STATUS              Status;\r
330 \r
331   Status = UsbCtrlRequest (\r
332              HubDev,\r
333              EfiUsbNoData,\r
334              USB_REQ_TYPE_CLASS,\r
335              USB_HUB_TARGET_HUB,\r
336              USB_HUB_REQ_SET_FEATURE,\r
337              Feature,\r
338              0,\r
339              NULL,\r
340              0\r
341              );\r
342 \r
343   return Status;\r
344 }\r
345 \r
346 \r
347 /**\r
348   Usb hub control transfer to set the port feature\r
349 \r
350   @param  HubDev                The Usb hub device\r
351   @param  Port                  The Usb port to set feature for\r
352   @param  Feature               The feature to set\r
353 \r
354   @retval EFI_SUCCESS           The feature is set for the port\r
355   @retval Others                Failed to set the feature\r
356 \r
357 **/\r
358 STATIC\r
359 EFI_STATUS\r
360 UsbHubCtrlSetPortFeature (\r
361   IN USB_DEVICE           *HubDev,\r
362   IN UINT8                Port,\r
363   IN UINT8                Feature\r
364   )\r
365 {\r
366   EFI_STATUS              Status;\r
367 \r
368   //\r
369   // In USB bus, all the port index starts from 0. But HUB\r
370   // indexes its port from 1. So, port number is added one.\r
371   //\r
372   Status = UsbCtrlRequest (\r
373              HubDev,\r
374              EfiUsbNoData,\r
375              USB_REQ_TYPE_CLASS,\r
376              USB_HUB_TARGET_PORT,\r
377              USB_HUB_REQ_SET_FEATURE,\r
378              Feature,\r
379              Port + 1,\r
380              NULL,\r
381              0\r
382              );\r
383 \r
384   return Status;\r
385 }\r
386 \r
387 \r
388 /**\r
389   Read the whole usb hub descriptor. It is necessary\r
390   to do it in two steps because hub descriptor is of\r
391   variable length\r
392 \r
393   @param  HubDev                The hub device\r
394   @param  HubDesc               The variable to return the descriptor\r
395 \r
396   @retval EFI_SUCCESS           The hub descriptor is read\r
397   @retval Others                Failed to read the hub descriptor\r
398 \r
399 **/\r
400 STATIC\r
401 EFI_STATUS\r
402 UsbHubReadDesc (\r
403   IN  USB_DEVICE              *HubDev,\r
404   OUT EFI_USB_HUB_DESCRIPTOR  *HubDesc\r
405   )\r
406 {\r
407   EFI_STATUS              Status;\r
408 \r
409   //\r
410   // First get the hub descriptor length\r
411   //\r
412   Status = UsbHubCtrlGetHubDesc (HubDev, HubDesc, 2);\r
413 \r
414   if (EFI_ERROR (Status)) {\r
415     return Status;\r
416   }\r
417 \r
418   //\r
419   // Get the whole hub descriptor\r
420   //\r
421   Status = UsbHubCtrlGetHubDesc (HubDev, HubDesc, HubDesc->Length);\r
422 \r
423   return Status;\r
424 }\r
425 \r
426 \r
427 \r
428 /**\r
429   Ack the hub change bits. If these bits are not ACKed, Hub will\r
430   always return changed bit map from its interrupt endpoint.\r
431 \r
432   @param  HubDev                The hub device\r
433 \r
434   @retval EFI_SUCCESS           The hub change status is ACKed\r
435   @retval Others                Failed to ACK the hub status\r
436 \r
437 **/\r
438 EFI_STATUS\r
439 UsbHubAckHubStatus (\r
440   IN  USB_DEVICE         *HubDev\r
441   )\r
442 {\r
443   EFI_USB_PORT_STATUS     HubState;\r
444   EFI_STATUS              Status;\r
445 \r
446   Status = UsbHubCtrlGetHubStatus (HubDev, (UINT32 *) &HubState);\r
447 \r
448   if (EFI_ERROR (Status)) {\r
449     return Status;\r
450   }\r
451 \r
452   if (USB_BIT_IS_SET (HubState.PortChangeStatus, USB_HUB_STAT_C_LOCAL_POWER)) {\r
453     UsbHubCtrlClearHubFeature (HubDev, USB_HUB_C_HUB_LOCAL_POWER);\r
454   }\r
455 \r
456   if (USB_BIT_IS_SET (HubState.PortChangeStatus, USB_HUB_STAT_C_OVER_CURRENT)) {\r
457     UsbHubCtrlClearHubFeature (HubDev, USB_HUB_C_HUB_OVER_CURRENT);\r
458   }\r
459 \r
460   return EFI_SUCCESS;\r
461 }\r
462 \r
463 \r
464 /**\r
465   Test whether the interface is a hub interface.\r
466 \r
467   @param  UsbIf                 The interface to test\r
468 \r
469   @retval TRUE                  The interface is a hub interface\r
470   @retval FALSE                 The interface isn't a hub interface\r
471 \r
472 **/\r
473 BOOLEAN\r
474 UsbIsHubInterface (\r
475   IN USB_INTERFACE        *UsbIf\r
476   )\r
477 {\r
478   EFI_USB_INTERFACE_DESCRIPTOR  *Setting;\r
479 \r
480   //\r
481   // If the hub is a high-speed hub with multiple TT,\r
482   // the hub will has a default setting of single TT.\r
483   //\r
484   Setting = &UsbIf->IfSetting->Desc;\r
485 \r
486   if ((Setting->InterfaceClass == USB_HUB_CLASS_CODE) &&\r
487       (Setting->InterfaceSubClass == USB_HUB_SUBCLASS_CODE)) {\r
488 \r
489     return TRUE;\r
490   }\r
491 \r
492   return FALSE;\r
493 }\r
494 \r
495 \r
496 /**\r
497   The callback function to the USB hub status change\r
498   interrupt endpoint. It is called periodically by\r
499   the underlying host controller.\r
500 \r
501   @param  Data                  The data read\r
502   @param  DataLength            The length of the data read\r
503   @param  Context               The context\r
504   @param  Result                The result of the last interrupt transfer\r
505 \r
506   @retval EFI_SUCCESS           The process is OK\r
507   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource\r
508 \r
509 **/\r
510 STATIC\r
511 EFI_STATUS\r
512 UsbOnHubInterrupt (\r
513   IN  VOID                *Data,\r
514   IN  UINTN               DataLength,\r
515   IN  VOID                *Context,\r
516   IN  UINT32              Result\r
517   )\r
518 {\r
519   USB_INTERFACE               *HubIf;\r
520   EFI_USB_IO_PROTOCOL         *UsbIo;\r
521   EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc;\r
522   EFI_STATUS                  Status;\r
523 \r
524   HubIf   = (USB_INTERFACE *) Context;\r
525   UsbIo   = &(HubIf->UsbIo);\r
526   EpDesc  = &(HubIf->HubEp->Desc);\r
527 \r
528   if (Result != EFI_USB_NOERROR) {\r
529     //\r
530     // If endpoint is stalled, clear the stall. Use UsbIo to access\r
531     // the control transfer so internal status are maintained.\r
532     //\r
533     if (USB_BIT_IS_SET (Result, EFI_USB_ERR_STALL)) {\r
534       UsbIoClearFeature (\r
535         UsbIo,\r
536         USB_TARGET_ENDPOINT,\r
537         USB_FEATURE_ENDPOINT_HALT,\r
538         EpDesc->EndpointAddress\r
539         );\r
540     }\r
541 \r
542     //\r
543     // Delete and submit a new async interrupt\r
544     //\r
545     Status = UsbIo->UsbAsyncInterruptTransfer (\r
546                       UsbIo,\r
547                       EpDesc->EndpointAddress,\r
548                       FALSE,\r
549                       0,\r
550                       0,\r
551                       NULL,\r
552                       NULL\r
553                       );\r
554 \r
555     if (EFI_ERROR (Status)) {\r
556       USB_ERROR (("UsbOnHubInterrupt: failed to remove async transfer - %r\n", Status));\r
557       return Status;\r
558     }\r
559 \r
560     Status = UsbIo->UsbAsyncInterruptTransfer (\r
561                       UsbIo,\r
562                       EpDesc->EndpointAddress,\r
563                       TRUE,\r
564                       USB_HUB_POLL_INTERVAL,\r
565                       HubIf->NumOfPort / 8 + 1,\r
566                       UsbOnHubInterrupt,\r
567                       HubIf\r
568                       );\r
569 \r
570     if (EFI_ERROR (Status)) {\r
571       USB_ERROR (("UsbOnHubInterrupt: failed to submit new async transfer - %r\n", Status));\r
572     }\r
573 \r
574     return Status;\r
575   }\r
576 \r
577   if ((DataLength == 0) || (Data == NULL)) {\r
578     return EFI_SUCCESS;\r
579   }\r
580 \r
581   //\r
582   // OK, actually something is changed, save the change map\r
583   // then signal the HUB to do enumeration. This is a good\r
584   // practise since UsbOnHubInterrupt is called in the context\r
585   // of host contrller's AsyncInterrupt monitor.\r
586   //\r
587   HubIf->ChangeMap = AllocateZeroPool (DataLength);\r
588 \r
589   if (HubIf->ChangeMap == NULL) {\r
590     return EFI_OUT_OF_RESOURCES;\r
591   }\r
592 \r
593   CopyMem (HubIf->ChangeMap, Data, DataLength);\r
594   gBS->SignalEvent (HubIf->HubNotify);\r
595 \r
596   return EFI_SUCCESS;\r
597 }\r
598 \r
599 //\r
600 // Array that maps the change bit to feature value which is\r
601 // used to clear these change bit. USB HUB API will clear\r
602 // these change bit automatically. For non-root hub, these\r
603 // bits determine whether hub will report the port in changed\r
604 // bit maps.\r
605 //\r
606 #define USB_HUB_MAP_SIZE  5\r
607 \r
608 USB_CHANGE_FEATURE_MAP  mHubFeatureMap[USB_HUB_MAP_SIZE] = {\r
609   {USB_PORT_STAT_C_CONNECTION,  USB_HUB_C_PORT_CONNECT},\r
610   {USB_PORT_STAT_C_ENABLE,      USB_HUB_C_PORT_ENABLE},\r
611   {USB_PORT_STAT_C_SUSPEND,     USB_HUB_C_PORT_SUSPEND},\r
612   {USB_PORT_STAT_C_OVERCURRENT, USB_HUB_C_PORT_OVER_CURRENT},\r
613   {USB_PORT_STAT_C_RESET,       USB_HUB_C_PORT_RESET},\r
614 };\r
615 \r
616 #define USB_ROOT_HUB_MAP_SIZE 5\r
617 \r
618 USB_CHANGE_FEATURE_MAP  mRootHubFeatureMap[USB_ROOT_HUB_MAP_SIZE] = {\r
619   {USB_PORT_STAT_C_CONNECTION,  EfiUsbPortConnectChange},\r
620   {USB_PORT_STAT_C_ENABLE,      EfiUsbPortEnableChange},\r
621   {USB_PORT_STAT_C_SUSPEND,     EfiUsbPortSuspendChange},\r
622   {USB_PORT_STAT_C_OVERCURRENT, EfiUsbPortOverCurrentChange},\r
623   {USB_PORT_STAT_C_RESET,       EfiUsbPortResetChange},\r
624 };\r
625 \r
626 \r
627 \r
628 /**\r
629   Initialize the device for a non-root hub\r
630 \r
631   @param  HubIf                 The USB hub interface\r
632 \r
633   @retval EFI_SUCCESS           The hub is initialized\r
634   @retval EFI_DEVICE_ERROR      Failed to initialize the hub\r
635 \r
636 **/\r
637 STATIC\r
638 EFI_STATUS\r
639 UsbHubInit (\r
640   IN USB_INTERFACE        *HubIf\r
641   )\r
642 {\r
643   EFI_USB_HUB_DESCRIPTOR  HubDesc;\r
644   USB_ENDPOINT_DESC       *EpDesc;\r
645   USB_INTERFACE_SETTING   *Setting;\r
646   EFI_USB_IO_PROTOCOL     *UsbIo;\r
647   USB_DEVICE              *HubDev;\r
648   EFI_STATUS              Status;\r
649   UINT8                   Index;\r
650 \r
651   //\r
652   // Locate the interrupt endpoint for port change map\r
653   //\r
654   HubIf->IsHub  = FALSE;\r
655   Setting       = HubIf->IfSetting;\r
656   HubDev        = HubIf->Device;\r
657   EpDesc        = NULL;\r
658 \r
659   for (Index = 0; Index < Setting->Desc.NumEndpoints; Index++) {\r
660     ASSERT ((Setting->Endpoints != NULL) && (Setting->Endpoints[Index] != NULL));\r
661 \r
662     EpDesc = Setting->Endpoints[Index];\r
663 \r
664     if (USB_BIT_IS_SET (EpDesc->Desc.EndpointAddress, USB_ENDPOINT_DIR_IN) &&\r
665        (USB_ENDPOINT_TYPE (&EpDesc->Desc) == USB_ENDPOINT_INTERRUPT)) {\r
666       break;\r
667     }\r
668   }\r
669 \r
670   if (Index == Setting->Desc.NumEndpoints) {\r
671     USB_ERROR (("UsbHubInit: no interrupt endpoint found for hub %d\n", HubDev->Address));\r
672     return EFI_DEVICE_ERROR;\r
673   }\r
674 \r
675   Status = UsbHubReadDesc (HubDev, &HubDesc);\r
676 \r
677   if (EFI_ERROR (Status)) {\r
678     USB_ERROR (("UsbHubInit: failed to read HUB descriptor %r\n", Status));\r
679     return Status;\r
680   }\r
681 \r
682   HubIf->NumOfPort = HubDesc.NumPorts;\r
683 \r
684   USB_DEBUG (("UsbHubInit: hub %d has %d ports\n", HubDev->Address,HubIf->NumOfPort));\r
685 \r
686   //\r
687   // Create an event to enumerate the hub's port. On\r
688   //\r
689   Status = gBS->CreateEvent (\r
690                   EVT_NOTIFY_SIGNAL,\r
691                   TPL_CALLBACK,\r
692                   UsbHubEnumeration,\r
693                   HubIf,\r
694                   &HubIf->HubNotify\r
695                   );\r
696 \r
697   if (EFI_ERROR (Status)) {\r
698     USB_ERROR (("UsbHubInit: failed to create signal for hub %d - %r\n",\r
699                 HubDev->Address, Status));\r
700 \r
701     return Status;\r
702   }\r
703 \r
704   //\r
705   // Create AsyncInterrupt to query hub port change endpoint\r
706   // periodically. If the hub ports are changed, hub will return\r
707   // changed port map from the interrupt endpoint. The port map\r
708   // must be able to hold (HubIf->NumOfPort + 1) bits (one bit for\r
709   // host change status).\r
710   //\r
711   UsbIo  = &HubIf->UsbIo;\r
712   Status = UsbIo->UsbAsyncInterruptTransfer (\r
713                     UsbIo,\r
714                     EpDesc->Desc.EndpointAddress,\r
715                     TRUE,\r
716                     USB_HUB_POLL_INTERVAL,\r
717                     HubIf->NumOfPort / 8 + 1,\r
718                     UsbOnHubInterrupt,\r
719                     HubIf\r
720                     );\r
721 \r
722   if (EFI_ERROR (Status)) {\r
723     USB_ERROR (("UsbHubInit: failed to queue interrupt transfer for hub %d - %r\n",\r
724                 HubDev->Address, Status));\r
725 \r
726     gBS->CloseEvent (HubIf->HubNotify);\r
727     HubIf->HubNotify = NULL;\r
728 \r
729     return Status;\r
730   }\r
731 \r
732   //\r
733   // OK, set IsHub to TRUE. Now usb bus can handle this device\r
734   // as a working HUB. If failed eariler, bus driver will not\r
735   // recognize it as a hub. Other parts of the bus should be able\r
736   // to work.\r
737   //\r
738   HubIf->IsHub  = TRUE;\r
739   HubIf->HubApi = &mUsbHubApi;\r
740   HubIf->HubEp  = EpDesc;\r
741 \r
742   //\r
743   // Feed power to all the hub ports. It should be ok\r
744   // for both gang/individual powered hubs.\r
745   //\r
746   for (Index = 0; Index < HubDesc.NumPorts; Index++) {\r
747     UsbHubCtrlSetPortFeature (HubIf->Device, Index, USB_HUB_PORT_POWER);\r
748   }\r
749 \r
750   gBS->Stall (HubDesc.PwrOn2PwrGood * 2 * USB_STALL_1_MS);\r
751   UsbHubAckHubStatus (HubIf->Device);\r
752 \r
753   USB_DEBUG (("UsbHubInit: hub %d initialized\n", HubDev->Address));\r
754   return Status;\r
755 }\r
756 \r
757 \r
758 \r
759 /**\r
760   Get the port status. This function is required to\r
761   ACK the port change bits although it will return\r
762   the port changes in PortState. Bus enumeration code\r
763   doesn't need to ACK the port change bits.\r
764 \r
765   @param  HubIf                 The hub interface\r
766   @param  Port                  The port of the hub to get state\r
767   @param  PortState             Variable to return the port state\r
768 \r
769   @retval EFI_SUCCESS           The port status is successfully returned\r
770   @retval Others                Failed to return the status\r
771 \r
772 **/\r
773 STATIC\r
774 EFI_STATUS\r
775 UsbHubGetPortStatus (\r
776   IN  USB_INTERFACE       *HubIf,\r
777   IN  UINT8               Port,\r
778   OUT EFI_USB_PORT_STATUS *PortState\r
779   )\r
780 {\r
781   EFI_STATUS              Status;\r
782 \r
783   Status  = UsbHubCtrlGetPortStatus (HubIf->Device, Port, PortState);\r
784 \r
785   return Status;\r
786 }\r
787 \r
788 \r
789 \r
790 /**\r
791   Clear the port change status.\r
792 \r
793   @param  HubIf                 The hub interface\r
794   @param  Port                  The hub port\r
795 \r
796   @return None\r
797 \r
798 **/\r
799 STATIC\r
800 VOID\r
801 UsbHubClearPortChange (\r
802   IN USB_INTERFACE        *HubIf,\r
803   IN UINT8                Port\r
804   )\r
805 {\r
806   EFI_USB_PORT_STATUS     PortState;\r
807   USB_CHANGE_FEATURE_MAP  *Map;\r
808   UINTN                   Index;\r
809   EFI_STATUS              Status;\r
810 \r
811   Status = UsbHubGetPortStatus (HubIf, Port, &PortState);\r
812 \r
813   if (EFI_ERROR (Status)) {\r
814     return;\r
815   }\r
816 \r
817   //\r
818   // OK, get the usb port status, now ACK the change bits.\r
819   // Don't return error when failed to clear the change bits.\r
820   // It may lead to extra port state report. USB bus should\r
821   // be able to handle this.\r
822   //\r
823   for (Index = 0; Index < USB_HUB_MAP_SIZE; Index++) {\r
824     Map = &mHubFeatureMap[Index];\r
825 \r
826     if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) {\r
827       UsbHubCtrlClearPortFeature (HubIf->Device, Port, Map->Feature);\r
828     }\r
829   }\r
830 }\r
831 \r
832 \r
833 \r
834 /**\r
835   Function to set the port feature for non-root hub\r
836 \r
837   @param  HubIf                 The hub interface\r
838   @param  Port                  The port of the hub\r
839   @param  Feature               The feature of the port to set\r
840 \r
841   @retval EFI_SUCCESS           The hub port feature is set\r
842   @retval Others                Failed to set the port feature\r
843 \r
844 **/\r
845 STATIC\r
846 EFI_STATUS\r
847 UsbHubSetPortFeature (\r
848   IN USB_INTERFACE        *HubIf,\r
849   IN UINT8                Port,\r
850   IN EFI_USB_PORT_FEATURE Feature\r
851   )\r
852 {\r
853   EFI_STATUS              Status;\r
854 \r
855   Status = UsbHubCtrlSetPortFeature (HubIf->Device, Port, Feature);\r
856 \r
857   return Status;\r
858 }\r
859 \r
860 \r
861 /**\r
862   Interface function to clear the port feature for non-root hub\r
863 \r
864   @param  HubIf                 The hub interface\r
865   @param  Port                  The port of the hub to clear feature for\r
866   @param  Feature               The feature to clear\r
867 \r
868   @retval EFI_SUCCESS           The port feature is cleared\r
869   @retval Others                Failed to clear the port feature\r
870 \r
871 **/\r
872 STATIC\r
873 EFI_STATUS\r
874 UsbHubClearPortFeature (\r
875   IN USB_INTERFACE        *HubIf,\r
876   IN UINT8                Port,\r
877   IN EFI_USB_PORT_FEATURE Feature\r
878   )\r
879 {\r
880   EFI_STATUS              Status;\r
881 \r
882   Status = UsbHubCtrlClearPortFeature (HubIf->Device, Port, Feature);\r
883 \r
884   return Status;\r
885 }\r
886 \r
887 \r
888 /**\r
889   Interface funtion to reset the port\r
890 \r
891   @param  HubIf                 The hub interface\r
892   @param  Port                  The port to reset\r
893 \r
894   @retval EFI_SUCCESS           The hub port is reset\r
895   @retval EFI_TIMEOUT           Failed to reset the port in time\r
896   @retval Others                Failed to reset the port\r
897 \r
898 **/\r
899 STATIC\r
900 EFI_STATUS\r
901 UsbHubResetPort (\r
902   IN USB_INTERFACE        *HubIf,\r
903   IN UINT8                Port\r
904   )\r
905 {\r
906   EFI_USB_PORT_STATUS     PortState;\r
907   UINTN                   Index;\r
908   EFI_STATUS              Status;\r
909 \r
910   Status  = UsbHubSetPortFeature (HubIf, Port, USB_HUB_PORT_RESET);\r
911 \r
912   if (EFI_ERROR (Status)) {\r
913     return Status;\r
914   }\r
915 \r
916   //\r
917   // Drive the reset signal for at least 10ms. Check USB 2.0 Spec\r
918   // section 7.1.7.5 for timing requirements.\r
919   //\r
920   gBS->Stall (20 * USB_STALL_1_MS);\r
921 \r
922   //\r
923   // USB hub will clear RESET bit if reset is actually finished.\r
924   //\r
925   ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS));\r
926 \r
927   for (Index = 0; Index < 20; Index++) {\r
928     Status = UsbHubGetPortStatus (HubIf, Port, &PortState);\r
929 \r
930     if (!EFI_ERROR (Status) &&\r
931         !USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_RESET)) {\r
932 \r
933       return EFI_SUCCESS;\r
934     }\r
935 \r
936     gBS->Stall (5 * USB_STALL_1_MS);\r
937   }\r
938 \r
939   return EFI_TIMEOUT;\r
940 }\r
941 \r
942 \r
943 /**\r
944   Release the hub's control of the interface\r
945 \r
946   @param  HubIf                 The hub interface\r
947 \r
948   @retval EFI_SUCCESS           The interface is release of hub control\r
949 \r
950 **/\r
951 STATIC\r
952 EFI_STATUS\r
953 UsbHubRelease (\r
954   IN USB_INTERFACE        *HubIf\r
955   )\r
956 {\r
957   EFI_USB_IO_PROTOCOL     *UsbIo;\r
958   EFI_STATUS              Status;\r
959 \r
960   UsbIo  = &HubIf->UsbIo;\r
961   Status = UsbIo->UsbAsyncInterruptTransfer (\r
962                     UsbIo,\r
963                     HubIf->HubEp->Desc.EndpointAddress,\r
964                     FALSE,\r
965                     USB_HUB_POLL_INTERVAL,\r
966                     0,\r
967                     NULL,\r
968                     0\r
969                     );\r
970 \r
971   if (EFI_ERROR (Status)) {\r
972     return Status;\r
973   }\r
974 \r
975   gBS->CloseEvent (HubIf->HubNotify);\r
976 \r
977   HubIf->IsHub      = FALSE;\r
978   HubIf->HubApi     = NULL;\r
979   HubIf->HubEp      = NULL;\r
980   HubIf->HubNotify  = NULL;\r
981 \r
982   USB_DEBUG (("UsbHubRelease: hub device %d released\n", HubIf->Device->Address));\r
983   return EFI_SUCCESS;\r
984 }\r
985 \r
986 \r
987 \r
988 /**\r
989   Initialize the interface for root hub\r
990 \r
991   @param  HubIf                 The root hub interface\r
992 \r
993   @retval EFI_SUCCESS           The interface is initialied for root hub\r
994   @retval Others                Failed to initialize the hub\r
995 \r
996 **/\r
997 STATIC\r
998 EFI_STATUS\r
999 UsbRootHubInit (\r
1000   IN USB_INTERFACE        *HubIf\r
1001   )\r
1002 {\r
1003   EFI_STATUS              Status;\r
1004   UINT8                   MaxSpeed;\r
1005   UINT8                   NumOfPort;\r
1006   UINT8                   Support64;\r
1007 \r
1008   Status = UsbHcGetCapability (HubIf->Device->Bus, &MaxSpeed, &NumOfPort, &Support64);\r
1009 \r
1010   if (EFI_ERROR (Status)) {\r
1011     return Status;\r
1012   }\r
1013 \r
1014   USB_DEBUG (("UsbRootHubInit: root hub %x - max speed %d, %d ports\n",\r
1015               HubIf, MaxSpeed, NumOfPort));\r
1016 \r
1017   HubIf->IsHub      = TRUE;\r
1018   HubIf->HubApi     = &mUsbRootHubApi;\r
1019   HubIf->HubEp      = NULL;\r
1020   HubIf->MaxSpeed   = MaxSpeed;\r
1021   HubIf->NumOfPort  = NumOfPort;\r
1022   HubIf->HubNotify  = NULL;\r
1023 \r
1024   //\r
1025   // Create a timer to poll root hub ports periodically\r
1026   //\r
1027   Status = gBS->CreateEvent (\r
1028                   EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
1029                   TPL_CALLBACK,\r
1030                   UsbRootHubEnumeration,\r
1031                   HubIf,\r
1032                   &HubIf->HubNotify\r
1033                   );\r
1034 \r
1035   if (EFI_ERROR (Status)) {\r
1036     return Status;\r
1037   }\r
1038 \r
1039   Status = gBS->SetTimer (\r
1040                   HubIf->HubNotify,\r
1041                   TimerPeriodic,\r
1042                   USB_ROOTHUB_POLL_INTERVAL\r
1043                   );\r
1044 \r
1045   if (EFI_ERROR (Status)) {\r
1046     gBS->CloseEvent (HubIf->HubNotify);\r
1047   }\r
1048 \r
1049   return Status;\r
1050 }\r
1051 \r
1052 \r
1053 \r
1054 /**\r
1055   Get the port status. This function is required to\r
1056   ACK the port change bits although it will return\r
1057   the port changes in PortState. Bus enumeration code\r
1058   doesn't need to ACK the port change bits.\r
1059 \r
1060   @param  HubIf                 The root hub interface\r
1061   @param  Port                  The root hub port to get the state\r
1062   @param  PortState             Variable to return the port state\r
1063 \r
1064   @retval EFI_SUCCESS           The port state is returned\r
1065   @retval Others                Failed to retrieve the port state\r
1066 \r
1067 **/\r
1068 STATIC\r
1069 EFI_STATUS\r
1070 UsbRootHubGetPortStatus (\r
1071   IN  USB_INTERFACE       *HubIf,\r
1072   IN  UINT8               Port,\r
1073   OUT EFI_USB_PORT_STATUS *PortState\r
1074   )\r
1075 {\r
1076   USB_BUS                 *Bus;\r
1077   EFI_STATUS              Status;\r
1078 \r
1079   Bus     = HubIf->Device->Bus;\r
1080   Status  = UsbHcGetRootHubPortStatus (Bus, Port, PortState);\r
1081 \r
1082   return Status;\r
1083 }\r
1084 \r
1085 \r
1086 /**\r
1087   Clear the port change status.\r
1088 \r
1089   @param  HubIf                 The root hub interface\r
1090   @param  Port                  The root hub port\r
1091 \r
1092   @retval EFI_SUCCESS           The port state is returned\r
1093   @retval Others                Failed to retrieve the port state\r
1094 \r
1095 **/\r
1096 STATIC\r
1097 VOID\r
1098 UsbRootHubClearPortChange (\r
1099   IN USB_INTERFACE        *HubIf,\r
1100   IN UINT8                Port\r
1101   )\r
1102 {\r
1103   EFI_USB_PORT_STATUS     PortState;\r
1104   USB_CHANGE_FEATURE_MAP  *Map;\r
1105   UINTN                   Index;\r
1106   EFI_STATUS              Status;\r
1107 \r
1108   Status = UsbRootHubGetPortStatus (HubIf, Port, &PortState);\r
1109 \r
1110   if (EFI_ERROR (Status)) {\r
1111     return;\r
1112   }\r
1113 \r
1114   //\r
1115   // OK, get the usb port status, now ACK the change bits.\r
1116   // Don't return error when failed to clear the change bits.\r
1117   // It may lead to extra port state report. USB bus should\r
1118   // be able to handle this.\r
1119   //\r
1120   for (Index = 0; Index < USB_ROOT_HUB_MAP_SIZE; Index++) {\r
1121     Map = &mRootHubFeatureMap[Index];\r
1122 \r
1123     if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) {\r
1124       UsbHcClearRootHubPortFeature (HubIf->Device->Bus, Port, Map->Feature);\r
1125     }\r
1126   }\r
1127 }\r
1128 \r
1129 \r
1130 \r
1131 /**\r
1132   Set the root hub port feature\r
1133 \r
1134   @param  HubIf                 The Usb hub interface\r
1135   @param  Port                  The hub port\r
1136   @param  Feature               The feature to set\r
1137 \r
1138   @retval EFI_SUCCESS           The root hub port is set with the feature\r
1139   @retval Others                Failed to set the feature\r
1140 \r
1141 **/\r
1142 STATIC\r
1143 EFI_STATUS\r
1144 UsbRootHubSetPortFeature (\r
1145   IN USB_INTERFACE        *HubIf,\r
1146   IN UINT8                Port,\r
1147   IN EFI_USB_PORT_FEATURE Feature\r
1148   )\r
1149 {\r
1150   EFI_STATUS              Status;\r
1151 \r
1152   Status  = UsbHcSetRootHubPortFeature (HubIf->Device->Bus, Port, Feature);\r
1153 \r
1154   return Status;\r
1155 }\r
1156 \r
1157 \r
1158 /**\r
1159   Clear the root hub port feature\r
1160 \r
1161   @param  HubIf                 The root hub interface\r
1162   @param  Port                  The root hub port\r
1163   @param  Feature               The feature to clear\r
1164 \r
1165   @retval EFI_SUCCESS           The root hub port is cleared of the feature\r
1166   @retval Others                Failed to clear the feature\r
1167 \r
1168 **/\r
1169 STATIC\r
1170 EFI_STATUS\r
1171 UsbRootHubClearPortFeature (\r
1172   IN USB_INTERFACE        *HubIf,\r
1173   IN UINT8                Port,\r
1174   IN EFI_USB_PORT_FEATURE Feature\r
1175   )\r
1176 {\r
1177   EFI_STATUS              Status;\r
1178 \r
1179   Status  = UsbHcClearRootHubPortFeature (HubIf->Device->Bus, Port, Feature);\r
1180 \r
1181   return Status;\r
1182 }\r
1183 \r
1184 \r
1185 /**\r
1186   Interface funtion to reset the root hub port\r
1187 \r
1188   @param  RootIf                The root hub interface\r
1189   @param  Port                  The port to reset\r
1190 \r
1191   @retval EFI_SUCCESS           The hub port is reset\r
1192   @retval EFI_TIMEOUT           Failed to reset the port in time\r
1193   @retval EFI_NOT_FOUND         The low/full speed device connected to high  speed\r
1194                                 root hub is released to the companion UHCI\r
1195   @retval Others                Failed to reset the port\r
1196 \r
1197 **/\r
1198 STATIC\r
1199 EFI_STATUS\r
1200 UsbRootHubResetPort (\r
1201   IN USB_INTERFACE        *RootIf,\r
1202   IN UINT8                Port\r
1203   )\r
1204 {\r
1205   USB_BUS                 *Bus;\r
1206   EFI_STATUS              Status;\r
1207   EFI_USB_PORT_STATUS     PortState;\r
1208   UINTN                   Index;\r
1209 \r
1210   //\r
1211   // Notice: although EHCI requires that ENABLED bit be cleared\r
1212   // when reset the port, we don't need to care that here. It\r
1213   // should be handled in the EHCI driver.\r
1214   //\r
1215   Bus     = RootIf->Device->Bus;\r
1216   Status  = UsbHcSetRootHubPortFeature (Bus, Port, EfiUsbPortReset);\r
1217 \r
1218   if (EFI_ERROR (Status)) {\r
1219     USB_ERROR (("UsbRootHubResetPort: failed to start reset on port %d\n", Port));\r
1220     return Status;\r
1221   }\r
1222 \r
1223   //\r
1224   // Drive the reset signal for at least 50ms. Check USB 2.0 Spec\r
1225   // section 7.1.7.5 for timing requirements.\r
1226   //\r
1227   gBS->Stall (50 * USB_STALL_1_MS);\r
1228 \r
1229   Status = UsbHcClearRootHubPortFeature (Bus, Port, EfiUsbPortReset);\r
1230 \r
1231   if (EFI_ERROR (Status)) {\r
1232     USB_ERROR (("UsbRootHubResetPort: failed to clear reset on port %d\n", Port));\r
1233     return Status;\r
1234   }\r
1235 \r
1236   gBS->Stall (USB_STALL_1_MS);\r
1237 \r
1238   //\r
1239   // USB host controller won't clear the RESET bit until\r
1240   // reset is actually finished.\r
1241   //\r
1242   ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS));\r
1243 \r
1244   for (Index = 0; Index < USB_HUB_LOOP; Index++) {\r
1245     Status = UsbHcGetRootHubPortStatus (Bus, Port, &PortState);\r
1246 \r
1247     if (EFI_ERROR (Status)) {\r
1248       return Status;\r
1249     }\r
1250 \r
1251     if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_RESET)) {\r
1252       break;\r
1253     }\r
1254 \r
1255     gBS->Stall (10 * USB_STALL_1_MS);\r
1256   }\r
1257 \r
1258   if (Index == USB_HUB_LOOP) {\r
1259     USB_ERROR (("UsbRootHubResetPort: reset not finished in time on port %d\n", Port));\r
1260     return EFI_TIMEOUT;\r
1261   }\r
1262 \r
1263   if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_ENABLE)) {\r
1264     //\r
1265     // OK, the port is reset. If root hub is of high speed and\r
1266     // the device is of low/full speed, release the ownership to\r
1267     // companion UHCI. If root hub is of full speed, it won't\r
1268     // automatically enable the port, we need to enable it manually.\r
1269     //\r
1270     if (RootIf->MaxSpeed == EFI_USB_SPEED_HIGH) {\r
1271       USB_ERROR (("UsbRootHubResetPort: release low/full speed device (%d) to UHCI\n", Port));\r
1272 \r
1273       UsbRootHubSetPortFeature (RootIf, Port, EfiUsbPortOwner);\r
1274       return EFI_NOT_FOUND;\r
1275 \r
1276     } else {\r
1277 \r
1278       Status = UsbRootHubSetPortFeature (RootIf, Port, EfiUsbPortEnable);\r
1279 \r
1280       if (EFI_ERROR (Status)) {\r
1281         USB_ERROR (("UsbRootHubResetPort: failed to enable port %d for UHCI\n", Port));\r
1282         return Status;\r
1283       }\r
1284 \r
1285       gBS->Stall (20 * USB_STALL_1_MS);\r
1286     }\r
1287   }\r
1288 \r
1289   return EFI_SUCCESS;\r
1290 }\r
1291 \r
1292 \r
1293 /**\r
1294   Release the root hub's control of the interface\r
1295 \r
1296   @param  HubIf                 The root hub interface\r
1297 \r
1298   @retval EFI_SUCCESS           The root hub's control of the interface is\r
1299                                 released.\r
1300 \r
1301 **/\r
1302 STATIC\r
1303 EFI_STATUS\r
1304 UsbRootHubRelease (\r
1305   IN USB_INTERFACE        *HubIf\r
1306   )\r
1307 {\r
1308   USB_DEBUG (("UsbRootHubRelease: root hub released for hub %x\n", HubIf));\r
1309 \r
1310   gBS->SetTimer (HubIf->HubNotify, TimerCancel, USB_ROOTHUB_POLL_INTERVAL);\r
1311   gBS->CloseEvent (HubIf->HubNotify);\r
1312 \r
1313   return EFI_SUCCESS;\r
1314 }\r
1315 \r
1316 USB_HUB_API mUsbHubApi = {\r
1317   UsbHubInit,\r
1318   UsbHubGetPortStatus,\r
1319   UsbHubClearPortChange,\r
1320   UsbHubSetPortFeature,\r
1321   UsbHubClearPortFeature,\r
1322   UsbHubResetPort,\r
1323   UsbHubRelease\r
1324 };\r
1325 \r
1326 USB_HUB_API mUsbRootHubApi = {\r
1327   UsbRootHubInit,\r
1328   UsbRootHubGetPortStatus,\r
1329   UsbRootHubClearPortChange,\r
1330   UsbRootHubSetPortFeature,\r
1331   UsbRootHubClearPortFeature,\r
1332   UsbRootHubResetPort,\r
1333   UsbRootHubRelease\r
1334 };\r