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