summaryrefslogtreecommitdiff
path: root/EdkModulePkg/Bus/Usb/UsbBus/Dxe/usbbus.c
diff options
context:
space:
mode:
Diffstat (limited to 'EdkModulePkg/Bus/Usb/UsbBus/Dxe/usbbus.c')
-rw-r--r--EdkModulePkg/Bus/Usb/UsbBus/Dxe/usbbus.c2305
1 files changed, 2305 insertions, 0 deletions
diff --git a/EdkModulePkg/Bus/Usb/UsbBus/Dxe/usbbus.c b/EdkModulePkg/Bus/Usb/UsbBus/Dxe/usbbus.c
new file mode 100644
index 0000000000..f4ac69e13f
--- /dev/null
+++ b/EdkModulePkg/Bus/Usb/UsbBus/Dxe/usbbus.c
@@ -0,0 +1,2305 @@
+/*++
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ Module Name:
+
+ UsbBus.c
+
+ Abstract:
+
+ USB Bus Driver
+
+ Revision History
+
+--*/
+
+#include "usbbus.h"
+
+//#ifdef EFI_DEBUG
+UINTN gUSBDebugLevel = EFI_D_ERROR;
+UINTN gUSBErrorLevel = EFI_D_ERROR;
+//#endif
+//
+// The UsbBusProtocol is just used to locate USB_BUS_CONTROLLER
+// structure in the UsbBusDriverControllerDriverStop(). Then we can
+// Close all opened protocols and release this structure.
+//
+STATIC EFI_GUID mUsbBusProtocolGuid = EFI_USB_BUS_PROTOCOL_GUID;
+
+
+
+//
+// EFI_DRIVER_BINDING_PROTOCOL Protocol Interface
+//
+EFI_STATUS
+EFIAPI
+UsbBusControllerDriverSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+EFIAPI
+UsbBusControllerDriverStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+EFIAPI
+UsbBusControllerDriverStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+EFI_DRIVER_BINDING_PROTOCOL gUsbBusDriverBinding = {
+ UsbBusControllerDriverSupported,
+ UsbBusControllerDriverStart,
+ UsbBusControllerDriverStop,
+ 0x10,
+ NULL,
+ NULL
+};
+
+//
+// Internal use only
+//
+STATIC
+EFI_STATUS
+ReportUsbStatusCode (
+ IN USB_BUS_CONTROLLER_DEVICE *UsbBusController,
+ IN EFI_STATUS_CODE_TYPE Type,
+ IN EFI_STATUS_CODE_VALUE Code
+ );
+
+//
+// Supported function
+//
+VOID
+InitializeUsbIoInstance (
+ IN USB_IO_CONTROLLER_DEVICE *UsbIoController
+ );
+
+STATIC
+USB_IO_CONTROLLER_DEVICE *
+CreateUsbIoControllerDevice (
+ VOID
+ );
+
+STATIC
+EFI_STATUS
+InitUsbIoController (
+ IN USB_IO_CONTROLLER_DEVICE *UsbIoController
+ );
+
+//
+// USB Device Configuration / Deconfiguration
+//
+STATIC
+EFI_STATUS
+UsbDeviceConfiguration (
+ IN USB_IO_CONTROLLER_DEVICE *ParentHubController,
+ IN EFI_HANDLE HostController,
+ IN UINT8 ParentPort,
+ IN USB_IO_DEVICE *UsbIoDevice
+ );
+
+//
+// Usb Bus enumeration function
+//
+STATIC
+VOID
+EFIAPI
+UsbEnumeration (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+EFI_STATUS
+ResetRootPort (
+ IN EFI_USB_HC_PROTOCOL *UsbHCInterface,
+ IN UINT8 PortNum,
+ IN UINT8 RetryTimes
+ );
+
+EFI_STATUS
+ResetHubPort (
+ IN USB_IO_CONTROLLER_DEVICE *UsbIoController,
+ IN UINT8 PortIndex
+ );
+
+EFI_STATUS
+ClearRootPortConnectionChangeStatus (
+ IN UINT8 PortNum,
+ IN EFI_USB_HC_PROTOCOL *UsbHCInterface
+ );
+
+STATIC
+EFI_STATUS
+ParentPortReset (
+ IN USB_IO_CONTROLLER_DEVICE *UsbIoController,
+ IN BOOLEAN ReConfigure,
+ IN UINT8 RetryTimes
+ );
+
+//
+// Following are address allocate and free functions
+//
+STATIC
+UINT8
+UsbAllocateAddress (
+ IN UINT8 *AddressPool
+ )
+{
+ UINT8 ByteIndex;
+ UINT8 BitIndex;
+
+ for (ByteIndex = 0; ByteIndex < 16; ByteIndex++) {
+ for (BitIndex = 0; BitIndex < 8; BitIndex++) {
+ if ((AddressPool[ByteIndex] & (1 << BitIndex)) == 0) {
+ //
+ // Found one, covert to address, and mark it use
+ //
+ AddressPool[ByteIndex] |= (1 << BitIndex);
+ return (UINT8) (ByteIndex * 8 + BitIndex);
+ }
+ }
+ }
+
+ return 0;
+
+}
+
+STATIC
+VOID
+UsbFreeAddress (
+ IN UINT8 DevAddress,
+ IN UINT8 *AddressPool
+ )
+{
+ UINT8 WhichByte;
+ UINT8 WhichBit;
+ //
+ // Locate the position
+ //
+ WhichByte = (UINT8) (DevAddress / 8);
+ WhichBit = (UINT8) (DevAddress & 0x7);
+
+ AddressPool[WhichByte] &= (~(1 << WhichBit));
+}
+
+EFI_STATUS
+EFIAPI
+UsbBusControllerDriverSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+/*++
+
+ Routine Description:
+ Test to see if this driver supports ControllerHandle. Any ControllerHandle
+ that has UsbHcProtocol installed will be supported.
+
+ Arguments:
+ This - Protocol instance pointer.
+ Controller - Handle of device to test
+ RemainingDevicePath - Not used
+
+ Returns:
+ EFI_SUCCESS - This driver supports this device.
+ EFI_UNSUPPORTED - This driver does not support this device.
+
+--*/
+{
+ EFI_STATUS OpenStatus;
+
+ //
+ // Check whether USB Host Controller Protocol is already
+ // installed on this handle. If it is installed, we can start
+ // USB Bus Driver now.
+ //
+ OpenStatus = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (EFI_ERROR (OpenStatus) && (OpenStatus != EFI_ALREADY_STARTED)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return OpenStatus;
+}
+
+
+EFI_STATUS
+EFIAPI
+UsbBusControllerDriverStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+/*++
+
+ Routine Description:
+
+ Starting the Usb Bus Driver
+
+ Arguments:
+
+ This - Protocol instance pointer.
+ Controller - Handle of device to test
+ RemainingDevicePath - Not used
+
+ Returns:
+
+ EFI_SUCCESS - This driver supports this device.
+ EFI_UNSUPPORTED - This driver does not support this device.
+ EFI_DEVICE_ERROR - This driver cannot be started due to device
+ Error
+ EFI_OUT_OF_RESOURCES- Can't allocate memory resources
+ EFI_ALREADY_STARTED - This driver has been started
+
+--*/
+{
+ EFI_STATUS Status;
+ EFI_STATUS OpenStatus;
+ USB_BUS_CONTROLLER_DEVICE *UsbBusDev;
+ USB_IO_DEVICE *RootHub;
+ USB_IO_CONTROLLER_DEVICE *RootHubController;
+ EFI_USB_HC_PROTOCOL *UsbHCInterface;
+
+ //
+ // Allocate USB_BUS_CONTROLLER_DEVICE structure
+ //
+ UsbBusDev = NULL;
+ UsbBusDev = AllocateZeroPool (sizeof (USB_BUS_CONTROLLER_DEVICE));
+ if (UsbBusDev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UsbBusDev->Signature = USB_BUS_DEVICE_SIGNATURE;
+ UsbBusDev->AddressPool[0] = 1;
+
+ //
+ // Get the Device Path Protocol on Controller's handle
+ //
+ OpenStatus = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &UsbBusDev->DevicePath,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (OpenStatus)) {
+ gBS->FreePool (UsbBusDev);
+ return EFI_UNSUPPORTED;
+ }
+ //
+ // Locate the Host Controller Interface
+ //
+ OpenStatus = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ (VOID **) &UsbHCInterface,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (OpenStatus) && (OpenStatus != EFI_ALREADY_STARTED)) {
+
+ //
+ // Report Status Code here since we will reset the host controller
+ //
+ REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,
+ EFI_IO_BUS_USB | EFI_IOB_EC_CONTROLLER_ERROR,
+ UsbBusDev->DevicePath
+ );
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ gBS->FreePool (UsbBusDev);
+ return EFI_UNSUPPORTED;
+ }
+
+ if (OpenStatus == EFI_ALREADY_STARTED) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ gBS->FreePool (UsbBusDev);
+ return EFI_ALREADY_STARTED;
+ }
+
+ UsbBusDev->UsbHCInterface = UsbHCInterface;
+
+ //
+ // Attach EFI_USB_BUS_PROTOCOL to controller handle,
+ // for locate UsbBusDev later
+ //
+ Status = gBS->InstallProtocolInterface (
+ &Controller,
+ &mUsbBusProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &UsbBusDev->BusIdentify
+ );
+
+ if (EFI_ERROR (Status)) {
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ gBS->FreePool (UsbBusDev);
+ return Status;
+ }
+ //
+ // Add root hub to the tree
+ //
+ RootHub = NULL;
+ RootHub = AllocateZeroPool (sizeof (USB_IO_DEVICE));
+ if (RootHub == NULL) {
+ gBS->UninstallProtocolInterface (
+ Controller,
+ &mUsbBusProtocolGuid,
+ &UsbBusDev->BusIdentify
+ );
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ gBS->FreePool (UsbBusDev);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RootHub->BusController = UsbBusDev;
+ RootHub->DeviceAddress = UsbAllocateAddress (UsbBusDev->AddressPool);
+
+ UsbBusDev->Root = RootHub;
+
+ //
+ // Allocate Root Hub Controller
+ //
+ RootHubController = CreateUsbIoControllerDevice ();
+ if (RootHubController == NULL) {
+ gBS->UninstallProtocolInterface (
+ Controller,
+ &mUsbBusProtocolGuid,
+ &UsbBusDev->BusIdentify
+ );
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ gBS->FreePool (UsbBusDev);
+ gBS->FreePool (RootHub);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UsbHCInterface->GetRootHubPortNumber (
+ UsbHCInterface,
+ &RootHubController->DownstreamPorts
+ );
+ RootHubController->UsbDevice = RootHub;
+ RootHubController->IsUsbHub = TRUE;
+ RootHubController->DevicePath = UsbBusDev->DevicePath;
+ RootHubController->HostController = Controller;
+
+ RootHub->NumOfControllers = 1;
+ RootHub->UsbController[0] = RootHubController;
+
+ //
+ // Report Status Code here since we will reset the host controller
+ //
+ REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+ EFI_PROGRESS_CODE,
+ EFI_IO_BUS_USB | EFI_IOB_PC_RESET,
+ UsbBusDev->DevicePath
+ );
+
+ //
+ // Reset USB Host Controller
+ //
+ UsbHCInterface->Reset (
+ UsbHCInterface,
+ EFI_USB_HC_RESET_GLOBAL
+ );
+
+ //
+ // Report Status Code while we are going to bring up the Host Controller
+ // and start bus enumeration
+ //
+ REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+ EFI_PROGRESS_CODE,
+ EFI_IO_BUS_USB | EFI_IOB_PC_ENABLE,
+ UsbBusDev->DevicePath
+ );
+
+ //
+ // Start USB Host Controller
+ //
+ UsbHCInterface->SetState (
+ UsbHCInterface,
+ EfiUsbHcStateOperational
+ );
+
+ //
+ // Create a timer to query root ports periodically
+ //
+ Status = gBS->CreateEvent (
+ EFI_EVENT_TIMER | EFI_EVENT_NOTIFY_SIGNAL,
+ EFI_TPL_CALLBACK,
+ UsbEnumeration,
+ RootHubController,
+ &RootHubController->HubNotify
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallProtocolInterface (
+ Controller,
+ &mUsbBusProtocolGuid,
+ &UsbBusDev->BusIdentify
+ );
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->FreePool (RootHubController);
+ gBS->FreePool (RootHub);
+ gBS->FreePool (UsbBusDev);
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Before depending on the timer to check root ports periodically,
+ // here we should check them immediately for the first time, or
+ // there will be an interval between bus start and devices start.
+ //
+ gBS->SignalEvent (RootHubController->HubNotify);
+
+ Status = gBS->SetTimer (
+ RootHubController->HubNotify,
+ TimerPeriodic,
+ BUSPOLLING_PERIOD
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallProtocolInterface (
+ Controller,
+ &mUsbBusProtocolGuid,
+ &UsbBusDev->BusIdentify
+ );
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->CloseEvent (RootHubController->HubNotify);
+ gBS->FreePool (RootHubController);
+ gBS->FreePool (RootHub);
+ gBS->FreePool (UsbBusDev);
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+//
+// Stop the bus controller
+//
+EFI_STATUS
+EFIAPI
+UsbBusControllerDriverStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+/*++
+
+ Routine Description:
+ Stop this driver on ControllerHandle. Support stoping any child handles
+ created by this driver.
+
+ Arguments:
+ This - Protocol instance pointer.
+ Controller - Handle of device to stop driver on
+ NumberOfChildren - Number of Children in the ChildHandleBuffer
+ ChildHandleBuffer - List of handles for the children we need to stop.
+
+ Returns:
+ EFI_SUCCESS
+ EFI_DEVICE_ERROR
+ others
+
+--*/
+{
+ EFI_STATUS Status;
+ USB_IO_DEVICE *Root;
+ USB_IO_CONTROLLER_DEVICE *RootHubController;
+ USB_BUS_CONTROLLER_DEVICE *UsbBusController;
+ EFI_USB_BUS_PROTOCOL *UsbIdentifier;
+ UINT8 Index2;
+ EFI_USB_HC_PROTOCOL *UsbHCInterface;
+ USB_IO_CONTROLLER_DEVICE *UsbController;
+ USB_IO_DEVICE *UsbIoDevice;
+ USB_IO_CONTROLLER_DEVICE *HubController;
+ UINTN Index;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+
+ if (NumberOfChildren > 0) {
+
+ for (Index = 0; Index < NumberOfChildren; Index++) {
+ Status = gBS->OpenProtocol (
+ ChildHandleBuffer[Index],
+ &gEfiUsbIoProtocolGuid,
+ (VOID **) &UsbIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // We are here since the handle passed in does not support
+ // UsbIo protocol. There are several reasons that will cause
+ // this.
+ // For combo device such as keyboard, it may have 2 devices
+ // in one, namely, keyboard and mouse. If we deconfigure one
+ // of them, the other will be freed at the same time. This will
+ // cause the status error. But this is the correct behavior.
+ // For hub device, if we deconfigure hub first, the other chile
+ // device will be disconnected also, this will also provide us
+ // a status error. Now we will only report EFI_SUCCESS since Uhc
+ // driver will be disconnected at the second time.(pls see
+ // CoreDisconnectController for details)
+ //
+ continue;
+ }
+
+ UsbController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (UsbIo);
+ UsbIoDevice = UsbController->UsbDevice;
+ HubController = UsbController->Parent;
+ UsbDeviceDeConfiguration (UsbIoDevice);
+ for (Index2 = 0; Index2 < HubController->DownstreamPorts; Index2++) {
+ if (HubController->Children[Index2] == UsbIoDevice) {
+ HubController->Children[Index2] = NULL;
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+ }
+ //
+ // Get the USB_BUS_CONTROLLER_DEVICE
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &mUsbBusProtocolGuid,
+ (VOID **) &UsbIdentifier,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ UsbBusController = USB_BUS_CONTROLLER_DEVICE_FROM_THIS (UsbIdentifier);
+
+ //
+ // Stop USB Host Controller
+ //
+ UsbHCInterface = UsbBusController->UsbHCInterface;
+
+ //
+ // Report Status Code here since we will reset the host controller
+ //
+ ReportUsbStatusCode (
+ UsbBusController,
+ EFI_PROGRESS_CODE,
+ EFI_IO_BUS_USB | EFI_IOB_PC_RESET
+ );
+
+ UsbHCInterface->SetState (
+ UsbHCInterface,
+ EfiUsbHcStateHalt
+ );
+
+ //
+ // Deconfiguration all its devices
+ //
+ Root = UsbBusController->Root;
+ RootHubController = Root->UsbController[0];
+
+ gBS->CloseEvent (RootHubController->HubNotify);
+
+ for (Index2 = 0; Index2 < RootHubController->DownstreamPorts; Index2++) {
+ if (RootHubController->Children[Index2]) {
+ UsbDeviceDeConfiguration (RootHubController->Children[Index2]);
+ RootHubController->Children[Index2] = NULL;
+ }
+ }
+
+ gBS->FreePool (RootHubController);
+ gBS->FreePool (Root);
+
+ //
+ // Uninstall USB Bus Protocol
+ //
+ gBS->UninstallProtocolInterface (
+ Controller,
+ &mUsbBusProtocolGuid,
+ &UsbBusController->BusIdentify
+ );
+
+ //
+ // Close USB_HC_PROTOCOL & DEVICE_PATH_PROTOCOL
+ // Opened by this Controller
+ //
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->FreePool (UsbBusController);
+
+ return EFI_SUCCESS;
+}
+//
+// USB Device Configuration
+//
+STATIC
+EFI_STATUS
+UsbDeviceConfiguration (
+ IN USB_IO_CONTROLLER_DEVICE *ParentHubController,
+ IN EFI_HANDLE HostController,
+ IN UINT8 ParentPort,
+ IN USB_IO_DEVICE *UsbIoDevice
+ )
+/*++
+
+ Routine Description:
+ Configurate a new device attached to the usb bus
+
+ Arguments:
+ ParentHubController - Parent Hub which this device is connected.
+ HostController - Host Controller handle
+ ParentPort - Parent Hub port which this device is connected.
+ UsbIoDevice - The device to be configured.
+
+ Returns:
+ EFI_SUCCESS
+ EFI_DEVICE_ERROR
+ EFI_OUT_OF_RESOURCES
+
+--*/
+{
+ UINT8 DevAddress;
+ UINT8 Index;
+ EFI_STATUS Result;
+ UINT32 Status;
+ CHAR16 *StrManufacturer;
+ CHAR16 *StrProduct;
+ CHAR16 *StrSerialNumber;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ UINT8 NumOfInterface;
+ USB_IO_CONTROLLER_DEVICE *FirstController;
+ USB_BUS_CONTROLLER_DEVICE *UsbBusDev;
+ USB_IO_CONTROLLER_DEVICE *UsbIoController;
+
+ UsbBusDev = UsbIoDevice->BusController;
+ //
+ // Since a USB device must have at least on interface,
+ // so create this instance first
+ //
+ FirstController = CreateUsbIoControllerDevice ();
+ FirstController->UsbDevice = UsbIoDevice;
+ UsbIoDevice->UsbController[0] = FirstController;
+ FirstController->InterfaceNumber = 0;
+ FirstController->ParentPort = ParentPort;
+ FirstController->Parent = ParentHubController;
+ FirstController->HostController = HostController;
+
+ InitializeUsbIoInstance (FirstController);
+
+ DEBUG ((gUSBDebugLevel, "Configuration Usb Device at 0x%x...\n", ParentPort));
+
+ //
+ // Ensure we used the correctly USB I/O instance
+ //
+ UsbIo = &FirstController->UsbIo;
+
+ //
+ // First retrieve the 1st 8 bytes of
+ // in order to get the MaxPacketSize for Endpoint 0
+ //
+ for (Index = 0; Index < 3; Index++) {
+
+ UsbIoDevice->DeviceDescriptor.MaxPacketSize0 = 8;
+
+ ParentPortReset (FirstController, FALSE, Index);
+
+ Result = UsbGetDescriptor (
+ UsbIo,
+ (USB_DT_DEVICE << 8),
+ 0,
+ 8,
+ &UsbIoDevice->DeviceDescriptor,
+ &Status
+ );
+ if (!EFI_ERROR (Result)) {
+ DEBUG ((gUSBDebugLevel,
+ "Get Device Descriptor Success, MaxPacketSize0 = 0x%x\n",
+ UsbIoDevice->DeviceDescriptor.MaxPacketSize0)
+ );
+ break;
+ }
+
+ }
+
+ if (Index == 3) {
+ ReportUsbStatusCode (
+ UsbBusDev,
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,
+ EFI_IO_BUS_USB | EFI_IOB_EC_READ_ERROR
+ );
+ DEBUG ((gUSBErrorLevel, "Get Device Descriptor Fail when configing\n"));
+ gBS->FreePool (FirstController);
+ return EFI_DEVICE_ERROR;
+ }
+
+ DevAddress = UsbAllocateAddress (UsbIoDevice->BusController->AddressPool);
+ if (DevAddress == 0) {
+ DEBUG ((gUSBErrorLevel, "Cannot allocate address\n"));
+ gBS->FreePool (FirstController);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Result = UsbSetDeviceAddress (UsbIo, DevAddress, &Status);
+
+ if (EFI_ERROR (Result)) {
+ DEBUG ((gUSBErrorLevel, "Set address error\n"));
+ ReportUsbStatusCode (
+ UsbBusDev,
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,
+ EFI_IO_BUS_USB | EFI_IOB_EC_WRITE_ERROR
+ );
+
+ UsbFreeAddress (
+ DevAddress,
+ UsbIoDevice->BusController->AddressPool
+ );
+
+ gBS->FreePool (FirstController);
+ return EFI_DEVICE_ERROR;
+ }
+
+ UsbIoDevice->DeviceAddress = DevAddress;
+
+ //
+ // Get the whole device descriptor
+ //
+ Result = UsbGetDescriptor (
+ UsbIo,
+ (USB_DT_DEVICE << 8),
+ 0,
+ sizeof (EFI_USB_DEVICE_DESCRIPTOR),
+ &UsbIoDevice->DeviceDescriptor,
+ &Status
+ );
+
+ if (EFI_ERROR (Result)) {
+ DEBUG ((gUSBErrorLevel, "Get whole Device Descriptor error\n"));
+ ReportUsbStatusCode (
+ UsbBusDev,
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,
+ EFI_IO_BUS_USB | EFI_IOB_EC_READ_ERROR
+ );
+ UsbFreeAddress (
+ DevAddress,
+ UsbIoDevice->BusController->AddressPool
+ );
+
+ gBS->FreePool (FirstController);
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Get & parse all configurations for this device, including
+ // all configuration descriptors, all interface descriptors, all
+ // endpoint descriptors
+ //
+ Result = UsbGetAllConfigurations (UsbIoDevice);
+
+ if (EFI_ERROR (Result)) {
+ DEBUG ((gUSBErrorLevel, "Failed to get device configuration\n"));
+ ReportUsbStatusCode (
+ UsbBusDev,
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,
+ EFI_IO_BUS_USB | EFI_IOB_EC_READ_ERROR
+ );
+ UsbFreeAddress (
+ DevAddress,
+ UsbIoDevice->BusController->AddressPool
+ );
+
+ gBS->FreePool (FirstController);
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Set the 1st configuration value
+ //
+ Result = UsbSetDefaultConfiguration (UsbIoDevice);
+ if (EFI_ERROR (Result)) {
+ DEBUG ((gUSBErrorLevel, "Failed to set device configuration\n"));
+ ReportUsbStatusCode (
+ UsbBusDev,
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,
+ EFI_IO_BUS_USB | EFI_IOB_EC_WRITE_ERROR
+ );
+ UsbFreeAddress (
+ DevAddress,
+ UsbIoDevice->BusController->AddressPool
+ );
+
+ gBS->FreePool (FirstController);
+ return EFI_DEVICE_ERROR;
+ }
+
+ UsbIoDevice->IsConfigured = TRUE;
+
+ //
+ // Get all string table if applicable
+ //
+ Result = UsbGetStringtable (UsbIoDevice);
+ if (EFI_ERROR (Result)) {
+ DEBUG ((gUSBDebugLevel, "Device doesn't support string table\n"));
+ } else {
+
+ StrManufacturer = NULL;
+ UsbIo->UsbGetStringDescriptor (
+ UsbIo,
+ UsbIoDevice->LangID[0],
+ (UsbIoDevice->DeviceDescriptor).StrManufacturer,
+ &StrManufacturer
+ );
+
+ StrProduct = NULL;
+ UsbIo->UsbGetStringDescriptor (
+ UsbIo,
+ UsbIoDevice->LangID[0],
+ (UsbIoDevice->DeviceDescriptor).StrProduct,
+ &StrProduct
+ );
+
+ StrSerialNumber = NULL;
+ UsbIo->UsbGetStringDescriptor (
+ UsbIo,
+ UsbIoDevice->LangID[0],
+ (UsbIoDevice->DeviceDescriptor).StrSerialNumber,
+ &StrSerialNumber
+ );
+
+ if (StrManufacturer) {
+ gBS->FreePool (StrManufacturer);
+ }
+
+ if (StrProduct) {
+ gBS->FreePool (StrProduct);
+ }
+
+ if (StrSerialNumber) {
+ gBS->FreePool (StrSerialNumber);
+ }
+ }
+ //
+ // Create USB_IO_CONTROLLER_DEVICE for
+ // each detected interface
+ //
+ FirstController->CurrentConfigValue =
+ UsbIoDevice->ActiveConfig->CongfigDescriptor.ConfigurationValue;
+
+ NumOfInterface =
+ UsbIoDevice->ActiveConfig->CongfigDescriptor.NumInterfaces;
+ UsbIoDevice->NumOfControllers = NumOfInterface;
+
+ Result = InitUsbIoController (FirstController);
+ if (EFI_ERROR (Result)) {
+ ReportUsbStatusCode (
+ UsbBusDev,
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,
+ EFI_IO_BUS_USB | EFI_IOB_EC_INTERFACE_ERROR
+ );
+ gBS->FreePool (FirstController);
+ UsbIoDevice->UsbController[0] = NULL;
+ return EFI_DEVICE_ERROR;
+ }
+
+ for (Index = 1; Index < NumOfInterface; Index++) {
+ UsbIoController = CreateUsbIoControllerDevice ();
+ UsbIoController->UsbDevice = UsbIoDevice;
+ UsbIoController->CurrentConfigValue =
+ UsbIoDevice->ActiveConfig->CongfigDescriptor.ConfigurationValue;
+ UsbIoController->InterfaceNumber = Index;
+ UsbIoDevice->UsbController[Index] = UsbIoController;
+ UsbIoController->ParentPort = ParentPort;
+ UsbIoController->Parent = ParentHubController;
+ UsbIoController->HostController = HostController;
+
+ //
+ // First copy the USB_IO Protocol instance
+ //
+ CopyMem (
+ &UsbIoController->UsbIo,
+ UsbIo,
+ sizeof (EFI_USB_IO_PROTOCOL)
+ );
+
+ Result = InitUsbIoController (UsbIoController);
+ if (EFI_ERROR (Result)) {
+ ReportUsbStatusCode (
+ UsbBusDev,
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,
+ EFI_IO_BUS_USB | EFI_IOB_EC_INTERFACE_ERROR
+ );
+ gBS->FreePool (UsbIoController);
+ UsbIoDevice->UsbController[Index] = NULL;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+//
+// USB Device DeConfiguration
+//
+
+EFI_STATUS
+UsbDeviceDeConfiguration (
+ IN USB_IO_DEVICE *UsbIoDevice
+ )
+/*++
+
+ Routine Description:
+ Remove Device, Device Handles, Uninstall Protocols.
+
+ Arguments:
+ UsbIoDevice - The device to be deconfigured.
+
+ Returns:
+ EFI_SUCCESS
+ EFI_DEVICE_ERROR
+
+--*/
+{
+ USB_IO_CONTROLLER_DEVICE *UsbController;
+ UINT8 index;
+ USB_IO_DEVICE *ChildDevice;
+ UINT8 Index;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+
+ DEBUG ((gUSBDebugLevel, "Enter Usb Device Deconfiguration\n"));
+
+ //
+ // Double check UsbIoDevice exists
+ //
+ if (UsbIoDevice == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ for (index = 0; index < UsbIoDevice->NumOfControllers; index++) {
+ //
+ // Check if it is a hub, if so, de configuration all its
+ // downstream ports
+ //
+ UsbController = UsbIoDevice->UsbController[index];
+
+ //
+ // Check the controller pointer
+ //
+ if (UsbController == NULL) {
+ continue;
+ }
+
+ if (UsbController->IsUsbHub) {
+
+ DEBUG ((gUSBDebugLevel, "Hub Deconfig, First Deconfig its downstream ports\n"));
+
+ //
+ // First Remove interrupt transfer request for the status
+ // change port
+ //
+ UsbIo = &UsbController->UsbIo;
+ UsbIo->UsbAsyncInterruptTransfer (
+ UsbIo,
+ UsbController->HubEndpointAddress,
+ FALSE,
+ 0,
+ 0,
+ NULL,
+ NULL
+ );
+
+ if (NULL != UsbController->HubNotify) {
+ gBS->CloseEvent (UsbController->HubNotify);
+ }
+
+ for (Index = 0; Index < UsbController->DownstreamPorts; Index++) {
+ if (UsbController->Children[Index]) {
+ ChildDevice = UsbController->Children[Index];
+ UsbDeviceDeConfiguration (ChildDevice);
+ UsbController->Children[Index] = NULL;
+ }
+ }
+ }
+ //
+ // If the controller is managed by a device driver, we need to
+ // disconnect them
+ //
+ if (UsbController->IsManagedByDriver) {
+ gBS->DisconnectController (
+ UsbController->Handle,
+ NULL,
+ NULL
+ );
+ }
+
+ //
+ // remove child handle reference to the USB_HC_PROTOCOL
+ //
+ gBS->CloseProtocol (
+ UsbController->HostController,
+ &gEfiUsbHcProtocolGuid,
+ gUsbBusDriverBinding.DriverBindingHandle,
+ UsbController->Handle
+ );
+
+ //
+ // Uninstall EFI_USB_IO_PROTOCOL & DEVICE_PATH_PROTOCOL
+ // installed on this handle
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ UsbController->Handle,
+ &gEfiDevicePathProtocolGuid,
+ UsbController->DevicePath,
+ &gEfiUsbIoProtocolGuid,
+ &UsbController->UsbIo,
+ NULL
+ );
+
+ if (UsbController->DevicePath != NULL) {
+ gBS->FreePool (UsbController->DevicePath);
+ }
+
+ gBS->FreePool (UsbController);
+ UsbIoDevice->UsbController[index] = NULL;
+ }
+ //
+ // Free address for later use
+ //
+ UsbFreeAddress (
+ UsbIoDevice->DeviceAddress,
+ UsbIoDevice->BusController->AddressPool
+ );
+
+ //
+ // Free all resouces allocated for all its configurations
+ //
+ UsbDestroyAllConfiguration (UsbIoDevice);
+
+ if (UsbIoDevice) {
+ gBS->FreePool (UsbIoDevice);
+ UsbIoDevice = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+//
+// After interrupt complete, this function will be called,
+// This function need to be well-defined later
+//
+STATIC
+EFI_STATUS
+EFIAPI
+OnHubInterruptComplete (
+ IN VOID *Data,
+ IN UINTN DataLength,
+ IN VOID *Context,
+ IN UINT32 Result
+ )
+/*++
+
+ Routine Description:
+ Whenever hub interrupt occurs, this routine will be called to check
+ which event happens.
+
+ Arguments:
+ Data - Hub interrupt transfer data.
+ DataLength - The length of the Data.
+ Context - Hub Controller Device.
+ Result - Hub interrupt transfer status.
+
+ Returns:
+ EFI_SUCCESS
+ EFI_DEVICE_ERROR
+
+--*/
+{
+ USB_IO_CONTROLLER_DEVICE *HubController;
+ UINT8 Index;
+ UINT8 *ptr;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ UINT32 UsbResult;
+ BOOLEAN Disconnected;
+ EFI_STATUS Status;
+
+ HubController = (USB_IO_CONTROLLER_DEVICE *) Context;
+ UsbIo = &HubController->UsbIo;
+
+ //
+ // If something error in this interrupt transfer,
+ //
+ if (Result != EFI_USB_NOERROR) {
+ if ((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) {
+ UsbClearEndpointHalt (
+ UsbIo,
+ HubController->HubEndpointAddress,
+ &UsbResult
+ );
+ }
+
+ //
+ // Delete & Submit this interrupt again
+ //
+ UsbIo->UsbAsyncInterruptTransfer (
+ UsbIo,
+ HubController->HubEndpointAddress,
+ FALSE,
+ 0,
+ 0,
+ NULL,
+ NULL
+ );
+
+ //
+ // try to detect if the hub itself was disconnected or not
+ //
+ Status = IsDeviceDisconnected (
+ HubController,
+ &Disconnected
+ );
+
+ if (!EFI_ERROR (Status) && Disconnected == TRUE) {
+ DEBUG ((gUSBErrorLevel, "Hub is disconnected\n"));
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Hub ports < 7
+ //
+ UsbIo->UsbAsyncInterruptTransfer (
+ UsbIo,
+ HubController->HubEndpointAddress,
+ TRUE,
+ 100,
+ 1,
+ OnHubInterruptComplete,
+ HubController
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (DataLength == 0 || Data == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Scan which port has status change
+ // Bit 0 stands for hub itself, other bit stands for
+ // the corresponding port
+ //
+ for (Index = 0; Index < DataLength * 8; Index++) {
+ ptr = (UINT8 *) Data + Index / 8;
+ if ((*ptr) & (1 << (Index & 0x7))) {
+ HubController->StatusChangePort = Index;
+ break;
+ }
+ }
+ //
+ // Signal hub notify event
+ //
+ gBS->SignalEvent (HubController->HubNotify);
+
+ return EFI_SUCCESS;
+}
+//
+// USB Root Hub Enumerator
+//
+STATIC
+VOID
+EFIAPI
+UsbEnumeration (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+/*++
+
+ Routine Description:
+ This is USB enumerator
+
+ Arguments:
+ Event - Indicating which event is signaled
+ Context - actually it is a USB_IO_DEVICE
+
+ Returns:
+ EFI_SUCCESS
+ Others
+
+--*/
+{
+ USB_IO_CONTROLLER_DEVICE *HubController;
+ EFI_USB_PORT_STATUS HubPortStatus;
+ EFI_STATUS Status;
+ UINT8 Index;
+ EFI_USB_HC_PROTOCOL *UsbHCInterface;
+ USB_IO_DEVICE *UsbIoDev;
+ USB_BUS_CONTROLLER_DEVICE *UsbBusDev;
+ EFI_HANDLE HostController;
+ USB_IO_DEVICE *OldUsbIoDevice;
+ USB_IO_DEVICE *NewDevice;
+ USB_IO_CONTROLLER_DEVICE *NewController;
+ UINT8 Index2;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ UINT8 StatusChangePort;
+
+ HubController = (USB_IO_CONTROLLER_DEVICE *) Context;
+ HostController = HubController->HostController;
+ UsbBusDev = HubController->UsbDevice->BusController;
+
+ if (HubController->UsbDevice->DeviceAddress == 1) {
+ //
+ // Root hub has the address 1
+ //
+ UsbIoDev = HubController->UsbDevice;
+ UsbHCInterface = UsbIoDev->BusController->UsbHCInterface;
+
+ for (Index = 0; Index < HubController->DownstreamPorts; Index++) {
+ UsbHCInterface->GetRootHubPortStatus (
+ UsbHCInterface,
+ Index,
+ (EFI_USB_PORT_STATUS *) &HubPortStatus
+ );
+
+ if (!IsPortConnectChange (HubPortStatus.PortChangeStatus)) {
+ continue;
+ }
+ //
+ // Clear root hub status change status
+ //
+ ClearRootPortConnectionChangeStatus (
+ Index,
+ UsbHCInterface
+ );
+
+ gBS->Stall (100 * 1000);
+
+ UsbHCInterface->GetRootHubPortStatus (
+ UsbHCInterface,
+ Index,
+ (EFI_USB_PORT_STATUS *) &HubPortStatus
+ );
+
+ if (IsPortConnect (HubPortStatus.PortStatus)) {
+
+ //
+ // There is something connected to this port
+ //
+ DEBUG ((gUSBDebugLevel, "Something attached from Root Hub in 0x%x\n", Index));
+
+ ReportUsbStatusCode (
+ UsbBusDev,
+ EFI_PROGRESS_CODE,
+ EFI_IO_BUS_USB | EFI_IOB_PC_HOTPLUG
+ );
+ //
+ // if there is something physically detached, but still logically
+ // attached...
+ //
+ OldUsbIoDevice = HubController->Children[Index];
+
+ if (NULL != OldUsbIoDevice) {
+ UsbDeviceDeConfiguration (OldUsbIoDevice);
+ HubController->Children[Index] = NULL;
+ }
+
+ NewDevice = AllocateZeroPool (sizeof (USB_IO_DEVICE));
+ if (NewDevice == NULL) {
+ return ;
+ }
+ //
+ // Initialize some fields by copying data from
+ // its parents
+ //
+ NewDevice->IsSlowDevice = IsPortLowSpeedDeviceAttached (HubPortStatus.PortStatus);
+
+ DEBUG ((gUSBDebugLevel, "DeviceSpeed 0x%x\n", NewDevice->IsSlowDevice));
+
+ NewDevice->BusController = UsbIoDev->BusController;
+
+ //
+ // Configure that device
+ //
+ Status = UsbDeviceConfiguration (
+ HubController,
+ HostController,
+ Index,
+ NewDevice
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->FreePool (NewDevice);
+ return ;
+ }
+ //
+ // Add this device to the usb bus tree
+ //
+ HubController->Children[Index] = NewDevice;
+
+ for (Index2 = 0; Index2 < NewDevice->NumOfControllers; Index2++) {
+ //
+ // If this device is hub, add to the hub index
+ //
+ NewController = NewDevice->UsbController[Index2];
+
+ Status = gBS->ConnectController (
+ NewController->Handle,
+ NULL,
+ NULL,
+ TRUE
+ );
+ //
+ // If connect success, we need to disconnect when
+ // stop the controller, otherwise we need not call
+ // gBS->DisconnectController ()
+ // This is used by those usb devices we don't plan
+ // to support. We can allocate
+ // controller handles for them, but we don't have
+ // device drivers to manage them.
+ //
+ NewController->IsManagedByDriver = (BOOLEAN) (!EFI_ERROR (Status));
+
+ if (IsHub (NewController)) {
+
+ NewController->IsUsbHub = TRUE;
+
+ //
+ // Configure Hub Controller
+ //
+ Status = DoHubConfig (NewController);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ //
+ // Create an event to do hub enumeration
+ //
+ gBS->CreateEvent (
+ EFI_EVENT_NOTIFY_SIGNAL,
+ EFI_TPL_CALLBACK,
+ UsbEnumeration,
+ NewController,
+ &NewController->HubNotify
+ );
+
+ //
+ // Add request to do query hub status
+ // change endpoint
+ // Hub ports < 7
+ //
+ UsbIo = &NewController->UsbIo;
+ UsbIo->UsbAsyncInterruptTransfer (
+ UsbIo,
+ NewController->HubEndpointAddress,
+ TRUE,
+ 100,
+ 1,
+ OnHubInterruptComplete,
+ NewController
+ );
+
+ }
+ }
+ } else {
+ //
+ // Something disconnected from USB root hub
+ //
+ DEBUG ((gUSBDebugLevel, "Something deteached from Root Hub\n"));
+
+ OldUsbIoDevice = HubController->Children[Index];
+
+ UsbDeviceDeConfiguration (OldUsbIoDevice);
+
+ HubController->Children[Index] = NULL;
+
+ UsbHCInterface->ClearRootHubPortFeature (
+ UsbHCInterface,
+ Index,
+ EfiUsbPortEnableChange
+ );
+
+ UsbHCInterface->GetRootHubPortStatus (
+ UsbHCInterface,
+ Index,
+ (EFI_USB_PORT_STATUS *) &HubPortStatus
+ );
+
+ }
+ }
+
+ return ;
+ } else {
+ //
+ // Event from Hub, Get the hub controller handle
+ //
+ //
+ // Get the status change endpoint
+ //
+ StatusChangePort = HubController->StatusChangePort;
+
+ //
+ // Clear HubController Status Change Bit
+ //
+ HubController->StatusChangePort = 0;
+
+ if (StatusChangePort == 0) {
+ //
+ // Hub changes, we don't handle here
+ //
+ return ;
+ }
+ //
+ // Check which event took place at that port
+ //
+ UsbIo = &HubController->UsbIo;
+ Status = HubGetPortStatus (
+ UsbIo,
+ StatusChangePort,
+ (UINT32 *) &HubPortStatus
+ );
+
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+ //
+ // Clear some change status
+ //
+ if (HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_ENABLE) {
+ //
+ // Clear Hub port enable change
+ //
+ DEBUG ((gUSBDebugLevel, "Port Enable Change\n"));
+ HubClearPortFeature (
+ UsbIo,
+ StatusChangePort,
+ EfiUsbPortEnableChange
+ );
+
+ HubGetPortStatus (
+ UsbIo,
+ StatusChangePort,
+ (UINT32 *) &HubPortStatus
+ );
+ }
+
+ if (HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) {
+ //
+ // Clear Hub reset change
+ //
+ DEBUG ((gUSBDebugLevel, "Port Reset Change\n"));
+ HubClearPortFeature (
+ UsbIo,
+ StatusChangePort,
+ EfiUsbPortResetChange
+ );
+
+ HubGetPortStatus (
+ UsbIo,
+ StatusChangePort,
+ (UINT32 *) &HubPortStatus
+ );
+ }
+
+ if (HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_OVERCURRENT) {
+ //
+ // Clear Hub overcurrent change
+ //
+ DEBUG ((gUSBDebugLevel, "Port Overcurrent Change\n"));
+ HubClearPortFeature (
+ UsbIo,
+ StatusChangePort,
+ EfiUsbPortOverCurrentChange
+ );
+
+ HubGetPortStatus (
+ UsbIo,
+ StatusChangePort,
+ (UINT32 *) &HubPortStatus
+ );
+ }
+
+ if (IsPortConnectChange (HubPortStatus.PortChangeStatus)) {
+ //
+ // First clear port connection change
+ //
+ DEBUG ((gUSBDebugLevel, "Port Connection Change\n"));
+ HubClearPortFeature (
+ UsbIo,
+ StatusChangePort,
+ EfiUsbPortConnectChange
+ );
+
+ HubGetPortStatus (
+ UsbIo,
+ StatusChangePort,
+ (UINT32 *) &HubPortStatus
+ );
+
+ if (IsPortConnect (HubPortStatus.PortStatus)) {
+
+ DEBUG ((gUSBDebugLevel, "New Device Connect on Hub port \n"));
+
+ ReportUsbStatusCode (
+ UsbBusDev,
+ EFI_PROGRESS_CODE,
+ EFI_IO_BUS_USB | EFI_IOB_PC_HOTPLUG
+ );
+
+ //
+ // if there is something physically detached, but still logically
+ // attached...
+ //
+ OldUsbIoDevice = HubController->Children[StatusChangePort - 1];
+
+ if (NULL != OldUsbIoDevice) {
+ UsbDeviceDeConfiguration (OldUsbIoDevice);
+ HubController->Children[StatusChangePort - 1] = NULL;
+ }
+
+ NewDevice = AllocateZeroPool (sizeof (USB_IO_DEVICE));
+ if (NewDevice == NULL) {
+ return ;
+ }
+
+ ResetHubPort (HubController, StatusChangePort);
+
+ HubGetPortStatus (
+ UsbIo,
+ StatusChangePort,
+ (UINT32 *) &HubPortStatus
+ );
+
+ //
+ // Initialize some fields
+ //
+ NewDevice->IsSlowDevice = IsPortLowSpeedDeviceAttached (HubPortStatus.PortStatus);
+
+ NewDevice->BusController = HubController->UsbDevice->BusController;
+
+ //
+ // Configure that device
+ //
+ Status = UsbDeviceConfiguration (
+ HubController,
+ HostController,
+ (UINT8) (StatusChangePort - 1),
+ NewDevice
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->FreePool (NewDevice);
+ return ;
+ }
+ //
+ // Add this device to the usb bus tree
+ // StatusChangePort is begin from 1,
+ //
+ HubController->Children[StatusChangePort - 1] = NewDevice;
+
+ for (Index2 = 0; Index2 < NewDevice->NumOfControllers; Index2++) {
+ //
+ // If this device is hub, add to the hub index
+ //
+ NewController = NewDevice->UsbController[Index2];
+
+ //
+ // Connect the controller to the driver image
+ //
+ Status = gBS->ConnectController (
+ NewController->Handle,
+ NULL,
+ NULL,
+ TRUE
+ );
+ //
+ // If connect success, we need to disconnect when
+ // stop the controller, otherwise we need not call
+ // gBS->DisconnectController ()
+ // This is used by those usb devices we don't plan
+ // to support. We can allocate
+ // controller handles for them, but we don't have
+ // device drivers to manage them.
+ //
+ NewController->IsManagedByDriver = (BOOLEAN) (!EFI_ERROR (Status));
+
+ //
+ // If this device is hub, add to the hub index
+ //
+ if (IsHub (NewController)) {
+
+ NewController->IsUsbHub = TRUE;
+
+ //
+ // Configure Hub
+ //
+ Status = DoHubConfig (NewController);
+
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ //
+ // Create an event to do hub enumeration
+ //
+ gBS->CreateEvent (
+ EFI_EVENT_NOTIFY_SIGNAL,
+ EFI_TPL_CALLBACK,
+ UsbEnumeration,
+ NewController,
+ &NewController->HubNotify
+ );
+
+ //
+ // Add request to do query hub status
+ // change endpoint
+ //
+ UsbIo = &NewController->UsbIo;
+ UsbIo->UsbAsyncInterruptTransfer (
+ UsbIo,
+ NewController->HubEndpointAddress, // Hub endpoint address
+ TRUE,
+ 100,
+ 1, // Hub ports < 7
+ OnHubInterruptComplete,
+ NewController
+ );
+ }
+ }
+ } else {
+ //
+ // Something disconnected from USB hub
+ //
+ DEBUG ((gUSBDebugLevel, "Something Device Detached on Hub port\n"));
+
+ OldUsbIoDevice = HubController->Children[StatusChangePort - 1];
+
+ UsbDeviceDeConfiguration (OldUsbIoDevice);
+
+ HubController->Children[StatusChangePort - 1] = NULL;
+
+ }
+
+ return ;
+ }
+
+ return ;
+ }
+}
+//
+// Clear port connection change status over a given root hub port
+//
+EFI_STATUS
+ClearRootPortConnectionChangeStatus (
+ UINT8 PortNum,
+ EFI_USB_HC_PROTOCOL *UsbHCInterface
+ )
+/*++
+
+ Routine Description:
+ Clear port connection change status over a given root hub port
+
+ Arguments:
+ PortNum - The given port.
+ UsbHCInterface - The EFI_USB_HC_PROTOCOL instance.
+
+ Returns:
+ EFI_SUCCESS
+
+--*/
+{
+ EFI_STATUS Status;
+ Status = UsbHCInterface->ClearRootHubPortFeature (
+ UsbHCInterface,
+ PortNum,
+ EfiUsbPortConnectChange
+ );
+ return Status;
+}
+
+STATIC
+USB_IO_CONTROLLER_DEVICE *
+CreateUsbIoControllerDevice (
+ VOID
+ )
+/*++
+
+ Routine Description:
+ Allocate a structure for USB_IO_CONTROLLER_DEVICE
+
+ Arguments:
+ N/A
+
+ Returns:
+ A pointer to a USB_IO_CONTROLLER_DEVICE structure,
+ Or NULL.
+
+--*/
+{
+ USB_IO_CONTROLLER_DEVICE *UsbIoControllerDev;
+
+ //
+ // Allocate USB_IO_CONTROLLER_DEVICE structure
+ //
+ UsbIoControllerDev = NULL;
+ UsbIoControllerDev = AllocateZeroPool (sizeof (USB_IO_CONTROLLER_DEVICE));
+
+ if (UsbIoControllerDev == NULL) {
+ return NULL;
+ }
+
+ UsbIoControllerDev->Signature = USB_IO_CONTROLLER_SIGNATURE;
+
+ return UsbIoControllerDev;
+}
+
+STATIC
+EFI_STATUS
+InitUsbIoController (
+ IN USB_IO_CONTROLLER_DEVICE *UsbIoController
+ )
+/*++
+
+ Routine Description:
+ Init and install EFI_USB_IO_PROTOCOL onto that controller.
+
+ Arguments:
+ UsbIoController - The Controller to be operated.
+
+ Returns:
+ EFI_SUCCESS
+ Others
+
+--*/
+{
+ USB_DEVICE_PATH UsbNode;
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_USB_HC_PROTOCOL *UsbHcProtocol;
+
+ //
+ // Build the child device path for each new USB_IO device
+ //
+ ZeroMem (&UsbNode, sizeof (UsbNode));
+ UsbNode.Header.Type = MESSAGING_DEVICE_PATH;
+ UsbNode.Header.SubType = MSG_USB_DP;
+ SetDevicePathNodeLength (&UsbNode.Header, sizeof (UsbNode));
+ UsbNode.InterfaceNumber = UsbIoController->InterfaceNumber;
+ UsbNode.ParentPortNumber = UsbIoController->ParentPort;
+ ParentDevicePath = UsbIoController->Parent->DevicePath;
+
+ UsbIoController->DevicePath =
+ AppendDevicePathNode (ParentDevicePath, &UsbNode.Header);
+ if (UsbIoController->DevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &UsbIoController->Handle,
+ &gEfiDevicePathProtocolGuid,
+ UsbIoController->DevicePath,
+ &gEfiUsbIoProtocolGuid,
+ &UsbIoController->UsbIo,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ UsbIoController->HostController,
+ &gEfiUsbHcProtocolGuid,
+ (VOID **) &UsbHcProtocol,
+ gUsbBusDriverBinding.DriverBindingHandle,
+ UsbIoController->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+ParentPortReset (
+ IN USB_IO_CONTROLLER_DEVICE *UsbIoController,
+ IN BOOLEAN ReConfigure,
+ IN UINT8 RetryTimes
+ )
+/*++
+
+ Routine Description:
+ Reset parent hub port to which this device is connected.
+
+ Arguments:
+ UsbIoController - Indicating the Usb Controller Device.
+ Reconfigure - Do we need to reconfigure it.
+ RetryTimes - Retry Times when failed
+ Returns:
+ EFI_SUCCESS
+ EFI_DEVICE_ERROR
+
+--*/
+{
+ USB_IO_DEVICE *ParentIoDev;
+ USB_IO_DEVICE *UsbIoDev;
+ USB_IO_CONTROLLER_DEVICE *ParentController;
+ UINT8 HubPort;
+ UINT32 Status;
+ EFI_STATUS Result;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ UINT8 Address;
+
+ ParentController = UsbIoController->Parent;
+ ParentIoDev = ParentController->UsbDevice;
+ UsbIoDev = UsbIoController->UsbDevice;
+ HubPort = UsbIoController->ParentPort;
+
+ gBS->Stall (100 * 1000);
+
+ if (ParentIoDev->DeviceAddress == 1) {
+ DEBUG ((gUSBDebugLevel, "Reset from Root Hub 0x%x\n", HubPort));
+ ResetRootPort (ParentIoDev->BusController->UsbHCInterface, HubPort, RetryTimes);
+ } else {
+ DEBUG ((gUSBDebugLevel, "Reset from Hub, Addr 0x%x\n", ParentIoDev->DeviceAddress));
+ ResetHubPort (ParentController, HubPort + 1);
+ }
+ //
+ // If we only need port reset, just return
+ //
+ if (!ReConfigure) {
+ return EFI_SUCCESS;
+ }
+ //
+ // Re-config that USB device
+ //
+ UsbIo = &UsbIoController->UsbIo;
+
+ //
+ // Assign a unique address to this device
+ //
+ Address = UsbIoDev->DeviceAddress;
+ UsbIoDev->DeviceAddress = 0;
+
+ Result = UsbSetDeviceAddress (UsbIo, Address, &Status);
+ UsbIoDev->DeviceAddress = Address;
+
+ if (EFI_ERROR (Result)) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Set the device to the default configuration
+ //
+ Result = UsbSetDefaultConfiguration (UsbIoDev);
+ if (EFI_ERROR (Result)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+UsbPortReset (
+ IN EFI_USB_IO_PROTOCOL *This
+ )
+/*++
+
+ Routine Description:
+ Resets and reconfigures the USB controller. This function will
+ work for all USB devices except USB Hub Controllers.
+
+ Arguments:
+ This - Indicates the calling context.
+
+ Returns:
+ EFI_SUCCESS
+ EFI_INVALID_PARAMETER
+ EFI_DEVICE_ERROR
+
+--*/
+{
+ USB_IO_CONTROLLER_DEVICE *UsbIoController;
+ EFI_STATUS Status;
+
+ UsbIoController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (This);
+
+ //
+ // Since at this time, this device has already been configured,
+ // it needs to be re-configured.
+ //
+ Status = ParentPortReset (UsbIoController, TRUE, 0);
+
+ return Status;
+}
+
+EFI_STATUS
+ResetRootPort (
+ IN EFI_USB_HC_PROTOCOL *UsbHCInterface,
+ IN UINT8 PortNum,
+ IN UINT8 RetryTimes
+ )
+/*++
+
+ Routine Description:
+ Reset Root Hub port.
+
+ Arguments:
+ UsbHCInterface - The EFI_USB_HC_PROTOCOL instance.
+ PortNum - The given port to be reset.
+ RetryTimes - RetryTimes when failed
+ Returns:
+ N/A
+
+--*/
+{
+ EFI_STATUS Status;
+
+ //
+ // reset root port
+ //
+ Status = UsbHCInterface->SetRootHubPortFeature (
+ UsbHCInterface,
+ PortNum,
+ EfiUsbPortReset
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ gBS->Stall (50 * 1000);
+
+ //
+ // clear reset root port
+ //
+ Status = UsbHCInterface->ClearRootHubPortFeature (
+ UsbHCInterface,
+ PortNum,
+ EfiUsbPortReset
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ gBS->Stall (1000);
+
+ Status = ClearRootPortConnectionChangeStatus (PortNum, UsbHCInterface);
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Set port enable
+ //
+ Status = UsbHCInterface->SetRootHubPortFeature (
+ UsbHCInterface,
+ PortNum,
+ EfiUsbPortEnable
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = UsbHCInterface->ClearRootHubPortFeature (
+ UsbHCInterface,
+ PortNum,
+ EfiUsbPortEnableChange
+ );
+ gBS->Stall ((1 + RetryTimes) * 50 * 1000);
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+ResetHubPort (
+ IN USB_IO_CONTROLLER_DEVICE *UsbIoController,
+ IN UINT8 PortIndex
+ )
+/*++
+
+ Routine Description:
+ Reset Hub port.
+
+ Arguments:
+ UsbIoController - The USB_IO_CONTROLLER_DEVICE instance.
+ PortIndex - The given port to be reset.
+
+ Returns:
+ EFI_SUCCESS
+ EFI_DEVICE_ERROR
+
+--*/
+{
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_USB_PORT_STATUS HubPortStatus;
+ UINT8 Number;
+
+ ASSERT (UsbIoController->IsUsbHub == TRUE);
+
+ UsbIo = &UsbIoController->UsbIo;
+
+ HubSetPortFeature (
+ UsbIo,
+ PortIndex,
+ EfiUsbPortReset
+ );
+
+ gBS->Stall (10 * 1000);
+
+ //
+ // Wait for port reset complete
+ //
+ Number = 10;
+ do {
+ HubGetPortStatus (
+ UsbIo,
+ PortIndex,
+ (UINT32 *) &HubPortStatus
+ );
+ gBS->Stall (10 * 100);
+ Number -= 1;
+ } while ((HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) == 0 && Number > 0);
+
+ if (Number == 0) {
+ //
+ // Cannot reset port, return error
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ gBS->Stall (1000);
+
+ HubGetPortStatus (
+ UsbIo,
+ PortIndex,
+ (UINT32 *) &HubPortStatus
+ );
+ //
+ // reset port will cause some bits change, clear them
+ //
+ if (HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_ENABLE) {
+ DEBUG ((gUSBDebugLevel, "Port Enable Change\n"));
+ HubClearPortFeature (
+ UsbIo,
+ PortIndex,
+ EfiUsbPortEnableChange
+ );
+ }
+
+ if (HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) {
+ DEBUG ((gUSBDebugLevel, "Port Reset Change\n"));
+ HubClearPortFeature (
+ UsbIo,
+ PortIndex,
+ EfiUsbPortResetChange
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+
+
+
+STATIC
+EFI_STATUS
+ReportUsbStatusCode (
+ IN USB_BUS_CONTROLLER_DEVICE *UsbBusController,
+ IN EFI_STATUS_CODE_TYPE Type,
+ IN EFI_STATUS_CODE_VALUE Code
+ )
+/*++
+
+Routine Description:
+
+ report a error Status code of USB bus driver controller
+
+ Arguments:
+ UsbBusController - USB_BUS_CONTROLLER_DEVICE
+ Type - EFI_STATUS_CODE_TYPE
+ Code - EFI_STATUS_CODE_VALUE
+ Returns:
+
+ None
+
+--*/
+{
+ return REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+ Type,
+ Code,
+ UsbBusController->DevicePath
+ );
+}
+
+
+EFI_STATUS
+IsDeviceDisconnected (
+ IN USB_IO_CONTROLLER_DEVICE *UsbIoController,
+ IN OUT BOOLEAN *Disconnected
+ )
+/*++
+
+ Routine Description:
+ Reset if the device is disconencted or not
+
+ Arguments:
+ UsbIoController - Indicating the Usb Controller Device.
+ Disconnected - Indicate whether the device is disconencted or not
+
+ Returns:
+ EFI_SUCCESS
+ EFI_DEVICE_ERROR
+
+--*/
+{
+ USB_IO_DEVICE *ParentIoDev;
+ USB_IO_DEVICE *UsbIoDev;
+ USB_IO_CONTROLLER_DEVICE *ParentController;
+ UINT8 HubPort;
+ EFI_STATUS Status;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_USB_PORT_STATUS PortStatus;
+ EFI_USB_HC_PROTOCOL *UsbHCInterface;
+
+ ParentController = UsbIoController->Parent;
+ ParentIoDev = ParentController->UsbDevice;
+ UsbIoDev = UsbIoController->UsbDevice;
+ HubPort = UsbIoController->ParentPort;
+
+ if (ParentIoDev->DeviceAddress == 1) {
+ //
+ // Connected to the root hub
+ //
+ UsbHCInterface = ParentIoDev->BusController->UsbHCInterface;
+ Status = UsbHCInterface->GetRootHubPortStatus (
+ UsbHCInterface,
+ HubPort,
+ &PortStatus
+ );
+
+ } else {
+ UsbIo = &UsbIoController->UsbIo;
+ Status = HubGetPortStatus (
+ &ParentController->UsbIo,
+ HubPort + 1,
+ (UINT32 *) &PortStatus
+ );
+
+ if (EFI_ERROR (Status)) {
+ return IsDeviceDisconnected (ParentController, Disconnected);
+ }
+ }
+
+ *Disconnected = FALSE;
+
+ if (!IsPortConnect (PortStatus.PortStatus)) {
+ *Disconnected = TRUE;
+ }
+
+ return EFI_SUCCESS;
+}