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