/** @file\r
\r
-Copyright (c) 2007, Intel Corporation\r
-All rights reserved. This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution. The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
-\r
- Module Name:\r
-\r
- UsbEnumer.c\r
-\r
- Abstract:\r
-\r
- Usb bus enumeration support\r
-\r
- Revision History\r
+ Usb bus enumeration support.\r
\r
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
#include "UsbBus.h"\r
\r
-\r
/**\r
- Return the endpoint descriptor in this interface\r
+ Return the endpoint descriptor in this interface.\r
\r
- @param UsbIf The interface to search in\r
- @param EpAddr The address of the endpoint to return\r
+ @param UsbIf The interface to search in.\r
+ @param EpAddr The address of the endpoint to return.\r
\r
- @return The endpoint descriptor or NULL\r
+ @return The endpoint descriptor or NULL.\r
\r
**/\r
USB_ENDPOINT_DESC *\r
)\r
{\r
USB_ENDPOINT_DESC *EpDesc;\r
- UINTN Index;\r
+ UINT8 Index;\r
+ UINT8 NumEndpoints;\r
+\r
+ NumEndpoints = UsbIf->IfSetting->Desc.NumEndpoints;\r
\r
- for (Index = 0; Index < UsbIf->IfSetting->Desc.NumEndpoints; Index++) {\r
+ for (Index = 0; Index < NumEndpoints; Index++) {\r
EpDesc = UsbIf->IfSetting->Endpoints[Index];\r
\r
if (EpDesc->Desc.EndpointAddress == EpAddr) {\r
\r
\r
/**\r
- Free the resource used by USB interface\r
+ Free the resource used by USB interface.\r
\r
- @param UsbIf The USB interface to free\r
-\r
- @return None\r
+ @param UsbIf The USB interface to free.\r
\r
+ @retval EFI_ACCESS_DENIED The interface is still occupied.\r
+ @retval EFI_SUCCESS The interface is freed.\r
**/\r
-STATIC\r
-VOID\r
+EFI_STATUS\r
UsbFreeInterface (\r
IN USB_INTERFACE *UsbIf\r
)\r
{\r
- UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);\r
+ EFI_STATUS Status;\r
\r
- gBS->UninstallMultipleProtocolInterfaces (\r
- UsbIf->Handle,\r
- &gEfiDevicePathProtocolGuid,\r
- UsbIf->DevicePath,\r
- &gEfiUsbIoProtocolGuid,\r
- &UsbIf->UsbIo,\r
- NULL\r
- );\r
+ UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);\r
\r
- if (UsbIf->DevicePath != NULL) {\r
- gBS->FreePool (UsbIf->DevicePath);\r
+ Status = gBS->UninstallMultipleProtocolInterfaces (\r
+ UsbIf->Handle,\r
+ &gEfiDevicePathProtocolGuid, UsbIf->DevicePath,\r
+ &gEfiUsbIoProtocolGuid, &UsbIf->UsbIo,\r
+ NULL\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ if (UsbIf->DevicePath != NULL) {\r
+ FreePool (UsbIf->DevicePath);\r
+ }\r
+ FreePool (UsbIf);\r
+ } else {\r
+ UsbOpenHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);\r
}\r
-\r
- gBS->FreePool (UsbIf);\r
+ return Status;\r
}\r
\r
\r
Create an interface for the descriptor IfDesc. Each\r
device's configuration can have several interfaces.\r
\r
- @param Device The device has the interface descriptor\r
- @param IfDesc The interface descriptor\r
+ @param Device The device has the interface descriptor.\r
+ @param IfDesc The interface descriptor.\r
\r
@return The created USB interface for the descriptor, or NULL.\r
\r
**/\r
-STATIC\r
USB_INTERFACE *\r
UsbCreateInterface (\r
IN USB_DEVICE *Device,\r
UsbIf->Signature = USB_INTERFACE_SIGNATURE;\r
UsbIf->Device = Device;\r
UsbIf->IfDesc = IfDesc;\r
+ ASSERT (IfDesc->ActiveIndex < USB_MAX_INTERFACE_SETTING);\r
UsbIf->IfSetting = IfDesc->Settings[IfDesc->ActiveIndex];\r
- UsbIf->UsbIo = mUsbIoProtocol;\r
+\r
+ CopyMem (\r
+ &(UsbIf->UsbIo),\r
+ &mUsbIoProtocol,\r
+ sizeof (EFI_USB_IO_PROTOCOL)\r
+ );\r
\r
//\r
// Install protocols for USBIO and device path\r
UsbIf->DevicePath = AppendDevicePathNode (HubIf->DevicePath, &UsbNode.Header);\r
\r
if (UsbIf->DevicePath == NULL) {\r
- USB_ERROR (("UsbCreateInterface: failed to create device path\n"));\r
+ DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to create device path\n"));\r
\r
Status = EFI_OUT_OF_RESOURCES;\r
goto ON_ERROR;\r
);\r
\r
if (EFI_ERROR (Status)) {\r
- USB_ERROR (("UsbCreateInterface: failed to install UsbIo - %r\n", Status));\r
+ DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to install UsbIo - %r\n", Status));\r
goto ON_ERROR;\r
}\r
\r
NULL\r
);\r
\r
- USB_ERROR (("UsbCreateInterface: failed to open host for child - %r\n", Status));\r
+ DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to open host for child - %r\n", Status));\r
goto ON_ERROR;\r
}\r
\r
return UsbIf;\r
\r
ON_ERROR:\r
- if (UsbIf->DevicePath) {\r
- gBS->FreePool (UsbIf->DevicePath);\r
+ if (UsbIf->DevicePath != NULL) {\r
+ FreePool (UsbIf->DevicePath);\r
}\r
\r
- gBS->FreePool (UsbIf);\r
+ FreePool (UsbIf);\r
return NULL;\r
}\r
\r
\r
/**\r
- Free the resource used by this USB device\r
-\r
- @param Device The USB device to free\r
+ Free the resource used by this USB device.\r
\r
- @return None\r
+ @param Device The USB device to free.\r
\r
**/\r
-STATIC\r
VOID\r
UsbFreeDevice (\r
IN USB_DEVICE *Device\r
/**\r
Create a device which is on the parent's ParentPort port.\r
\r
- @param ParentIf The parent HUB interface\r
- @param ParentPort The port on the HUB this device is connected to\r
+ @param ParentIf The parent HUB interface.\r
+ @param ParentPort The port on the HUB this device is connected to.\r
\r
- @return Created USB device\r
+ @return Created USB device, Or NULL.\r
\r
**/\r
-STATIC\r
USB_DEVICE *\r
UsbCreateDevice (\r
IN USB_INTERFACE *ParentIf,\r
Device->ParentAddr = ParentIf->Device->Address;\r
Device->ParentIf = ParentIf;\r
Device->ParentPort = ParentPort;\r
+ Device->Tier = (UINT8)(ParentIf->Device->Tier + 1);\r
return Device;\r
}\r
\r
\r
/**\r
Connect the USB interface with its driver. EFI USB bus will\r
- create a USB interface for each seperate interface descriptor.\r
+ create a USB interface for each separate interface descriptor.\r
\r
- @param UsbIf The interface to connect driver to\r
+ @param UsbIf The interface to connect driver to.\r
\r
- @return EFI_SUCCESS : Interface is managed by some driver\r
- @return Others : Failed to locate a driver for this interface\r
+ @return EFI_SUCCESS Interface is managed by some driver.\r
+ @return Others Failed to locate a driver for this interface.\r
\r
**/\r
-STATIC\r
EFI_STATUS\r
UsbConnectDriver (\r
IN USB_INTERFACE *UsbIf\r
// connect drivers with this interface\r
//\r
if (UsbIsHubInterface (UsbIf)) {\r
- USB_DEBUG (("UsbConnectDriver: found a hub device\n"));\r
+ DEBUG ((EFI_D_INFO, "UsbConnectDriver: found a hub device\n"));\r
Status = mUsbHubApi.Init (UsbIf);\r
\r
} else {\r
// twisted TPL used. It should be no problem for us to connect\r
// or disconnect at CALLBACK.\r
//\r
- OldTpl = UsbGetCurrentTpl ();\r
- USB_DEBUG (("UsbConnectDriver: TPL before connect is %d\n", OldTpl));\r
\r
- gBS->RestoreTPL (TPL_CALLBACK);\r
+ //\r
+ // Only recursively wanted usb child device\r
+ //\r
+ if (UsbBusIsWantedUsbIO (UsbIf->Device->Bus, UsbIf)) {\r
+ OldTpl = UsbGetCurrentTpl ();\r
+ DEBUG ((EFI_D_INFO, "UsbConnectDriver: TPL before connect is %d, %p\n", (UINT32)OldTpl, UsbIf->Handle));\r
\r
- Status = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE);\r
- UsbIf->IsManaged = (BOOLEAN)!EFI_ERROR (Status);\r
+ gBS->RestoreTPL (TPL_CALLBACK);\r
\r
- USB_DEBUG (("UsbConnectDriver: TPL after connect is %d\n", UsbGetCurrentTpl()));\r
- ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);\r
+ Status = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE);\r
+ UsbIf->IsManaged = (BOOLEAN)!EFI_ERROR (Status);\r
\r
- gBS->RaiseTPL (OldTpl);\r
+ DEBUG ((EFI_D_INFO, "UsbConnectDriver: TPL after connect is %d\n", (UINT32)UsbGetCurrentTpl()));\r
+ ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);\r
+\r
+ gBS->RaiseTPL (OldTpl);\r
+ }\r
}\r
\r
return Status;\r
settings. Only one setting is active. It will\r
also reset its endpoints' toggle to zero.\r
\r
- @param IfDesc The interface descriptor to set\r
- @param Alternate The alternate setting number to locate\r
+ @param IfDesc The interface descriptor to set.\r
+ @param Alternate The alternate setting number to locate.\r
\r
- @retval EFI_NOT_FOUND There is no setting with this alternate index\r
+ @retval EFI_NOT_FOUND There is no setting with this alternate index.\r
@retval EFI_SUCCESS The interface is set to Alternate setting.\r
\r
**/\r
)\r
{\r
USB_INTERFACE_SETTING *Setting;\r
- UINT8 Index;\r
+ UINTN Index;\r
\r
//\r
// Locate the active alternate setting\r
Setting = NULL;\r
\r
for (Index = 0; Index < IfDesc->NumOfSetting; Index++) {\r
+ ASSERT (Index < USB_MAX_INTERFACE_SETTING);\r
Setting = IfDesc->Settings[Index];\r
\r
if (Setting->Desc.AlternateSetting == Alternate) {\r
\r
IfDesc->ActiveIndex = Index;\r
\r
- USB_DEBUG (("UsbSelectSetting: setting %d selected for interface %d\n",\r
+ ASSERT (Setting != NULL);\r
+ DEBUG ((EFI_D_INFO, "UsbSelectSetting: setting %d selected for interface %d\n",\r
Alternate, Setting->Desc.InterfaceNumber));\r
\r
//\r
Select a new configuration for the device. Each\r
device may support several configurations.\r
\r
- @param Device The device to select configuration\r
- @param ConfigValue The index of the configuration ( != 0)\r
+ @param Device The device to select configuration.\r
+ @param ConfigValue The index of the configuration ( != 0).\r
\r
- @retval EFI_NOT_FOUND There is no configuration with the index\r
- @retval EFI_OUT_OF_RESOURCES Failed to allocate resource\r
+ @retval EFI_NOT_FOUND There is no configuration with the index.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.\r
@retval EFI_SUCCESS The configuration is selected.\r
\r
**/\r
\r
Device->ActiveConfig = ConfigDesc;\r
\r
- USB_DEBUG (("UsbSelectConfig: config %d selected for device %d\n",\r
+ DEBUG ((EFI_D_INFO, "UsbSelectConfig: config %d selected for device %d\n",\r
ConfigValue, Device->Address));\r
\r
//\r
UsbIf = UsbCreateInterface (Device, ConfigDesc->Interfaces[Index]);\r
\r
if (UsbIf == NULL) {\r
+ Device->NumOfInterface = Index;\r
return EFI_OUT_OF_RESOURCES;\r
}\r
\r
+ ASSERT (Index < USB_MAX_INTERFACE);\r
Device->Interfaces[Index] = UsbIf;\r
\r
//\r
Status = UsbConnectDriver (UsbIf);\r
\r
if (EFI_ERROR (Status)) {\r
- USB_ERROR (("UsbSelectConfig: failed to connect driver %r, ignored\n", Status));\r
+ DEBUG ((\r
+ DEBUG_WARN,\r
+ "UsbSelectConfig: failed to connect driver %r, ignored\n",\r
+ Status\r
+ ));\r
}\r
}\r
\r
}\r
\r
\r
-\r
/**\r
Disconnect the USB interface with its driver.\r
\r
- @param UsbIf The interface to disconnect driver from\r
-\r
- @return None\r
+ @param UsbIf The interface to disconnect driver from.\r
\r
**/\r
-STATIC\r
-VOID\r
+EFI_STATUS\r
UsbDisconnectDriver (\r
IN USB_INTERFACE *UsbIf\r
)\r
{\r
EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
\r
//\r
// Release the hub if it's a hub controller, otherwise\r
// disconnect the driver if it is managed by other drivers.\r
//\r
+ Status = EFI_SUCCESS;\r
if (UsbIf->IsHub) {\r
- UsbIf->HubApi->Release (UsbIf);\r
+ Status = UsbIf->HubApi->Release (UsbIf);\r
\r
} else if (UsbIf->IsManaged) {\r
//\r
// or disconnect at CALLBACK.\r
//\r
OldTpl = UsbGetCurrentTpl ();\r
- USB_DEBUG (("UsbDisconnectDriver: old TPL is %d\n", OldTpl));\r
+ DEBUG ((EFI_D_INFO, "UsbDisconnectDriver: old TPL is %d, %p\n", (UINT32)OldTpl, UsbIf->Handle));\r
\r
gBS->RestoreTPL (TPL_CALLBACK);\r
\r
- gBS->DisconnectController (UsbIf->Handle, NULL, NULL);\r
- UsbIf->IsManaged = FALSE;\r
+ Status = gBS->DisconnectController (UsbIf->Handle, NULL, NULL);\r
+ if (!EFI_ERROR (Status)) {\r
+ UsbIf->IsManaged = FALSE;\r
+ }\r
\r
- USB_DEBUG (("UsbDisconnectDriver: TPL after disconnect is %d\n", UsbGetCurrentTpl()));\r
+ DEBUG (( EFI_D_INFO, "UsbDisconnectDriver: TPL after disconnect is %d, %d\n", (UINT32)UsbGetCurrentTpl(), Status));\r
ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);\r
\r
gBS->RaiseTPL (OldTpl);\r
}\r
-}\r
\r
+ return Status;\r
+}\r
\r
\r
/**\r
- Remove the current device configuration\r
-\r
- @param Device The USB device to remove configuration from\r
+ Remove the current device configuration.\r
\r
- @return None\r
+ @param Device The USB device to remove configuration from.\r
\r
**/\r
-VOID\r
+EFI_STATUS\r
UsbRemoveConfig (\r
IN USB_DEVICE *Device\r
)\r
{\r
USB_INTERFACE *UsbIf;\r
UINTN Index;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
\r
//\r
// Remove each interface of the device\r
//\r
+ ReturnStatus = EFI_SUCCESS;\r
for (Index = 0; Index < Device->NumOfInterface; Index++) {\r
+ ASSERT (Index < USB_MAX_INTERFACE);\r
UsbIf = Device->Interfaces[Index];\r
\r
if (UsbIf == NULL) {\r
continue;\r
}\r
\r
- UsbDisconnectDriver (UsbIf);\r
- UsbFreeInterface (UsbIf);\r
- Device->Interfaces[Index] = NULL;\r
+ Status = UsbDisconnectDriver (UsbIf);\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = UsbFreeInterface (UsbIf);\r
+ if (EFI_ERROR (Status)) {\r
+ UsbConnectDriver (UsbIf);\r
+ }\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ Device->Interfaces[Index] = NULL;\r
+ } else {\r
+ ReturnStatus = Status;\r
+ }\r
}\r
\r
Device->ActiveConfig = NULL;\r
- Device->NumOfInterface = 0;\r
+ return ReturnStatus;\r
}\r
\r
\r
-\r
/**\r
Remove the device and all its children from the bus.\r
\r
- @param Device The device to remove\r
+ @param Device The device to remove.\r
\r
- @retval EFI_SUCCESS The device is removed\r
+ @retval EFI_SUCCESS The device is removed.\r
\r
**/\r
EFI_STATUS\r
USB_BUS *Bus;\r
USB_DEVICE *Child;\r
EFI_STATUS Status;\r
- UINT8 Index;\r
+ EFI_STATUS ReturnStatus;\r
+ UINTN Index;\r
\r
Bus = Device->Bus;\r
\r
// Remove all the devices on its downstream ports. Search from devices[1].\r
// Devices[0] is the root hub.\r
//\r
- for (Index = 1; Index < USB_MAX_DEVICES; Index++) {\r
+ ReturnStatus = EFI_SUCCESS;\r
+ for (Index = 1; Index < Bus->MaxDevices; Index++) {\r
Child = Bus->Devices[Index];\r
\r
if ((Child == NULL) || (Child->ParentAddr != Device->Address)) {\r
\r
Status = UsbRemoveDevice (Child);\r
\r
- if (EFI_ERROR (Status)) {\r
- USB_ERROR (("UsbRemoveDevice: failed to remove child, ignore error\n"));\r
+ if (!EFI_ERROR (Status)) {\r
Bus->Devices[Index] = NULL;\r
+ } else {\r
+ Bus->Devices[Index]->DisconnectFail = TRUE;\r
+ ReturnStatus = Status;\r
+ DEBUG ((EFI_D_INFO, "UsbRemoveDevice: failed to remove child %p at parent %p\n", Child, Device));\r
}\r
}\r
\r
- UsbRemoveConfig (Device);\r
+ if (EFI_ERROR (ReturnStatus)) {\r
+ return ReturnStatus;\r
+ }\r
\r
- USB_DEBUG (("UsbRemoveDevice: device %d removed\n", Device->Address));\r
+ Status = UsbRemoveConfig (Device);\r
\r
- Bus->Devices[Device->Address] = NULL;\r
- UsbFreeDevice (Device);\r
+ if (!EFI_ERROR (Status)) {\r
+ DEBUG (( EFI_D_INFO, "UsbRemoveDevice: device %d removed\n", Device->Address));\r
\r
- return EFI_SUCCESS;\r
+ ASSERT (Device->Address < Bus->MaxDevices);\r
+ Bus->Devices[Device->Address] = NULL;\r
+ UsbFreeDevice (Device);\r
+ } else {\r
+ Bus->Devices[Device->Address]->DisconnectFail = TRUE;\r
+ }\r
+ return Status;\r
}\r
\r
\r
/**\r
- Find the child device on the hub's port\r
+ Find the child device on the hub's port.\r
\r
- @param HubIf The hub interface\r
- @param Port The port of the hub this child is connected to\r
+ @param HubIf The hub interface.\r
+ @param Port The port of the hub this child is connected to.\r
\r
- @return The device on the hub's port, or NULL if there is none\r
+ @return The device on the hub's port, or NULL if there is none.\r
\r
**/\r
-STATIC\r
USB_DEVICE *\r
UsbFindChild (\r
IN USB_INTERFACE *HubIf,\r
//\r
// Start checking from device 1, device 0 is the root hub\r
//\r
- for (Index = 1; Index < USB_MAX_DEVICES; Index++) {\r
+ for (Index = 1; Index < Bus->MaxDevices; Index++) {\r
Device = Bus->Devices[Index];\r
\r
if ((Device != NULL) && (Device->ParentAddr == HubIf->Device->Address) &&\r
}\r
\r
\r
-\r
/**\r
Enumerate and configure the new device on the port of this HUB interface.\r
\r
- @param HubIf The HUB that has the device connected\r
- @param Port The port index of the hub (started with zero)\r
+ @param HubIf The HUB that has the device connected.\r
+ @param Port The port index of the hub (started with zero).\r
+ @param ResetIsNeeded The boolean to control whether skip the reset of the port.\r
\r
- @retval EFI_SUCCESS The device is enumerated (added or removed)\r
- @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device\r
- @retval Others Failed to enumerate the device\r
+ @retval EFI_SUCCESS The device is enumerated (added or removed).\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device.\r
+ @retval Others Failed to enumerate the device.\r
\r
**/\r
-STATIC\r
EFI_STATUS\r
UsbEnumerateNewDev (\r
IN USB_INTERFACE *HubIf,\r
- IN UINT8 Port\r
+ IN UINT8 Port,\r
+ IN BOOLEAN ResetIsNeeded\r
)\r
{\r
USB_BUS *Bus;\r
USB_DEVICE *Child;\r
USB_DEVICE *Parent;\r
EFI_USB_PORT_STATUS PortState;\r
- UINT8 Address;\r
+ UINTN Address;\r
UINT8 Config;\r
EFI_STATUS Status;\r
\r
- Address = USB_MAX_DEVICES;\r
Parent = HubIf->Device;\r
Bus = Parent->Bus;\r
HubApi = HubIf->HubApi;\r
+ Address = Bus->MaxDevices;\r
\r
-\r
- //\r
- // Wait at least 100 ms for the power on port to stable\r
- //\r
- gBS->Stall (100 * USB_STALL_1_MS);\r
+ gBS->Stall (USB_WAIT_PORT_STABLE_STALL);\r
\r
//\r
// Hub resets the device for at least 10 milliseconds.\r
// and the hub is a EHCI root hub, ResetPort will release\r
// the device to its companion UHCI and return an error.\r
//\r
- Status = HubApi->ResetPort (HubIf, Port);\r
-\r
- if (EFI_ERROR (Status)) {\r
- USB_ERROR (("UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status));\r
+ if (ResetIsNeeded) {\r
+ Status = HubApi->ResetPort (HubIf, Port);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status));\r
\r
- return Status;\r
+ return Status;\r
+ }\r
+ DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: hub port %d is reset\n", Port));\r
+ } else {\r
+ DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: hub port %d reset is skipped\n", Port));\r
}\r
\r
- USB_DEBUG (("UsbEnumerateNewDev: hub port %d is reset\n", Port));\r
-\r
Child = UsbCreateDevice (HubIf, Port);\r
\r
if (Child == NULL) {\r
Status = HubApi->GetPortStatus (HubIf, Port, &PortState);\r
\r
if (EFI_ERROR (Status)) {\r
- USB_ERROR (("UsbEnumerateNewDev: failed to get speed of port %d\n", Port));\r
+ DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get speed of port %d\n", Port));\r
goto ON_ERROR;\r
}\r
\r
- if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) {\r
- Child->Speed = EFI_USB_SPEED_LOW;\r
-\r
+ if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) {\r
+ DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: No device present at port %d\n", Port));\r
+ goto ON_ERROR;\r
+ } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_SUPER_SPEED)){\r
+ Child->Speed = EFI_USB_SPEED_SUPER;\r
+ Child->MaxPacket0 = 512;\r
} else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) {\r
- Child->Speed = EFI_USB_SPEED_HIGH;\r
-\r
+ Child->Speed = EFI_USB_SPEED_HIGH;\r
+ Child->MaxPacket0 = 64;\r
+ } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) {\r
+ Child->Speed = EFI_USB_SPEED_LOW;\r
+ Child->MaxPacket0 = 8;\r
} else {\r
- Child->Speed = EFI_USB_SPEED_FULL;\r
+ Child->Speed = EFI_USB_SPEED_FULL;\r
+ Child->MaxPacket0 = 8;\r
}\r
\r
- USB_DEBUG (("UsbEnumerateNewDev: device is of %d speed\n", Child->Speed));\r
+ DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device is of %d speed\n", Child->Speed));\r
\r
- if (Child->Speed != EFI_USB_SPEED_HIGH) {\r
+ if (((Child->Speed == EFI_USB_SPEED_LOW) || (Child->Speed == EFI_USB_SPEED_FULL)) &&\r
+ (Parent->Speed == EFI_USB_SPEED_HIGH)) {\r
//\r
- // If the child isn't a high speed device, it is necessary to\r
- // set the transaction translator. This is quite simple:\r
+ // If the child is a low or full speed device, it is necessary to\r
+ // set the transaction translator. Port TT is 1-based.\r
+ // This is quite simple:\r
// 1. if parent is of high speed, then parent is our translator\r
// 2. otherwise use parent's translator.\r
//\r
- if (Parent->Speed == EFI_USB_SPEED_HIGH) {\r
- Child->Translator.TranslatorHubAddress = Parent->Address;\r
- Child->Translator.TranslatorPortNumber = Port;\r
-\r
- } else {\r
- Child->Translator = Parent->Translator;\r
- }\r
-\r
- USB_DEBUG (("UsbEnumerateNewDev: device uses translator (%d, %d)\n",\r
- Child->Translator.TranslatorHubAddress,\r
- Child->Translator.TranslatorPortNumber));\r
+ Child->Translator.TranslatorHubAddress = Parent->Address;\r
+ Child->Translator.TranslatorPortNumber = (UINT8) (Port + 1);\r
+ } else {\r
+ Child->Translator = Parent->Translator;\r
}\r
+ DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device uses translator (%d, %d)\n",\r
+ Child->Translator.TranslatorHubAddress,\r
+ Child->Translator.TranslatorPortNumber));\r
\r
//\r
// After port is reset, hub establishes a signal path between\r
- // the device and host (DEFALUT state). Device¡¯s registers are\r
+ // the device and host (DEFALUT state). Device's registers are\r
// reset, use default address 0 (host enumerates one device at\r
// a time) , and ready to respond to control transfer at EP 0.\r
//\r
\r
- //\r
- // Host sends a Get_Descriptor request to learn the max packet\r
- // size of default pipe (only part of the device¡¯s descriptor).\r
- //\r
- Status = UsbGetMaxPacketSize0 (Child);\r
-\r
- if (EFI_ERROR (Status)) {\r
- USB_ERROR (("UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status));\r
- goto ON_ERROR;\r
- }\r
-\r
- USB_DEBUG (("UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0));\r
-\r
//\r
// Host assigns an address to the device. Device completes the\r
// status stage with default address, then switches to new address.\r
// ADDRESS state. Address zero is reserved for root hub.\r
//\r
- for (Address = 1; Address < USB_MAX_DEVICES; Address++) {\r
+ ASSERT (Bus->MaxDevices <= 256);\r
+ for (Address = 1; Address < Bus->MaxDevices; Address++) {\r
if (Bus->Devices[Address] == NULL) {\r
break;\r
}\r
}\r
\r
- if (Address == USB_MAX_DEVICES) {\r
- USB_ERROR (("UsbEnumerateNewDev: address pool is full for port %d\n", Port));\r
+ if (Address >= Bus->MaxDevices) {\r
+ DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: address pool is full for port %d\n", Port));\r
\r
Status = EFI_ACCESS_DENIED;\r
goto ON_ERROR;\r
}\r
\r
+ Status = UsbSetAddress (Child, (UINT8)Address);\r
+ Child->Address = (UINT8)Address;\r
Bus->Devices[Address] = Child;\r
- Status = UsbSetAddress (Child, Address);\r
- Child->Address = Address;\r
\r
if (EFI_ERROR (Status)) {\r
- USB_ERROR (("UsbEnumerateNewDev: failed to set device address - %r\n", Status));\r
+ DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set device address - %r\n", Status));\r
goto ON_ERROR;\r
}\r
\r
+ gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL);\r
+\r
+ DEBUG ((EFI_D_INFO, "UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address));\r
+\r
//\r
- // Wait 20ms for set address to complete\r
+ // Host sends a Get_Descriptor request to learn the max packet\r
+ // size of default pipe (only part of the device's descriptor).\r
//\r
- gBS->Stall (20 * USB_STALL_1_MS);\r
+ Status = UsbGetMaxPacketSize0 (Child);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status));\r
+ goto ON_ERROR;\r
+ }\r
\r
- USB_DEBUG (("UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address));\r
+ DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0));\r
\r
//\r
- // Host learns about the device¡¯s abilities by requesting device's\r
+ // Host learns about the device's abilities by requesting device's\r
// entire descriptions.\r
//\r
Status = UsbBuildDescTable (Child);\r
\r
if (EFI_ERROR (Status)) {\r
- USB_ERROR (("UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status));\r
+ DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status));\r
goto ON_ERROR;\r
}\r
\r
Status = UsbSetConfig (Child, Config);\r
\r
if (EFI_ERROR (Status)) {\r
- USB_ERROR (("UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status));\r
+ DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status));\r
goto ON_ERROR;\r
}\r
\r
- USB_DEBUG (("UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address));\r
+ DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address));\r
\r
//\r
// Host assigns and loads a device driver.\r
Status = UsbSelectConfig (Child, Config);\r
\r
if (EFI_ERROR (Status)) {\r
- USB_ERROR (("UsbEnumerateNewDev: failed to create interfaces - %r\n", Status));\r
+ DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to create interfaces - %r\n", Status));\r
goto ON_ERROR;\r
}\r
\r
+ //\r
+ // Report Status Code to indicate USB device has been detected by hotplug\r
+ //\r
+ REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+ EFI_PROGRESS_CODE,\r
+ (EFI_IO_BUS_USB | EFI_IOB_PC_HOTPLUG),\r
+ Bus->DevicePath\r
+ );\r
return EFI_SUCCESS;\r
\r
ON_ERROR:\r
- if (Address != USB_MAX_DEVICES) {\r
- Bus->Devices[Address] = NULL;\r
- }\r
-\r
- if (Child != NULL) {\r
- UsbFreeDevice (Child);\r
- }\r
-\r
+ //\r
+ // If reach here, it means the enumeration process on a given port is interrupted due to error.\r
+ // The s/w resources, including the assigned address(Address) and the allocated usb device data\r
+ // structure(Bus->Devices[Address]), will NOT be freed here. These resources will be freed when\r
+ // the device is unplugged from the port or DriverBindingStop() is invoked.\r
+ //\r
+ // This way is used to co-work with the lower layer EDKII UHCI/EHCI/XHCI host controller driver.\r
+ // It's mainly because to keep UEFI spec unchanged EDKII XHCI driver have to maintain a state machine\r
+ // to keep track of the mapping between actual address and request address. If the request address\r
+ // (Address) is freed here, the Address value will be used by next enumerated device. Then EDKII XHCI\r
+ // host controller driver will have wrong information, which will cause further transaction error.\r
+ //\r
+ // EDKII UHCI/EHCI doesn't get impacted as it's make sense to reserve s/w resource till it gets unplugged.\r
+ //\r
return Status;\r
}\r
\r
\r
-\r
/**\r
Process the events on the port.\r
\r
- @param HubIf The HUB that has the device connected\r
- @param Port The port index of the hub (started with zero)\r
+ @param HubIf The HUB that has the device connected.\r
+ @param Port The port index of the hub (started with zero).\r
\r
- @retval EFI_SUCCESS The device is enumerated (added or removed)\r
- @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device\r
- @retval Others Failed to enumerate the device\r
+ @retval EFI_SUCCESS The device is enumerated (added or removed).\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device.\r
+ @retval Others Failed to enumerate the device.\r
\r
**/\r
-STATIC\r
EFI_STATUS\r
UsbEnumeratePort (\r
IN USB_INTERFACE *HubIf,\r
Status = HubApi->GetPortStatus (HubIf, Port, &PortState);\r
\r
if (EFI_ERROR (Status)) {\r
- USB_ERROR (("UsbEnumeratePort: failed to get state of port %d\n", Port));\r
+ DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: failed to get state of port %d\n", Port));\r
return Status;\r
}\r
\r
- if (PortState.PortChangeStatus == 0) {\r
+ //\r
+ // Only handle connection/enable/overcurrent/reset change.\r
+ // Usb super speed hub may report other changes, such as warm reset change. Ignore them.\r
+ //\r
+ 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
return EFI_SUCCESS;\r
}\r
\r
- USB_DEBUG (("UsbEnumeratePort: port %d state - %x, change - %x\n",\r
- Port, PortState.PortStatus, PortState.PortChangeStatus));\r
+ DEBUG (( EFI_D_INFO, "UsbEnumeratePort: port %d state - %02x, change - %02x on %p\n",\r
+ Port, PortState.PortStatus, PortState.PortChangeStatus, HubIf));\r
\r
//\r
// This driver only process two kinds of events now: over current and\r
// connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET.\r
// ENABLE/RESET is used to reset port. SUSPEND isn't supported.\r
//\r
- Status = EFI_SUCCESS;\r
\r
if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) {\r
+\r
+ if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) {\r
+ //\r
+ // Case1:\r
+ // Both OverCurrent and OverCurrentChange set, means over current occurs,\r
+ // which probably is caused by short circuit. It has to wait system hardware\r
+ // to perform recovery.\r
+ //\r
+ DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: Critical Over Current\n", Port));\r
+ return EFI_DEVICE_ERROR;\r
+\r
+ }\r
//\r
- // If overcurrent condition is cleared, enable the port again\r
+ // Case2:\r
+ // Only OverCurrentChange set, means system has been recoveried from\r
+ // over current. As a result, all ports are nearly power-off, so\r
+ // it's necessary to detach and enumerate all ports again.\r
//\r
- if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) {\r
- HubApi->SetPortFeature (HubIf, Port, USB_HUB_PORT_POWER);\r
- }\r
+ DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 2.0 device Recovery Over Current\n", Port));\r
+ }\r
\r
- } else if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) {\r
+ if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) {\r
//\r
- // Device connected or disconnected. Either way, if there is\r
- // already a device present in the bus, need to remove it.\r
+ // Case3:\r
+ // 1.1 roothub port reg doesn't reflect over-current state, while its counterpart\r
+ // on 2.0 roothub does. When over-current has influence on 1.1 device, the port\r
+ // would be disabled, so it's also necessary to detach and enumerate again.\r
//\r
- Child = UsbFindChild (HubIf, Port);\r
+ DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 1.1 device Recovery Over Current\n", Port));\r
+ }\r
\r
- if (Child != NULL) {\r
- USB_DEBUG (("UsbEnumeratePort: device at port %d removed from system\n", Port));\r
- UsbRemoveDevice (Child);\r
- }\r
+ if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) {\r
+ //\r
+ // Case4:\r
+ // Device connected or disconnected normally.\r
+ //\r
+ DEBUG ((EFI_D_INFO, "UsbEnumeratePort: Device Connect/Disconnect Normally\n", Port));\r
+ }\r
\r
- if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) {\r
- //\r
- // Now, new device connected, enumerate and configure the device\r
- //\r
- USB_DEBUG (("UsbEnumeratePort: new device connected at port %d\n", Port));\r
- Status = UsbEnumerateNewDev (HubIf, Port);\r
+ //\r
+ // Following as the above cases, it's safety to remove and create again.\r
+ //\r
+ Child = UsbFindChild (HubIf, Port);\r
\r
+ if (Child != NULL) {\r
+ DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device at port %d removed from root hub %p\n", Port, HubIf));\r
+ UsbRemoveDevice (Child);\r
+ }\r
+\r
+ if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) {\r
+ //\r
+ // Now, new device connected, enumerate and configure the device\r
+ //\r
+ DEBUG (( EFI_D_INFO, "UsbEnumeratePort: new device connected at port %d\n", Port));\r
+ if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET)) {\r
+ Status = UsbEnumerateNewDev (HubIf, Port, FALSE);\r
} else {\r
- USB_DEBUG (("UsbEnumeratePort: device disconnected event on port %d\n", Port));\r
+ Status = UsbEnumerateNewDev (HubIf, Port, TRUE);\r
}\r
+\r
+ } else {\r
+ DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device disconnected event on port %d\n", Port));\r
}\r
\r
HubApi->ClearPortChange (HubIf, Port);\r
\r
\r
/**\r
- Enumerate all the changed hub ports\r
-\r
- @param Event The event that is triggered\r
- @param Context The context to the event\r
+ Enumerate all the changed hub ports.\r
\r
- @return None\r
+ @param Event The event that is triggered.\r
+ @param Context The context to the event.\r
\r
**/\r
VOID\r
+EFIAPI\r
UsbHubEnumeration (\r
IN EFI_EVENT Event,\r
IN VOID *Context\r
UINT8 Byte;\r
UINT8 Bit;\r
UINT8 Index;\r
+ USB_DEVICE *Child;\r
\r
- ASSERT (Context);\r
+ ASSERT (Context != NULL);\r
\r
HubIf = (USB_INTERFACE *) Context;\r
\r
+ for (Index = 0; Index < HubIf->NumOfPort; Index++) {\r
+ Child = UsbFindChild (HubIf, Index);\r
+ if ((Child != NULL) && (Child->DisconnectFail == TRUE)) {\r
+ DEBUG (( EFI_D_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from hub %p, try again\n", Index, HubIf));\r
+ UsbRemoveDevice (Child);\r
+ }\r
+ }\r
+\r
if (HubIf->ChangeMap == NULL) {\r
return ;\r
}\r
\r
\r
/**\r
- Enumerate all the changed hub ports\r
-\r
- @param Event The event that is triggered\r
- @param Context The context to the event\r
+ Enumerate all the changed hub ports.\r
\r
- @return None\r
+ @param Event The event that is triggered.\r
+ @param Context The context to the event.\r
\r
**/\r
VOID\r
+EFIAPI\r
UsbRootHubEnumeration (\r
IN EFI_EVENT Event,\r
IN VOID *Context\r
{\r
USB_INTERFACE *RootHub;\r
UINT8 Index;\r
+ USB_DEVICE *Child;\r
\r
RootHub = (USB_INTERFACE *) Context;\r
\r
for (Index = 0; Index < RootHub->NumOfPort; Index++) {\r
+ Child = UsbFindChild (RootHub, Index);\r
+ if ((Child != NULL) && (Child->DisconnectFail == TRUE)) {\r
+ DEBUG (( EFI_D_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from root hub %p, try again\n", Index, RootHub));\r
+ UsbRemoveDevice (Child);\r
+ }\r
+\r
UsbEnumeratePort (RootHub, Index);\r
}\r
}\r