]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c
MdeModulePkg: fix UninstallMultipleProtocolInterfaces() calls
[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
9388c6b1 162 UsbIf->Handle,\r
e237e7ae 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
1702e2ce 722 Status = EFI_NOT_FOUND;\r
92870c98 723 goto ON_ERROR;\r
724 } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_SUPER_SPEED)){\r
725 Child->Speed = EFI_USB_SPEED_SUPER;\r
726 Child->MaxPacket0 = 512;\r
e237e7ae 727 } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) {\r
92870c98 728 Child->Speed = EFI_USB_SPEED_HIGH;\r
729 Child->MaxPacket0 = 64;\r
730 } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) {\r
731 Child->Speed = EFI_USB_SPEED_LOW;\r
732 Child->MaxPacket0 = 8;\r
e237e7ae 733 } else {\r
92870c98 734 Child->Speed = EFI_USB_SPEED_FULL;\r
735 Child->MaxPacket0 = 8;\r
e237e7ae 736 }\r
737\r
d2577026 738 DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device is of %d speed\n", Child->Speed));\r
e237e7ae 739\r
16d718a5 740 if (((Child->Speed == EFI_USB_SPEED_LOW) || (Child->Speed == EFI_USB_SPEED_FULL)) &&\r
741 (Parent->Speed == EFI_USB_SPEED_HIGH)) {\r
e237e7ae 742 //\r
16d718a5 743 // If the child is a low or full speed device, it is necessary to\r
b4c24e2d 744 // set the transaction translator. Port TT is 1-based.\r
745 // This is quite simple:\r
e237e7ae 746 // 1. if parent is of high speed, then parent is our translator\r
747 // 2. otherwise use parent's translator.\r
748 //\r
16d718a5 749 Child->Translator.TranslatorHubAddress = Parent->Address;\r
750 Child->Translator.TranslatorPortNumber = (UINT8) (Port + 1);\r
751 } else {\r
752 Child->Translator = Parent->Translator;\r
e237e7ae 753 }\r
16d718a5 754 DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device uses translator (%d, %d)\n",\r
755 Child->Translator.TranslatorHubAddress,\r
756 Child->Translator.TranslatorPortNumber));\r
e237e7ae 757\r
758 //\r
759 // After port is reset, hub establishes a signal path between\r
ac644614 760 // the device and host (DEFALUT state). Device's registers are\r
e237e7ae 761 // reset, use default address 0 (host enumerates one device at\r
762 // a time) , and ready to respond to control transfer at EP 0.\r
763 //\r
764\r
e237e7ae 765 //\r
766 // Host assigns an address to the device. Device completes the\r
767 // status stage with default address, then switches to new address.\r
768 // ADDRESS state. Address zero is reserved for root hub.\r
769 //\r
a9292c13 770 ASSERT (Bus->MaxDevices <= 256);\r
771 for (Address = 1; Address < Bus->MaxDevices; Address++) {\r
e237e7ae 772 if (Bus->Devices[Address] == NULL) {\r
773 break;\r
774 }\r
775 }\r
776\r
a9292c13 777 if (Address >= Bus->MaxDevices) {\r
d2577026 778 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: address pool is full for port %d\n", Port));\r
e237e7ae 779\r
780 Status = EFI_ACCESS_DENIED;\r
781 goto ON_ERROR;\r
782 }\r
783\r
92870c98 784 Status = UsbSetAddress (Child, (UINT8)Address);\r
785 Child->Address = (UINT8)Address;\r
e237e7ae 786 Bus->Devices[Address] = Child;\r
e237e7ae 787\r
788 if (EFI_ERROR (Status)) {\r
d2577026 789 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set device address - %r\n", Status));\r
e237e7ae 790 goto ON_ERROR;\r
791 }\r
92870c98 792\r
41e8ff27 793 gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL);\r
e237e7ae 794\r
d2577026 795 DEBUG ((EFI_D_INFO, "UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address));\r
e237e7ae 796\r
92870c98 797 //\r
798 // Host sends a Get_Descriptor request to learn the max packet\r
799 // size of default pipe (only part of the device's descriptor).\r
800 //\r
801 Status = UsbGetMaxPacketSize0 (Child);\r
802\r
803 if (EFI_ERROR (Status)) {\r
804 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status));\r
805 goto ON_ERROR;\r
806 }\r
807\r
808 DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0));\r
809\r
e237e7ae 810 //\r
ac644614 811 // Host learns about the device's abilities by requesting device's\r
e237e7ae 812 // entire descriptions.\r
813 //\r
814 Status = UsbBuildDescTable (Child);\r
815\r
816 if (EFI_ERROR (Status)) {\r
d2577026 817 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status));\r
e237e7ae 818 goto ON_ERROR;\r
819 }\r
820\r
821 //\r
822 // Select a default configuration: UEFI must set the configuration\r
823 // before the driver can connect to the device.\r
824 //\r
825 Config = Child->DevDesc->Configs[0]->Desc.ConfigurationValue;\r
826 Status = UsbSetConfig (Child, Config);\r
827\r
828 if (EFI_ERROR (Status)) {\r
d2577026 829 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status));\r
e237e7ae 830 goto ON_ERROR;\r
831 }\r
832\r
d2577026 833 DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address));\r
e237e7ae 834\r
835 //\r
836 // Host assigns and loads a device driver.\r
837 //\r
838 Status = UsbSelectConfig (Child, Config);\r
839\r
840 if (EFI_ERROR (Status)) {\r
d2577026 841 DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to create interfaces - %r\n", Status));\r
e237e7ae 842 goto ON_ERROR;\r
843 }\r
844\r
37623a5c 845 //\r
846 // Report Status Code to indicate USB device has been detected by hotplug\r
847 //\r
848 REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
849 EFI_PROGRESS_CODE,\r
850 (EFI_IO_BUS_USB | EFI_IOB_PC_HOTPLUG),\r
851 Bus->DevicePath\r
852 );\r
e237e7ae 853 return EFI_SUCCESS;\r
854\r
855ON_ERROR:\r
7a4d52ad 856 //\r
857 // If reach here, it means the enumeration process on a given port is interrupted due to error.\r
858 // The s/w resources, including the assigned address(Address) and the allocated usb device data\r
859 // structure(Bus->Devices[Address]), will NOT be freed here. These resources will be freed when\r
860 // the device is unplugged from the port or DriverBindingStop() is invoked.\r
861 //\r
862 // This way is used to co-work with the lower layer EDKII UHCI/EHCI/XHCI host controller driver.\r
863 // It's mainly because to keep UEFI spec unchanged EDKII XHCI driver have to maintain a state machine\r
864 // to keep track of the mapping between actual address and request address. If the request address\r
865 // (Address) is freed here, the Address value will be used by next enumerated device. Then EDKII XHCI\r
866 // host controller driver will have wrong information, which will cause further transaction error.\r
867 //\r
868 // EDKII UHCI/EHCI doesn't get impacted as it's make sense to reserve s/w resource till it gets unplugged.\r
869 //\r
e237e7ae 870 return Status;\r
871}\r
872\r
873\r
e237e7ae 874/**\r
875 Process the events on the port.\r
876\r
8616fc4c 877 @param HubIf The HUB that has the device connected.\r
878 @param Port The port index of the hub (started with zero).\r
e237e7ae 879\r
8616fc4c 880 @retval EFI_SUCCESS The device is enumerated (added or removed).\r
881 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device.\r
882 @retval Others Failed to enumerate the device.\r
e237e7ae 883\r
884**/\r
e237e7ae 885EFI_STATUS\r
886UsbEnumeratePort (\r
887 IN USB_INTERFACE *HubIf,\r
888 IN UINT8 Port\r
889 )\r
890{\r
891 USB_HUB_API *HubApi;\r
892 USB_DEVICE *Child;\r
893 EFI_USB_PORT_STATUS PortState;\r
894 EFI_STATUS Status;\r
895\r
896 Child = NULL;\r
897 HubApi = HubIf->HubApi;\r
898\r
899 //\r
900 // Host learns of the new device by polling the hub for port changes.\r
901 //\r
902 Status = HubApi->GetPortStatus (HubIf, Port, &PortState);\r
903\r
904 if (EFI_ERROR (Status)) {\r
d2577026 905 DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: failed to get state of port %d\n", Port));\r
e237e7ae 906 return Status;\r
907 }\r
908\r
92870c98 909 //\r
910 // Only handle connection/enable/overcurrent/reset change.\r
911 // Usb super speed hub may report other changes, such as warm reset change. Ignore them.\r
912 //\r
913 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 914 return EFI_SUCCESS;\r
915 }\r
916\r
92870c98 917 DEBUG (( EFI_D_INFO, "UsbEnumeratePort: port %d state - %02x, change - %02x on %p\n",\r
a50f7c4c 918 Port, PortState.PortStatus, PortState.PortChangeStatus, HubIf));\r
e237e7ae 919\r
920 //\r
921 // This driver only process two kinds of events now: over current and\r
922 // connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET.\r
923 // ENABLE/RESET is used to reset port. SUSPEND isn't supported.\r
924 //\r
d1102dba
LG
925\r
926 if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) {\r
e237e7ae 927\r
50fa1b3a 928 if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) {\r
929 //\r
41e8ff27 930 // Case1:\r
d1102dba 931 // Both OverCurrent and OverCurrentChange set, means over current occurs,\r
41e8ff27 932 // which probably is caused by short circuit. It has to wait system hardware\r
933 // to perform recovery.\r
50fa1b3a 934 //\r
d2577026 935 DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: Critical Over Current\n", Port));\r
50fa1b3a 936 return EFI_DEVICE_ERROR;\r
d1102dba
LG
937\r
938 }\r
41e8ff27 939 //\r
940 // Case2:\r
d1102dba 941 // Only OverCurrentChange set, means system has been recoveried from\r
41e8ff27 942 // over current. As a result, all ports are nearly power-off, so\r
d1102dba 943 // it's necessary to detach and enumerate all ports again.\r
41e8ff27 944 //\r
d1102dba 945 DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 2.0 device Recovery Over Current\n", Port));\r
50fa1b3a 946 }\r
e237e7ae 947\r
d1102dba 948 if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) {\r
e237e7ae 949 //\r
41e8ff27 950 // Case3:\r
951 // 1.1 roothub port reg doesn't reflect over-current state, while its counterpart\r
d1102dba 952 // on 2.0 roothub does. When over-current has influence on 1.1 device, the port\r
41e8ff27 953 // would be disabled, so it's also necessary to detach and enumerate again.\r
e237e7ae 954 //\r
d2577026 955 DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 1.1 device Recovery Over Current\n", Port));\r
50fa1b3a 956 }\r
d1102dba 957\r
50fa1b3a 958 if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) {\r
959 //\r
41e8ff27 960 // Case4:\r
d1102dba 961 // Device connected or disconnected normally.\r
50fa1b3a 962 //\r
9a95972e 963 DEBUG ((EFI_D_INFO, "UsbEnumeratePort: Device Connect/Disconnect Normally\n", Port));\r
50fa1b3a 964 }\r
e237e7ae 965\r
d1102dba 966 //\r
41e8ff27 967 // Following as the above cases, it's safety to remove and create again.\r
50fa1b3a 968 //\r
969 Child = UsbFindChild (HubIf, Port);\r
d1102dba 970\r
50fa1b3a 971 if (Child != NULL) {\r
a1b749d0 972 DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device at port %d removed from root hub %p\n", Port, HubIf));\r
50fa1b3a 973 UsbRemoveDevice (Child);\r
e237e7ae 974 }\r
d1102dba 975\r
50fa1b3a 976 if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) {\r
977 //\r
d1102dba 978 // Now, new device connected, enumerate and configure the device\r
50fa1b3a 979 //\r
d2577026 980 DEBUG (( EFI_D_INFO, "UsbEnumeratePort: new device connected at port %d\n", Port));\r
bf4808d6
FT
981 if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET)) {\r
982 Status = UsbEnumerateNewDev (HubIf, Port, FALSE);\r
983 } else {\r
984 Status = UsbEnumerateNewDev (HubIf, Port, TRUE);\r
985 }\r
d1102dba 986\r
50fa1b3a 987 } else {\r
d2577026 988 DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device disconnected event on port %d\n", Port));\r
50fa1b3a 989 }\r
d1102dba 990\r
e237e7ae 991 HubApi->ClearPortChange (HubIf, Port);\r
992 return Status;\r
993}\r
994\r
995\r
996/**\r
8616fc4c 997 Enumerate all the changed hub ports.\r
e237e7ae 998\r
8616fc4c 999 @param Event The event that is triggered.\r
1000 @param Context The context to the event.\r
e237e7ae 1001\r
e237e7ae 1002**/\r
1003VOID\r
8616fc4c 1004EFIAPI\r
e237e7ae 1005UsbHubEnumeration (\r
1006 IN EFI_EVENT Event,\r
1007 IN VOID *Context\r
1008 )\r
1009{\r
1010 USB_INTERFACE *HubIf;\r
1011 UINT8 Byte;\r
1012 UINT8 Bit;\r
1013 UINT8 Index;\r
127884c5 1014 USB_DEVICE *Child;\r
d1102dba 1015\r
ec30be9e 1016 ASSERT (Context != NULL);\r
e237e7ae 1017\r
1018 HubIf = (USB_INTERFACE *) Context;\r
1019\r
127884c5
FT
1020 for (Index = 0; Index < HubIf->NumOfPort; Index++) {\r
1021 Child = UsbFindChild (HubIf, Index);\r
1022 if ((Child != NULL) && (Child->DisconnectFail == TRUE)) {\r
1023 DEBUG (( EFI_D_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from hub %p, try again\n", Index, HubIf));\r
1024 UsbRemoveDevice (Child);\r
1025 }\r
1026 }\r
1027\r
e237e7ae 1028 if (HubIf->ChangeMap == NULL) {\r
1029 return ;\r
1030 }\r
1031\r
1032 //\r
1033 // HUB starts its port index with 1.\r
1034 //\r
1035 Byte = 0;\r
1036 Bit = 1;\r
1037\r
1038 for (Index = 0; Index < HubIf->NumOfPort; Index++) {\r
1039 if (USB_BIT_IS_SET (HubIf->ChangeMap[Byte], USB_BIT (Bit))) {\r
1040 UsbEnumeratePort (HubIf, Index);\r
1041 }\r
1042\r
1043 USB_NEXT_BIT (Byte, Bit);\r
1044 }\r
1045\r
1046 UsbHubAckHubStatus (HubIf->Device);\r
1047\r
1048 gBS->FreePool (HubIf->ChangeMap);\r
1049 HubIf->ChangeMap = NULL;\r
1050 return ;\r
1051}\r
1052\r
1053\r
1054/**\r
8616fc4c 1055 Enumerate all the changed hub ports.\r
e237e7ae 1056\r
8616fc4c 1057 @param Event The event that is triggered.\r
1058 @param Context The context to the event.\r
e237e7ae 1059\r
e237e7ae 1060**/\r
1061VOID\r
eb1f5ab3 1062EFIAPI\r
e237e7ae 1063UsbRootHubEnumeration (\r
1064 IN EFI_EVENT Event,\r
1065 IN VOID *Context\r
1066 )\r
1067{\r
1068 USB_INTERFACE *RootHub;\r
1069 UINT8 Index;\r
127884c5 1070 USB_DEVICE *Child;\r
e237e7ae 1071\r
1072 RootHub = (USB_INTERFACE *) Context;\r
1073\r
1074 for (Index = 0; Index < RootHub->NumOfPort; Index++) {\r
127884c5
FT
1075 Child = UsbFindChild (RootHub, Index);\r
1076 if ((Child != NULL) && (Child->DisconnectFail == TRUE)) {\r
1077 DEBUG (( EFI_D_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from root hub %p, try again\n", Index, RootHub));\r
1078 UsbRemoveDevice (Child);\r
1079 }\r
d1102dba 1080\r
e237e7ae 1081 UsbEnumeratePort (RootHub, Index);\r
1082 }\r
1083}\r