/** @file USB Bus Driver ;****************************************************************************** ;* Copyright (c) 2012 - 2021, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include "UsbBus.h" #include "Hub.h" #include "UsbHelper.h" #include #ifndef MDEPKG_NDEBUG UINTN gUSBDebugLevel = EFI_D_INFO; 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. // USB_BUS_PRIVATE *mPrivate; EFI_USB_CORE_PROTOCOL *mUsbCore; BOOLEAN mInSmram; STATIC EFI_GUID mUsbBusProtocolGuid = EFI_USB_BUS_PROTOCOL_GUID; STATIC USB_CHANGE_FEATURE_MAP mPortChangeMap[8] = { { USB_PORT_STAT_C_CONNECTION, EfiUsbPortConnectChange }, { USB_PORT_STAT_C_ENABLE, EfiUsbPortEnableChange }, { USB_PORT_STAT_C_SUSPEND, EfiUsbPortSuspendChange }, { USB_PORT_STAT_C_OVERCURRENT, EfiUsbPortOverCurrentChange }, { USB_PORT_STAT_C_RESET, EfiUsbPortResetChange }, { USB_PORT_STAT_C_BH_RESET, EfiUsbPortBhResetChange }, { USB_PORT_STAT_C_LINK_STATE, EfiUsbPortLinkStateChange }, { USB_PORT_STAT_C_CONFIG_ERROR,EfiUsbPortConfigErrorChange }, }; STATIC UINT16 mSpecialStallDeviceTable[] = { 0x054c, 0x05c9, 1000, // Sony ODD DRX-S90U, 1 second stall 0x0000, 0x0000, 0 }; STATIC UINT16 mGetStringRequiredDeviceTable[] = { 0x0404, 0x0418, 0, // NCR MINI MISC device 0x0000, 0x0000, 0 }; extern EFI_USB_IO_PROTOCOL mUsbIoInterface; // // USB Bus Driver Guid // EFI_GUID mUsbBusDriverGuid = { 0x347b6711, 0xe458, 0x43c8, 0x96, 0x36, 0xf1, 0x73, 0xf6, 0x98, 0xb3, 0x29 }; STATIC UINTN DevicePathSize ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath ); STATIC EFI_DEVICE_PATH_PROTOCOL * CopyDevicePath ( IN EFI_USB_CORE_PROTOCOL *UsbCore, IN EFI_DEVICE_PATH_PROTOCOL *Src1, IN EFI_DEVICE_PATH_PROTOCOL *Src2 ); STATIC VOID EFIAPI ConnectControllerCallback ( IN UINTN Event, IN VOID *Context ); STATIC VOID EFIAPI FreeUsbIoDeviceCallback ( IN UINTN Event, IN VOID *Context ); STATIC VOID FreeUsbIoControllerDevice( IN USB_IO_CONTROLLER_DEVICE *UsbController ); STATIC VOID InsertUsbIoDevAddressConvertTable( IN USB_IO_DEVICE *UsbIoDev, IN BOOLEAN IsRootHub ); STATIC VOID RemoveUsbIoDevAddressConvertTable( IN USB_IO_DEVICE *UsbIoDev ); STATIC VOID RemoveUsbIoControllerDevAddressConvertTable( IN USB_IO_CONTROLLER_DEVICE *UsbController ); // // USB bus entry point // EFI_STATUS EFIAPI UsbBusDriverEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ); // // 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, 0x18, NULL, NULL }; // // Internal use only // STATIC EFI_STATUS ReportUsbStatusCode ( IN USB_BUS_CONTROLLER_DEVICE *UsbBusDev, 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 ( IN USB_BUS_CONTROLLER_DEVICE *UsbBusDev ); // // 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 *UsbIoDev ); // // Usb Bus enumeration function // STATIC VOID EFIAPI RootHubEnumeration ( IN UINTN SyncEnumeration, IN VOID *Context ); STATIC VOID HubEnumeration ( IN UINTN SyncEnumeration, IN VOID *Context ); STATIC EFI_STATUS UsbSetTransactionTranslator ( IN USB_IO_CONTROLLER_DEVICE *ParentHubController, IN UINT8 ParentPort, IN OUT USB_IO_DEVICE *Device ); STATIC EFI_STATUS ReleasePortToCHC ( IN USB_BUS_CONTROLLER_DEVICE *UsbBusDev, IN UINT8 PortNum ); EFI_STATUS ResetRootPort ( IN USB_IO_CONTROLLER_DEVICE *UsbIoController, IN UINT8 PortNum, OUT UINT32 *HubPortStatus ); EFI_STATUS ResetHubPort ( IN USB_IO_CONTROLLER_DEVICE *UsbIoController, IN UINT8 PortIndex, OUT UINT32 *HubPortStatus ); /** Check is it special device which needs extra stall to waiting for further reaction @param UsbDevice Pointer of USB_IO_DEVICE @retval TRUE It is special stall device @retval FALSE It is not special stall device **/ STATIC BOOLEAN IsSpecialStallDevice ( IN USB_IO_DEVICE *UsbDevice ) { UINTN Index; for (Index = 0; Index < (sizeof (mSpecialStallDeviceTable) / sizeof (UINT16)) && mSpecialStallDeviceTable[Index] != 0; Index += 3) { if (mSpecialStallDeviceTable[Index] == UsbDevice->DeviceDescriptor.IdVendor && mSpecialStallDeviceTable[Index + 1] == UsbDevice->DeviceDescriptor.IdProduct) { mUsbCore->Stall (mSpecialStallDeviceTable[Index + 2] * 1000); return TRUE; } } return FALSE; } /** Check is it special device which needs get string to avoiding device self reset @param UsbDevice Pointer of USB_IO_DEVICE @retval TRUE It is special device @retval FALSE It is not special device **/ STATIC BOOLEAN IsGetStringRequiredDevice ( IN USB_IO_DEVICE *UsbDevice ) { UINTN Index; for (Index = 0; Index < (sizeof (mGetStringRequiredDeviceTable) / sizeof (UINT16)) && mGetStringRequiredDeviceTable[Index] != 0; Index += 3) { if (mGetStringRequiredDeviceTable[Index] == UsbDevice->DeviceDescriptor.IdVendor && mGetStringRequiredDeviceTable[Index + 1] == UsbDevice->DeviceDescriptor.IdProduct) { return TRUE; } } return FALSE; } /** Uses USB I/O to check whether the device is a USB Keyboard device. @param UsbIo Points to a USB I/O protocol instance. **/ STATIC BOOLEAN IsInLegacyProcess ( IN EFI_USB_IO_PROTOCOL *UsbIo ) { EFI_STATUS Status; EFI_USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; if (mUsbCore->IsCsmEnabled() == EFI_SUCCESS) { if (PAUSE_IN_PROGRESS) { // // Get the Default interface descriptor, currently we // assume it is interface 1 // Status = UsbGetInterfaceDescriptor ( UsbIo, &InterfaceDescriptor ); if (!EFI_ERROR (Status) && InterfaceDescriptor.InterfaceClass == CLASS_HID && InterfaceDescriptor.InterfaceSubClass == SUBCLASS_BOOT && InterfaceDescriptor.InterfaceProtocol == PROTOCOL_KEYBOARD ) { return TRUE; } } else if (EBDA(EBDA_OPROM_PROCESSING_FLAG) != 0) { return TRUE; } } return FALSE; } /** Allocate address for usb device @param AddressPool Pool of usb device address @retval Usb device address **/ STATIC UINT8 UsbAllocateDeviceAddress ( IN UINT8 DeviceSpeed, IN UINT8 *AddressPool ) { UINT8 ByteIndex; UINT8 BitIndex; UINTN MaxSupportedDevices; // // XHC support 256 devices // MaxSupportedDevices = (DeviceSpeed == EFI_USB_SPEED_SUPER) ? (256 >> 3) : (128 >> 3); for (ByteIndex = 0; ByteIndex < MaxSupportedDevices; 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; } /** Free address for usb device @param DevAddress Usb device address @param AddressPool Pool of usb device address **/ STATIC VOID UsbFreeDeviceAddress ( 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)); } /** Entry point for EFI drivers. @param ImageHandle EFI_HANDLE @param SystemTable EFI_SYSTEM_TABLE @retval EFI_SUCCESS @retval others **/ EFI_STATUS EFIAPI UsbBusDriverEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_HANDLE Handle; // // Locate UsbCore protocol // Status = gBS->LocateProtocol ( &gEfiUsbCoreProtocolGuid, NULL, (VOID **)&mUsbCore ); if (EFI_ERROR (Status)) { return EFI_UNSUPPORTED; } // // Check the phase of instance // mUsbCore->IsInSmm (&mInSmram); if (!mInSmram) { // // Now in boot service // Status = mUsbCore->AllocateBuffer( sizeof (USB_BUS_PRIVATE), ALIGNMENT_32, (VOID **)&mPrivate ); if (EFI_ERROR (Status)) { return Status; } mPrivate->Signature = USB_BUS_SIGNATURE; mPrivate->NumLowSpeedHc = -1; CopyMem ( &mPrivate->UsbIoInterface, &mUsbIoInterface, sizeof (EFI_USB_IO_PROTOCOL) ); // // Install protocol for internal use. Pass in a NULL to install to a new handle // Handle = NULL; Status = gBS->InstallProtocolInterface ( &Handle, &mUsbBusDriverGuid, EFI_NATIVE_INTERFACE, mPrivate ); if (EFI_ERROR (Status)) { return Status; } // // Install protocols // Status = EfiLibInstallDriverBindingComponentName2 ( ImageHandle, SystemTable, &gUsbBusDriverBinding, ImageHandle, &gUsbBusComponentName, &gUsbBusComponentName2 ); if (EFI_ERROR (Status)) { return Status; } mPrivate->DriverBindingHandle = gUsbBusDriverBinding.DriverBindingHandle; // // Register module in DXE instance // mUsbCore->ModuleRegistration (ImageHandle); } else { // // SMM instance, disable DEBUG message out // #ifndef MDEPKG_NDEBUG gUSBDebugLevel = 0; gUSBErrorLevel = 0; #endif // // Now in SMM, get private storage first // Status = gBS->LocateProtocol ( &mUsbBusDriverGuid, NULL, (VOID **)&mPrivate ); if (EFI_ERROR(Status)) { return Status; } // // Setup UsbCore for use by SMM instance // mUsbCore->AddressConvert ( SMM_ADDRESS, (VOID*)(UINTN)mUsbCore, (VOID**)&mUsbCore ); // // Register module in SMM instance // mUsbCore->ModuleRegistration (ImageHandle); // // Setup Smm address convert table for Smm security policy // mUsbCore->InsertAddressConvertTable ( ACT_FUNCTION_POINTER, &mPrivate->UsbIoInterface, sizeof(EFI_USB_IO_PROTOCOL) / sizeof(VOID*) ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_BODY, mPrivate, sizeof (USB_BUS_PRIVATE) ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &mPrivate, 1 ); } return Status; } /** Test to see if this driver supports ControllerHandle. Any ControllerHandle that has UsbHcProtocol installed will be supported. @param This Protocol instance pointer. @param Controller Handle of device to test @param RemainingDevicePath Device Path Protocol instance pointer @retval EFI_SUCCESS This driver supports this device. @retval EFI_UNSUPPORTED This driver does not support this device. **/ EFI_STATUS EFIAPI UsbBusControllerDriverSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; EFI_USB3_HC_PROTOCOL *Usb3Hc; EFI_USB2_HC_PROTOCOL *Usb2Hc; EFI_DEV_PATH_PTR Node; // // Check Device Path // if (RemainingDevicePath != NULL) { Node.DevPath = RemainingDevicePath; if (Node.DevPath->Type != MESSAGING_DEVICE_PATH || Node.DevPath->SubType != MSG_USB_DP || DevicePathNodeLength(Node.DevPath) != sizeof(USB_DEVICE_PATH)) { return EFI_UNSUPPORTED; } } // // Open the IO Abstraction(s) needed to perform the supported test // Status = gBS->OpenProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **) &ParentDevicePath, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (Status == EFI_ALREADY_STARTED) { return EFI_ALREADY_STARTED; } if (EFI_ERROR (Status)) { return Status; } gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); // // Check whether USB Host Controller Protocol is already // installed on this handle. If it is installed, we can start // USB Bus Driver now. // Status = gBS->OpenProtocol ( Controller, &gEfiUsb3HcProtocolGuid, (VOID **)&Usb3Hc, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (Status == EFI_ALREADY_STARTED) { return EFI_SUCCESS; } if (!EFI_ERROR (Status)) { gBS->CloseProtocol ( Controller, &gEfiUsb3HcProtocolGuid, This->DriverBindingHandle, Controller ); return EFI_SUCCESS; } else { // // Check whether USB2 Host Controller Protocol is already // installed on this handle. If it is installed, we can start // USB Bus Driver now. // Status = gBS->OpenProtocol ( Controller, &gEfiUsb2HcProtocolGuid, (VOID **)&Usb2Hc, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (Status == EFI_ALREADY_STARTED) { return EFI_SUCCESS; } if (!EFI_ERROR (Status)) { gBS->CloseProtocol ( Controller, &gEfiUsb2HcProtocolGuid, This->DriverBindingHandle, Controller ); return EFI_SUCCESS; } } return Status; } /** Starting the Usb Bus Driver @param This Protocol instance pointer. @param Controller Handle of device to test @param RemainingDevicePath Not used @retval EFI_SUCCESS This driver supports this device. @retval EFI_DEVICE_ERROR This driver cannot be started due to device Error @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources **/ EFI_STATUS EFIAPI UsbBusControllerDriverStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; USB_BUS_CONTROLLER_DEVICE *UsbBusDev; USB_IO_DEVICE *RootHub; USB_IO_CONTROLLER_DEVICE *RootHubController; UINT8 MaxSpeed; UINT8 PortNumber; UINT8 Is64BitCapable; EFI_USB3_HC_PROTOCOL *Usb3Hc; EFI_USB2_HC_PROTOCOL *Usb2Hc; EFI_USB3_HC_PROTOCOL *Usb3LowSpeedHc; EFI_USB_IO_PROTOCOL *UsbIo; UINTN HandleCount; EFI_HANDLE *HandleBuffer; UINT8 UsbClassCReg[4]; UINTN Index; EFI_PCI_IO_PROTOCOL *PciIo; USB_HC_DESC *HcDesc; UINTN SegNum; UINTN BusNum; UINTN DevNum; UINTN FuncNum; EFI_USB3_HC_CALLBACK RootHubEnumerationFunc; // // Locate the Host Controller Interface // Usb3Hc = NULL; Usb2Hc = NULL; UsbIo = NULL; Status = gBS->OpenProtocol ( Controller, &gEfiUsb3HcProtocolGuid, (VOID **)&Usb3Hc, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { Status = gBS->OpenProtocol ( Controller, &gEfiUsb2HcProtocolGuid, (VOID **)&Usb2Hc, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (!EFI_ERROR (Status)) { Status = gBS->HandleProtocol ( Controller, &gEfiUsbIoProtocolGuid, (VOID **) &UsbIo ); // // Install Usb3Hc for Usb2Hc only driver // Usb3Hc = InstallUsb3HcThunk (UsbIo, Usb2Hc); } if (!Usb3Hc) { goto exit; } } if (Usb2Hc) { Usb2Hc->GetCapability ( Usb2Hc, &MaxSpeed, &PortNumber, &Is64BitCapable ); } else { Usb3Hc->GetCapability ( Usb3Hc, &MaxSpeed, &PortNumber, &Is64BitCapable ); } // // For USB2 HC with CHC, we should waiting for CHC installed for the USB1.1 port routing // if (mPrivate->NumLowSpeedHc == -1) { mPrivate->NumLowSpeedHc = 0; // // Collect whole handles of low speed HC on first time // Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiPciIoProtocolGuid, NULL, &HandleCount, &HandleBuffer ); if (EFI_ERROR (Status)) { Status = EFI_UNSUPPORTED; goto close_hc_protocol; } for (Index = 0; Index < HandleCount; Index++) { Status = gBS->HandleProtocol ( HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **)&PciIo ); if (EFI_ERROR (Status)) { Status = EFI_UNSUPPORTED; goto close_hc_protocol; } Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint8, 9, 3, UsbClassCReg ); if (EFI_ERROR (Status)) { Status = EFI_UNSUPPORTED; goto close_hc_protocol; } // // Test whether the controller is low speed HC or not // if (UsbClassCReg[2] == 0x0C && UsbClassCReg[1] == 0x03 && UsbClassCReg[0] <= 0x10) { PciIo->GetLocation ( PciIo, &SegNum, &BusNum, &DevNum, &FuncNum ); HcDesc = &mPrivate->LowSpeedHc[mPrivate->NumLowSpeedHc]; HcDesc->Controller = HandleBuffer[Index]; HcDesc->SegNum = (UINT8)SegNum; HcDesc->BusNum = (UINT8)BusNum; HcDesc->DevNum = (UINT8)DevNum; HcDesc->FuncNum = (UINT8)FuncNum; mPrivate->NumLowSpeedHc ++; } } gBS->FreePool (HandleBuffer); } if (MaxSpeed == EFI_USB_SPEED_HIGH) { Status = gBS->HandleProtocol ( Controller, &gEfiPciIoProtocolGuid, (VOID **)&PciIo ); if (EFI_ERROR (Status)) { Status = EFI_UNSUPPORTED; goto close_hc_protocol; } PciIo->GetLocation ( PciIo, &SegNum, &BusNum, &DevNum, &FuncNum ); for (Index = 0, HcDesc = mPrivate->LowSpeedHc; Index < (UINTN)mPrivate->NumLowSpeedHc; Index ++, HcDesc ++) { if (HcDesc->SegNum == (UINT8)SegNum && HcDesc->BusNum == (UINT8)BusNum && HcDesc->DevNum == (UINT8)DevNum) { // // Check is the HC driver been installed in the controller // Status = gBS->HandleProtocol ( HcDesc->Controller, &gEfiUsb3HcProtocolGuid, (VOID **)&Usb3LowSpeedHc ); if (EFI_ERROR (Status)) { // // Try to connect the HC // gBS->ConnectController ( HcDesc->Controller, NULL, NULL, FALSE ); } } } } // // Allocate USB_BUS_CONTROLLER_DEVICE structure // Status = mUsbCore->AllocateBuffer ( sizeof (USB_BUS_CONTROLLER_DEVICE), ALIGNMENT_32, (VOID **) &UsbBusDev ); if (EFI_ERROR (Status)) { Status = EFI_OUT_OF_RESOURCES; goto close_hc_protocol; } UsbBusDev->Signature = USB_BUS_DEVICE_SIGNATURE; UsbBusDev->AddressPool[0] = 1; UsbBusDev->Usb3HCInterface = Usb3Hc; // // Get the Device Path Protocol on Controller's handle // Status = gBS->OpenProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **) &UsbBusDev->DevicePath, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { goto free_usbbusdev; } // // 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)) { goto close_devicepath_protocol; } // // Add root hub to the tree // Status = mUsbCore->AllocateBuffer ( sizeof (USB_IO_DEVICE), ALIGNMENT_32, (VOID **) &RootHub ); if (EFI_ERROR (Status)) { Status = EFI_OUT_OF_RESOURCES; goto uninstall_protocol; } RootHub->BusController = UsbBusDev; RootHub->DeviceAddress = UsbAllocateDeviceAddress ( MaxSpeed, UsbBusDev->AddressPool ); UsbBusDev->Root = RootHub; // // Allocate Root Hub Controller // RootHubController = CreateUsbIoControllerDevice ( UsbBusDev ); if (RootHubController == NULL) { Status = EFI_OUT_OF_RESOURCES; goto free_roothub; } RootHubController->DownstreamPorts = PortNumber; RootHubController->UsbDevice = RootHub; RootHubController->IsUsbHub = TRUE; // // In order to prevent the UEFI OS boot hang, allocate the buffer of DevicePath in BS for UEFI OS // RootHubController->UefiDevicePath = CopyDevicePath (NULL, UsbBusDev->DevicePath, NULL); // // Allocate the buffer of DevicePath in reserved memory for legacy mode hot-plug // RootHubController->DevicePath = CopyDevicePath (mUsbCore, UsbBusDev->DevicePath, NULL); RootHubController->HostController = Controller; if (RemainingDevicePath != NULL) { RootHubController->RemainingDevicePath = CopyDevicePath (mUsbCore, (EFI_DEVICE_PATH_PROTOCOL*)((UINT8*)RemainingDevicePath + sizeof(USB_DEVICE_PATH)), NULL); } RootHub->NumOfInterfaces = 1; RootHub->UsbController[0] = RootHubController; RootHub->DeviceSpeed = MaxSpeed; // // 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 ); // // Start USB Host Controller // UsbBusDev->Usb3HCInterface->SetState ( UsbBusDev->Usb3HCInterface, EfiUsbHcStateOperational ); // // Do root hub enumeration at first time (high speed only) // RootHubEnumeration(1, RootHubController); // // Convert SMM instance of RootHubEnumeration to HC_EVENT_CONNECT_CHANGE callback // RootHubEnumerationFunc = RootHubEnumeration; mUsbCore->AddressConvert ( SMM_ADDRESS, (VOID*)(UINTN)RootHubEnumerationFunc, (VOID**)&RootHubEnumerationFunc ); // // Register Callback for HC_EVENT_CONNECT_CHANGE Event // UsbBusDev->Usb3HCInterface->RegisterEvent ( UsbBusDev->Usb3HCInterface, HC_EVENT_CONNECT_CHANGE, RootHubEnumerationFunc, RootHubController, &RootHubController->HubNotify ); // // Setup Smm address convert table for Smm security policy // mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_BODY, UsbBusDev, sizeof (USB_BUS_CONTROLLER_DEVICE) ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbBusDev->Usb3HCInterface, 1 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbBusDev->Root, 1 ); // // Insert IoDevice and attached IoDeviceController into SMM convert table // InsertUsbIoDevAddressConvertTable(RootHub, TRUE); Status = EFI_SUCCESS; goto exit; free_roothub: mUsbCore->FreeBuffer ( sizeof (USB_IO_DEVICE), RootHub ); uninstall_protocol: gBS->UninstallProtocolInterface ( Controller, &mUsbBusProtocolGuid, &UsbBusDev->BusIdentify ); close_devicepath_protocol: gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); free_usbbusdev: mUsbCore->FreeBuffer ( sizeof (USB_BUS_CONTROLLER_DEVICE), UsbBusDev ); close_hc_protocol: gBS->CloseProtocol ( Controller, &gEfiUsb3HcProtocolGuid, This->DriverBindingHandle, Controller ); gBS->CloseProtocol ( Controller, &gEfiUsb2HcProtocolGuid, This->DriverBindingHandle, Controller ); exit: return Status; } /** Stop this driver on ControllerHandle. Support stoping any child handles created by this driver. @param This Protocol instance pointer. @param Controller Handle of device to stop driver on @param NumberOfChildren Number of Children in the ChildHandleBuffer @param ChildHandleBuffer List of handles for the children we need to stop. @retval EFI_SUCCESS @retval EFI_DEVICE_ERROR @retval others **/ EFI_STATUS EFIAPI UsbBusControllerDriverStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ) { EFI_STATUS Status; USB_IO_DEVICE *RootHub; USB_IO_CONTROLLER_DEVICE *RootHubController; USB_BUS_CONTROLLER_DEVICE *UsbBusDev; EFI_USB_BUS_PROTOCOL *UsbIdentifier; UINT8 Index2; USB_IO_CONTROLLER_DEVICE *UsbController; USB_IO_DEVICE *UsbIoDev; USB_IO_CONTROLLER_DEVICE *HubController; UINTN Index; EFI_USB_IO_PROTOCOL *UsbIo; // // Initialize global variables // 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); UsbIoDev = UsbController->UsbDevice; HubController = UsbController->Parent; UsbDeviceDeConfiguration (UsbIoDev); for (Index2 = 0; Index2 < HubController->DownstreamPorts; Index2++) { if (HubController->Children[Index2] == UsbIoDev) { 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; } UsbBusDev = USB_BUS_CONTROLLER_DEVICE_FROM_THIS (UsbIdentifier); // // Stop USB Host Controller // // Report Status Code here since we will reset the host controller // ReportUsbStatusCode ( UsbBusDev, EFI_PROGRESS_CODE, EFI_IO_BUS_USB | EFI_IOB_PC_RESET ); // // Uninstall USB Bus Protocol // gBS->UninstallProtocolInterface ( Controller, &mUsbBusProtocolGuid, &UsbBusDev->BusIdentify ); UsbBusDev->Usb3HCInterface->SetState ( UsbBusDev->Usb3HCInterface, EfiUsbHcStateHalt ); // // Deconfiguration all its devices // RootHub = UsbBusDev->Root; RootHubController = RootHub->UsbController[0]; UsbBusDev->Usb3HCInterface->UnregisterEvent ( UsbBusDev->Usb3HCInterface, RootHubController->HubNotify ); for (Index2 = 0; Index2 < RootHubController->DownstreamPorts; Index2++) { if (RootHubController->Children[Index2]) { UsbDeviceDeConfiguration (RootHubController->Children[Index2]); RootHubController->Children[Index2] = NULL; } } // // Uninstall Usb3Hc thunk instance // UninstallUsb3HcThunk (UsbBusDev->Usb3HCInterface); if (RootHubController->UefiDevicePath != NULL) { gBS->FreePool(RootHubController->UefiDevicePath); } if (RootHubController->DevicePath != NULL) { mUsbCore->FreeBuffer ( DevicePathSize (RootHubController->DevicePath), RootHubController->DevicePath ); } if (RootHubController->RemainingDevicePath != NULL) { mUsbCore->FreeBuffer ( DevicePathSize(RootHubController->RemainingDevicePath), RootHubController->RemainingDevicePath ); } mUsbCore->FreeBuffer ( sizeof (USB_IO_CONTROLLER_DEVICE), RootHubController ); mUsbCore->FreeBuffer ( sizeof (USB_IO_DEVICE), RootHub ); mUsbCore->FreeBuffer ( sizeof (USB_BUS_CONTROLLER_DEVICE), UsbBusDev ); // // Remove Smm address convert table // mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_BODY, UsbBusDev ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbBusDev->Usb3HCInterface ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbBusDev->Root ); // // Remove IoDevice and attached IoDeviceController from SMM convert table // RemoveUsbIoDevAddressConvertTable(RootHub); RemoveUsbIoControllerDevAddressConvertTable(RootHubController); // // Close USB_HC_PROTOCOL & DEVICE_PATH_PROTOCOL // Opened by this Controller // gBS->CloseProtocol ( Controller, &gEfiUsb3HcProtocolGuid, This->DriverBindingHandle, Controller ); gBS->CloseProtocol ( Controller, &gEfiUsb2HcProtocolGuid, This->DriverBindingHandle, Controller ); gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); return EFI_SUCCESS; } /** Configurate a new device attached to the usb bus @param ParentHubController Parent Hub which this device is connected. @param HostController Host Controller handle @param ParentPort Parent Hub port which this device is connected. @param UsbIoDev The device to be configured. @retval EFI_SUCCESS @retval EFI_DEVICE_ERROR @retval EFI_OUT_OF_RESOURCES **/ STATIC EFI_STATUS UsbDeviceConfiguration ( IN USB_IO_CONTROLLER_DEVICE *ParentHubController, IN EFI_HANDLE HostController, IN UINT8 ParentPort, IN USB_IO_DEVICE *UsbIoDev ) { UINT8 DevAddress; UINT8 Index; EFI_STATUS Status; UINT32 Result; EFI_USB_IO_PROTOCOL *UsbIo; UINT8 NumOfInterface; USB_IO_CONTROLLER_DEVICE *FirstController; USB_BUS_CONTROLLER_DEVICE *UsbBusDev; USB_IO_CONTROLLER_DEVICE *UsbIoController; EFI_DEVICE_PATH_PROTOCOL *DevicePath; UINT8 UsbDevicePath[sizeof(USB_DEVICE_PATH) + END_DEVICE_PATH_LENGTH]; USB_DEVICE_PATH *UsbNode; EFI_USB_HID_DESCRIPTOR *HidDescriptor; POST_CODE (BDS_CONNECT_USB_BUS); UsbBusDev = UsbIoDev->BusController; // // Build the Usb device path // UsbNode = (USB_DEVICE_PATH*)UsbDevicePath; UsbNode->Header.Type = MESSAGING_DEVICE_PATH; UsbNode->Header.SubType = MSG_USB_DP; SetDevicePathNodeLength (&UsbNode->Header, sizeof(USB_DEVICE_PATH)); UsbNode->InterfaceNumber = 0; UsbNode->ParentPortNumber = ParentPort; SetDevicePathEndNode ((EFI_DEVICE_PATH_PROTOCOL*)(UsbNode + 1)); DevicePath = CopyDevicePath (mUsbCore, ParentHubController->DevicePath, &UsbNode->Header); UsbSetTransactionTranslator ( ParentHubController, ParentPort, UsbIoDev ); // // Since a USB device must have at least one interface, // so create this instance first // FirstController = CreateUsbIoControllerDevice (UsbBusDev); if (FirstController == NULL) { return EFI_OUT_OF_RESOURCES; } FirstController->UsbDevice = UsbIoDev; UsbIoDev->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 // if (UsbIoDev->DeviceSpeed == EFI_USB_SPEED_SUPER) { UsbIoDev->DeviceDescriptor.BcdUSB = 0x300; UsbIoDev->DeviceDescriptor.MaxPacketSize0 = 9; } else { UsbIoDev->DeviceDescriptor.MaxPacketSize0 = 8; } // // Some wireless keyboard doesn't accept 8 bytes length for GetDescriptor command, // set the length to sizeof (EFI_USB_DEVICE_DESCRIPTOR) for it // Status = mUsbCore->UsbGetDescriptor ( UsbIo, (USB_DESC_TYPE_DEVICE << 8), 0, (UsbIoDev->DeviceSpeed == EFI_USB_SPEED_LOW) ? sizeof (EFI_USB_DEVICE_DESCRIPTOR) : 8, &UsbIoDev->DeviceDescriptor, &Result ); if (!EFI_ERROR (Status)) { DEBUG ( (gUSBDebugLevel, "Get Device Descriptor Success, MaxPacketSize0 = 0x%x\n", UsbIoDev->DeviceDescriptor.MaxPacketSize0) ); } else { 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")); Status = EFI_DEVICE_ERROR; goto free_first_controller; } DevAddress = UsbAllocateDeviceAddress ( UsbIoDev->BusController->Root->DeviceSpeed, UsbIoDev->BusController->AddressPool ); if (DevAddress == 0) { DEBUG ((gUSBErrorLevel, "Cannot allocate address\n")); Status = EFI_OUT_OF_RESOURCES; goto free_first_controller; } // // Stall 1ms after GetDescriptor for specific device compatibility(0.1ms minimum) // mUsbCore->Stall (1000); Status = UsbSetDeviceAddress (UsbIo, DevAddress, &Result); if (EFI_ERROR (Status)) { DEBUG ((gUSBErrorLevel, "Set address error\n")); ReportUsbStatusCode ( UsbBusDev, EFI_ERROR_CODE | EFI_ERROR_MINOR, EFI_IO_BUS_USB | EFI_IOB_EC_WRITE_ERROR ); // // Free device address which allocated to the device // UsbFreeDeviceAddress ( DevAddress, UsbIoDev->BusController->AddressPool ); Status = EFI_DEVICE_ERROR; goto free_first_controller; } UsbIoDev->DeviceAddress = DevAddress; // // SetAddress Complete Time by Spec, Max 50ms // mUsbCore->Stall (10 * 1000); // // Get the whole device descriptor // Status = mUsbCore->UsbGetDescriptor ( UsbIo, (USB_DESC_TYPE_DEVICE << 8), 0, sizeof (EFI_USB_DEVICE_DESCRIPTOR), &UsbIoDev->DeviceDescriptor, &Result ); if (EFI_ERROR (Status)) { 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 ); // // Return EFI_UNSUPPORTED to keep the device address // Status = EFI_UNSUPPORTED; goto free_first_controller; } // // Get & parse all configurations for this device, including // all configuration descriptors, all interface descriptors, all // endpoint descriptors // Status = UsbGetAllConfigurations (UsbIoDev); if (EFI_ERROR (Status)) { 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 ); // // Return EFI_UNSUPPORTED to keep the device address // Status = EFI_UNSUPPORTED; goto free_first_controller; } // // Stall 1ms after GetDescriptor for specific device compatibility(0.1ms minimum) // mUsbCore->Stall (1000); // // Set the 1st configuration value // Status = UsbSetDefaultConfiguration (UsbIoDev); if (EFI_ERROR (Status)) { 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 ); // // Return EFI_UNSUPPORTED to keep the device address // Status = EFI_UNSUPPORTED; goto free_configurations; } UsbIoDev->IsConfigured = TRUE; #if (KEEP_USBIO_FOR_IGNORED_DEVICE == 0) // // Knock off the ignored device after get VID/PID // Status = mUsbCore->CheckIgnoredDevice(DevicePath, UsbIo); if (EFI_ERROR(Status)) { // // Return EFI_UNSUPPORTED to keep the device address // Status = EFI_UNSUPPORTED; goto free_configurations; } #endif // // Process GetString if device required // if (IsGetStringRequiredDevice (UsbIoDev)) { UsbGetStringtable (UsbIoDev); UsbIoDev->LangIDConfigured = TRUE; } // // Create USB_IO_CONTROLLER_DEVICE for // each detected interface // FirstController->CurrentConfigValue = UsbIoDev->ActiveConfig->CongfigDescriptor.ConfigurationValue; FirstController->AlternateSetting = GetFirstAlternateSetting (&FirstController->UsbIo); // // As some device will remain on last(not first) alternate setting even being port reset, // set first(default) alternate setting for corresponding interface to ensure // it is on proper alternate setting // SetFirstAlternateSetting (&FirstController->UsbIo); // // Register the HID descriptor to UsbCore if available // if ((HidDescriptor = GetHidDescriptor (&FirstController->UsbIo)) != NULL) { mUsbCore->RegisterHidDescriptor (&FirstController->UsbIo, HidDescriptor); } NumOfInterface = UsbIoDev->ActiveConfig->CongfigDescriptor.NumInterfaces; // // Maximum interfaces checking // if (NumOfInterface > USB_MAXINTERFACES) { NumOfInterface = USB_MAXINTERFACES; } UsbIoDev->NumOfInterfaces = NumOfInterface; // // Backup the device pathes // FirstController->DevicePath = DevicePath; if (FirstController->Parent->RemainingDevicePath) { FirstController->RemainingDevicePath = CopyDevicePath (mUsbCore, FirstController->Parent->RemainingDevicePath, NULL); } for (Index = 1; Index < NumOfInterface; Index++) { UsbIoController = CreateUsbIoControllerDevice (UsbBusDev); if (UsbIoController == NULL) { Status = EFI_OUT_OF_RESOURCES; goto free_configurations; } UsbIoController->UsbDevice = UsbIoDev; UsbIoController->CurrentConfigValue = UsbIoDev->ActiveConfig->CongfigDescriptor.ConfigurationValue; UsbIoController->InterfaceNumber = UsbNode->InterfaceNumber = Index; UsbIoDev->UsbController[Index] = UsbIoController; UsbIoController->ParentPort = ParentPort; UsbIoController->Parent = ParentHubController; UsbIoController->HostController = HostController; // // First copy the USB_IO Protocol instance // InitializeUsbIoInstance (UsbIoController); UsbIoController->AlternateSetting = GetFirstAlternateSetting (&UsbIoController->UsbIo); // // As some device will remain on last(not first) alternate setting even being port reset, // set first(default) alternate setting for corresponding interface to ensure // it is on proper alternate setting // SetFirstAlternateSetting (&UsbIoController->UsbIo); // // Register the HID descriptor to UsbCore if available // if ((HidDescriptor = GetHidDescriptor (&UsbIoController->UsbIo)) != NULL) { mUsbCore->RegisterHidDescriptor (&UsbIoController->UsbIo, HidDescriptor); } // // Allocate new DevicePath for this interface // DevicePath = CopyDevicePath (mUsbCore, ParentHubController->DevicePath, &UsbNode->Header); // // Backup the device pathes // UsbIoController->DevicePath = DevicePath; if (UsbIoController->Parent->RemainingDevicePath) { UsbIoController->RemainingDevicePath = CopyDevicePath (mUsbCore, UsbIoController->Parent->RemainingDevicePath, NULL); } } // // Insert IoDevice and attached IoDeviceController into SMM convert table // InsertUsbIoDevAddressConvertTable(UsbIoDev, FALSE); Status = EFI_SUCCESS; goto exit; free_configurations: // // Free all resouces allocated for all its configurations // UsbDestroyAllConfiguration (UsbIoDev); free_first_controller: mUsbCore->FreeBuffer ( DevicePathSize(DevicePath), DevicePath ); mUsbCore->FreeBuffer( sizeof (USB_IO_CONTROLLER_DEVICE), FirstController ); UsbIoDev->UsbController[0] = NULL; exit: return Status; } /** Remove Device, Device Handles, Uninstall Protocols. @param UsbIoDev The device to be deconfigured. @retval EFI_SUCCESS @retval EFI_DEVICE_ERROR **/ EFI_STATUS UsbDeviceDeConfiguration ( IN USB_IO_DEVICE *UsbIoDev ) { USB_IO_CONTROLLER_DEVICE *UsbController; UINT8 index; USB_IO_DEVICE *ChildDevice; UINT8 Index; EFI_USB_IO_PROTOCOL *UsbIo; USB_BUS_CONTROLLER_DEVICE *UsbBusDev; UINTN Mode; // // Double check UsbIoDevice exists // if (UsbIoDev == NULL) { return EFI_SUCCESS; } UsbBusDev = UsbIoDev->BusController; mUsbCore->GetMode(&Mode); // // Disable to device(used for Xhci) // CancelIo ( UsbBusDev->Usb3HCInterface, UsbIoDev->DeviceAddress, 0, 0, NULL ); for (index = 0; index < UsbIoDev->NumOfInterfaces; index++) { // // Check if it is a hub, if so, de configuration all its // downstream ports // UsbController = UsbIoDev->UsbController[index]; // // Check the controller pointer // if (UsbController == NULL) { continue; } // // Set the inactive flag to signal this device is going to be removed // UsbController->Inactive = TRUE; 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; UsbAsyncInterruptTransfer ( UsbIo, UsbController->HubEndpointAddress, FALSE, 0, 0, NULL, NULL ); if (NULL != UsbController->HubNotify) { UsbBusDev->Usb3HCInterface->UnregisterEvent( UsbBusDev->Usb3HCInterface, UsbController->HubNotify ); } for (Index = 0; Index < UsbController->DownstreamPorts; Index++) { if (UsbController->Children[Index]) { ChildDevice = UsbController->Children[Index]; UsbDeviceDeConfiguration (ChildDevice); UsbController->Children[Index] = NULL; } } } // // Unregister HID descriptor whether available or not // mUsbCore->UnregisterHidDescriptor (&UsbController->UsbIo); if (Mode == USB_CORE_RUNTIME_MODE || IsInLegacyProcess (&UsbController->UsbIo)) { // // Disconnect Usb devices during legacy mode for hot plug // mUsbCore->DisconnectUsbDevices( &UsbController->UsbIo ); } if (Mode != USB_CORE_RUNTIME_MODE) { // // Timer avaliable under POST time, use timer callback to disconnect // the device to make the disconnection under appropriate TPL // mUsbCore->RegisterNonSmmCallback( ConnectControllerCallback, DISCONNECT_CONTROLLER, UsbController ); } else { // // Free IoControllerDevice resource immediately under runtime // FreeUsbIoControllerDevice(UsbController); } UsbIoDev->UsbController[index] = NULL; } if (Mode != USB_CORE_RUNTIME_MODE) { // // Use timer callback to free the allocated resources to make the operation under // appropriate TPL // mUsbCore->RegisterNonSmmCallback( FreeUsbIoDeviceCallback, 0, UsbIoDev ); } else { // // Free UsbIo device resources immediately under runtime // FreeUsbIoDeviceCallback(0, UsbIoDev); } return EFI_SUCCESS; } /** Whenever hub interrupt occurs, this routine will be called to check which event happens. @param Data Hub interrupt transfer data. @param DataLength The length of the Data. @param Context Hub Controller Device. @param Result Hub interrupt transfer status. @retval EFI_SUCCESS @retval EFI_DEVICE_ERROR **/ STATIC EFI_STATUS EFIAPI OnHubInterruptComplete ( IN VOID *Data, IN UINTN DataLength, IN VOID *Context, IN UINT32 Result ) { USB_IO_CONTROLLER_DEVICE *HubController; 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) { mUsbCore->UsbClearEndpointHalt ( UsbIo, HubController->HubEndpointAddress, &UsbResult ); } // // Delete & Submit this interrupt again // 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 // UsbAsyncInterruptTransfer ( UsbIo, HubController->HubEndpointAddress, TRUE, 128, (HubController->DownstreamPorts / 8) + 1, OnHubInterruptComplete, HubController ); return EFI_DEVICE_ERROR; } if (DataLength == 0 || Data == NULL) { return EFI_SUCCESS; } CopyMem ( &HubController->StatusChangePort, Data, (HubController->DownstreamPorts / 8) + 1 ); HubEnumeration(0, HubController); return EFI_SUCCESS; } /** This is USB RootHub enumerator @param Event Indicating which event is signaled @param Context actually it is a USB_IO_DEVICE **/ STATIC VOID EFIAPI RootHubEnumeration ( IN UINTN SyncEnumeration, IN VOID *Context ) { USB_IO_CONTROLLER_DEVICE *HubController; EFI_USB_PORT_STATUS HubPortStatus; EFI_STATUS Status; UINT8 Index; UINT8 Index1; USB_IO_DEVICE *UsbIoDev; USB_BUS_CONTROLLER_DEVICE *UsbBusDev; EFI_HANDLE HostController; USB_IO_DEVICE *OldUsbIoDev; USB_IO_DEVICE *NewDevice; USB_IO_CONTROLLER_DEVICE *NewController; UINT8 Index2; EFI_USB_IO_PROTOCOL *UsbIo; UINTN Mode; UINTN HandleCount; EFI_HANDLE *HandleBuffer; EFI_USB_BUS_PROTOCOL *UsbBusProtocol; USB_BUS_CONTROLLER_DEVICE *CHCHubController; EFI_DEVICE_PATH_PROTOCOL *DevicePath; UINT8 UsbDevicePath[sizeof(USB_DEVICE_PATH) + END_DEVICE_PATH_LENGTH]; USB_DEVICE_PATH *UsbNode; UINTN RetryCount; BOOLEAN AsyncRetried; HubController = (USB_IO_CONTROLLER_DEVICE *) Context; HostController = HubController->HostController; UsbBusDev = HubController->UsbDevice->BusController; AsyncRetried = FALSE; // // Skip the root port enumeration if it is triggered by root port reset to // preventing port state from destroyed by unnecessary port enumeration // if (!SyncEnumeration && HubController->PortResetProcessing) return; // // Root hub has the address 1 // UsbIoDev = HubController->UsbDevice; for (Index = 0; Index < HubController->DownstreamPorts && Index < USB_MAXCHILDREN; Index++) { //[-start-220117-YUNLEI0159-modify]// #ifdef LCFC_SUPPORT if(Index <= 2){ mUsbCore->Stall (20 * 1000); } #endif //[-end-220117-YUNLEI0159-modify]// GetRootHubPortStatus ( UsbBusDev->Usb3HCInterface, Index, (EFI_USB_PORT_STATUS *) &HubPortStatus ); if (!IsPortConnectChange (HubPortStatus.PortChangeStatus) && ((HubController->Children[Index] == NULL && IsPortConnect (HubPortStatus.PortStatus)) || (HubController->Children[Index] != NULL && !IsPortConnect (HubPortStatus.PortStatus)))) { // // Some root port doesn't show connect changed but it did connected after system reset // We force the connect changed bit set to make it detectable // HubPortStatus.PortChangeStatus = 1; } if (HubPortStatus.PortChangeStatus == 0) { continue; } // // Clear some change status // for (Index2 = 0; Index2 < sizeof(mPortChangeMap) / sizeof(USB_CHANGE_FEATURE_MAP); Index2 ++) { if (HubPortStatus.PortChangeStatus & mPortChangeMap[Index2].ChangedBit) { // // Clear Hub port change bit // ClearRootHubPortFeature ( UsbBusDev->Usb3HCInterface, Index, mPortChangeMap[Index2].Feature ); } } // // Check the overcurrent status // if (HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_OVERCURRENT) { // // Bypass if overcurrent // if (HubPortStatus.PortStatus & USB_PORT_STAT_OVERCURRENT) { continue; } } if (IsPortConnectChange (HubPortStatus.PortChangeStatus) || IsPortResetChange (HubPortStatus.PortChangeStatus)) { if (IsPortConnect (HubPortStatus.PortStatus)) { // // There is something connected to this port // DEBUG ((gUSBDebugLevel, "Something connected to Root Hub at Port0x%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... // OldUsbIoDev = HubController->Children[Index]; if (NULL != OldUsbIoDev) { UsbDeviceDeConfiguration (OldUsbIoDev); HubController->Children[Index] = NULL; } #if (KEEP_USBIO_FOR_IGNORED_DEVICE == 0) // // Check is it should be ignored before port reset // UsbNode = (USB_DEVICE_PATH*)UsbDevicePath; UsbNode->Header.Type = MESSAGING_DEVICE_PATH; UsbNode->Header.SubType = MSG_USB_DP; SetDevicePathNodeLength (&UsbNode->Header, sizeof(USB_DEVICE_PATH)); UsbNode->InterfaceNumber = 0; UsbNode->ParentPortNumber = Index; SetDevicePathEndNode ((EFI_DEVICE_PATH_PROTOCOL*)(UsbNode + 1)); DevicePath = CopyDevicePath (mUsbCore, HubController->DevicePath, &UsbNode->Header); Status = mUsbCore->CheckIgnoredDevice(DevicePath, NULL); mUsbCore->FreeBuffer ( DevicePathSize(DevicePath), DevicePath ); if (EFI_ERROR(Status)) { // // This device should be skipped! // continue; } #endif if (!SyncEnumeration) { // // Stall 30ms to waiting for signal stable. Hot-plug only // mUsbCore->Stall(30 * 1000); } // // Set retry once for the situation of device configuration error // RetryCount = 1; FAIL_RETRY: // // Reset the port and get the exact speed // Status = ResetRootPort( HubController, Index, (UINT32*)&HubPortStatus ); if (EFI_ERROR (Status)) { // // Ignore enumeration when reset error // continue; } if (!IsPortConnect (HubPortStatus.PortStatus)) { // // Ignore enumeration when port disconnected // if (SyncEnumeration && (HubController->UsbDevice->DeviceSpeed == EFI_USB_SPEED_HIGH) && (HubPortStatus.PortStatus & USB_PORT_STAT_OWNER)) { // // The device is full/low speed and has released the ownership from EHC to CHC // Do sync root hub enumerate from CHC if SyncEnumeration on // HandleBuffer = NULL; Status = gBS->LocateHandleBuffer ( ByProtocol, &mUsbBusProtocolGuid, NULL, &HandleCount, &HandleBuffer ); if (!EFI_ERROR (Status) && HandleBuffer) { for (Index1 = 0; Index1 < (UINT8)HandleCount; Index1 ++) { Status = gBS->HandleProtocol ( HandleBuffer[Index1], &mUsbBusProtocolGuid, (VOID **)&UsbBusProtocol ); if (EFI_ERROR (Status)) { continue; } CHCHubController = USB_BUS_CONTROLLER_DEVICE_FROM_THIS(UsbBusProtocol); if (CHCHubController->Root->DeviceSpeed == EFI_USB_SPEED_FULL) { // // This is full/low speed HC // RootHubEnumeration (SyncEnumeration, CHCHubController->Root->UsbController[0]); } } gBS->FreePool (HandleBuffer); } } Index = 0xff; continue; } Status = mUsbCore->AllocateBuffer( sizeof (USB_IO_DEVICE), ALIGNMENT_32, (VOID **)&NewDevice ); if (EFI_ERROR (Status)) { return; } // // Initialize some fields by copying data from its parents // NewDevice->DeviceDescriptor.MaxPacketSize0 = 8; NewDevice->BusController = UsbIoDev->BusController; // // Setup the device speed // if (HubPortStatus.PortStatus & USB_PORT_STAT_LOW_SPEED) { DEBUG ((gUSBDebugLevel, "Low Speed Device Attached to Hub\n")); NewDevice->DeviceSpeed = EFI_USB_SPEED_LOW; } else if (HubPortStatus.PortStatus & USB_PORT_STAT_HIGH_SPEED) { DEBUG ((gUSBDebugLevel, "High Speed Device Attached to Hub\n")); NewDevice->DeviceSpeed = EFI_USB_SPEED_HIGH; } else if (HubPortStatus.PortStatus & USB_PORT_STAT_SUPER_SPEED) { DEBUG ((gUSBDebugLevel, "Super Speed Device Attached to Hub\n")); NewDevice->DeviceSpeed = EFI_USB_SPEED_SUPER; } else { DEBUG ((gUSBDebugLevel, "Full Speed Device Attached to Hub\n")); NewDevice->DeviceSpeed = EFI_USB_SPEED_FULL; } // // Remove the RemainingDevicePath once initialization finish // if (!SyncEnumeration && HubController->RemainingDevicePath) { mUsbCore->FreeBuffer ( DevicePathSize(HubController->RemainingDevicePath), HubController->RemainingDevicePath ); HubController->RemainingDevicePath = NULL; } // // Configure that device // Status = UsbDeviceConfiguration ( HubController, HostController, Index, NewDevice ); // // Add this device to the usb bus tree // HubController->Children[Index] = NewDevice; if (EFI_ERROR (Status)) { // // Keep the resource to occupy the device address if device in the address state // if (Status != EFI_UNSUPPORTED) { mUsbCore->FreeBuffer( sizeof (USB_IO_DEVICE), NewDevice ); HubController->Children[Index] = NULL; if (Status == EFI_DEVICE_ERROR) { if (RetryCount == 0) { if (SyncEnumeration) UsbBusDev->AsyncRetry = 3; if (UsbBusDev->AsyncRetry) { // // Clear port enable bit to workaround some device failed on configuration issue // ClearRootHubPortFeature ( UsbBusDev->Usb3HCInterface, Index, EfiUsbPortEnable ); AsyncRetried = TRUE; } } else { // // Port reset retry upon device configuration returns EFI_DEVICE_ERROR for below situations: // 1. Sometime the device will reconnected after first time port reset // 2. Some USB 3.0 devices plugged in USB 2.0 port after reset from OS // RetryCount --; // // Stall 10ms to waiting for signal stable // mUsbCore->Stall (10 * 1000); goto FAIL_RETRY; } } } continue; } for (Index2 = 0; Index2 < NewDevice->NumOfInterfaces; Index2++) { // // If this device is hub, add to the hub index // NewController = NewDevice->UsbController[Index2]; mUsbCore->GetMode(&Mode); if (Mode == USB_CORE_RUNTIME_MODE || IsInLegacyProcess (&NewController->UsbIo)) { // // Connect Usb devices during legacy mode for hot plug // mUsbCore->ConnectUsbDevices ( &NewController->UsbIo, NewController->DevicePath ); } if (Mode != USB_CORE_RUNTIME_MODE) { // // Timer avaliable under POST time, use timer callback to connect // the device to make the connection under appropriate TPL // mUsbCore->RegisterNonSmmCallback( ConnectControllerCallback, CONNECT_CONTROLLER, NewController ); } if (IsHub (NewController)) { NewController->IsUsbHub = TRUE; // // Configure Hub // Status = DoHubConfig (NewController); if (EFI_ERROR (Status)) { continue; } // // Add request to do query hub status // change endpoint. // UsbIo = &NewController->UsbIo; // // Do Hub Enumeration // NewController->StatusChangePort = 0xffff; HubEnumeration(SyncEnumeration, NewController); UsbAsyncInterruptTransfer ( UsbIo, NewController->HubEndpointAddress, TRUE, 128, (NewController->DownstreamPorts / 8) + 1, OnHubInterruptComplete, NewController ); } } if (IsSpecialStallDevice (NewDevice)) { Index = 0xff; continue; } // // Rescan check to ensure no port status changed in whole scanned port // if (SyncEnumeration) { for (Index2 = 0; Index2 < Index; Index2 ++) { GetRootHubPortStatus ( UsbBusDev->Usb3HCInterface, Index2, (EFI_USB_PORT_STATUS *) &HubPortStatus ); if (IsPortConnectChange (HubPortStatus.PortChangeStatus) || IsPortResetChange (HubPortStatus.PortChangeStatus)) { Index = Index2 - 1; break; } } } } else { // // Something disconnected from USB root hub // DEBUG ((gUSBDebugLevel, "Something disconnected from Root Hub at Port0x%x\n", Index)); OldUsbIoDev = HubController->Children[Index]; UsbDeviceDeConfiguration (OldUsbIoDev); HubController->Children[Index] = NULL; } } } if (!SyncEnumeration && AsyncRetried && UsbBusDev->AsyncRetry) UsbBusDev->AsyncRetry --; } /** This is Usb Hub enumerator @param Event Indicating which event is signaled @param Context actually it is a USB_IO_DEVICE **/ STATIC VOID HubEnumeration ( IN UINTN SyncEnumeration, IN VOID *Context ) { USB_IO_CONTROLLER_DEVICE *HubController; EFI_USB_PORT_STATUS HubPortStatus; EFI_STATUS Status; USB_BUS_CONTROLLER_DEVICE *UsbBusDev; EFI_HANDLE HostController; USB_IO_DEVICE *OldUsbIoDev; USB_IO_DEVICE *NewDevice; USB_IO_CONTROLLER_DEVICE *NewController; UINT16 StatusChangePort; UINT8 Index; UINT8 Index2; EFI_USB_IO_PROTOCOL *UsbIo; UINTN Mode; EFI_DEVICE_PATH_PROTOCOL *DevicePath; UINT8 UsbDevicePath[sizeof(USB_DEVICE_PATH) + END_DEVICE_PATH_LENGTH]; USB_DEVICE_PATH *UsbNode; UINTN RetryCount; BOOLEAN AsyncRetried; HubController = (USB_IO_CONTROLLER_DEVICE *) Context; HostController = HubController->HostController; UsbBusDev = HubController->UsbDevice->BusController; StatusChangePort = HubController->StatusChangePort; HubController->StatusChangePort = 0; AsyncRetried = FALSE; // // Skip the hub port enumeration if it is triggered by hub port reset to // preventing port state from destroyed by unnecessary port enumeration // if (!SyncEnumeration && HubController->PortResetProcessing) return; // // Scan which port has status change // Bit 0 stands for hub itself, other bit stands for // the corresponding port // for (Index = 1; Index <= HubController->DownstreamPorts && Index <= USB_MAXCHILDREN; Index++) { if (!(StatusChangePort & (1 << Index))) { continue; } // // Check which event took place at that port // UsbIo = &HubController->UsbIo; Status = HubGetPortStatus ( UsbIo, Index, (UINT32 *) &HubPortStatus ); if (!IsPortConnectChange (HubPortStatus.PortChangeStatus) && ((HubController->Children[Index - 1] == NULL && IsPortConnect (HubPortStatus.PortStatus)) || (HubController->Children[Index - 1] != NULL && !IsPortConnect (HubPortStatus.PortStatus)))) { // // Some hub port doesn't show connect changed but it did connected after system reset // We force the connect changed bit set to make it detectable // HubPortStatus.PortChangeStatus = 1; } if (EFI_ERROR (Status) || HubPortStatus.PortChangeStatus == 0) { continue; } // // Clear some change status // for (Index2 = 0; Index2 < sizeof(mPortChangeMap) / sizeof(USB_CHANGE_FEATURE_MAP); Index2 ++) { if (HubPortStatus.PortChangeStatus & mPortChangeMap[Index2].ChangedBit) { // // Clear Hub port change bit // HubClearPortFeature ( UsbIo, Index, mPortChangeMap[Index2].Feature ); } } // // Check the overcurrent status // if (HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_OVERCURRENT) { // // Bypass if overcurrent // if (HubPortStatus.PortStatus & USB_PORT_STAT_OVERCURRENT) { continue; } } if (IsPortConnectChange (HubPortStatus.PortChangeStatus) || IsPortResetChange (HubPortStatus.PortChangeStatus)) { 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... // OldUsbIoDev = HubController->Children[Index - 1]; if (NULL != OldUsbIoDev) { UsbDeviceDeConfiguration (OldUsbIoDev); HubController->Children[Index - 1] = NULL; } #if (KEEP_USBIO_FOR_IGNORED_DEVICE == 0) // // Check is it should be ignored before port reset // UsbNode = (USB_DEVICE_PATH*)UsbDevicePath; UsbNode->Header.Type = MESSAGING_DEVICE_PATH; UsbNode->Header.SubType = MSG_USB_DP; SetDevicePathNodeLength (&UsbNode->Header, sizeof(USB_DEVICE_PATH)); UsbNode->InterfaceNumber = 0; UsbNode->ParentPortNumber = Index - 1; SetDevicePathEndNode ((EFI_DEVICE_PATH_PROTOCOL*)(UsbNode + 1)); DevicePath = CopyDevicePath (mUsbCore, HubController->DevicePath, &UsbNode->Header); Status = mUsbCore->CheckIgnoredDevice(DevicePath, NULL); mUsbCore->FreeBuffer ( DevicePathSize(DevicePath), DevicePath ); if (EFI_ERROR(Status)) { // // This device should be skipped! // continue; } #endif if (!SyncEnumeration) { // // Stall 30ms to waiting for signal stable. Hot-plug only // mUsbCore->Stall(30 * 1000); } // // Set retry once for the situation of device configuration error // RetryCount = 1; FAIL_RETRY: // // There is something connected to this port, // reset that port // Status = ResetHubPort ( HubController, Index, (UINT32*)&HubPortStatus ); if (EFI_ERROR (Status)) { // // Ignore enumeration when reset error // continue; } if (!IsPortConnect (HubPortStatus.PortStatus)) { // // Ignore enumeration when port disconnected // Index = 0; continue; } Status = mUsbCore->AllocateBuffer( sizeof (USB_IO_DEVICE), ALIGNMENT_32, (VOID **)&NewDevice ); if (EFI_ERROR (Status)) { continue; } // // Initialize some fields // NewDevice->DeviceDescriptor.MaxPacketSize0 = 8; NewDevice->BusController = HubController->UsbDevice->BusController; // // Setup the device speed // if (HubPortStatus.PortStatus & USB_PORT_STAT_LOW_SPEED) { DEBUG ((gUSBDebugLevel, "Low Speed Device Attached to Hub\n")); NewDevice->DeviceSpeed = EFI_USB_SPEED_LOW; } else if (HubPortStatus.PortStatus & USB_PORT_STAT_HIGH_SPEED) { DEBUG ((gUSBDebugLevel, "High Speed Device Attached to Hub\n")); NewDevice->DeviceSpeed = EFI_USB_SPEED_HIGH; } else if (HubPortStatus.PortStatus & USB_PORT_STAT_SUPER_SPEED) { DEBUG ((gUSBDebugLevel, "Super Speed Device Attached to Hub\n")); NewDevice->DeviceSpeed = EFI_USB_SPEED_SUPER; } else { DEBUG ((gUSBDebugLevel, "Full Speed Device Attached to Hub\n")); NewDevice->DeviceSpeed = EFI_USB_SPEED_FULL; } // // Remove the RemainingDevicePath once initialization finish // if (!SyncEnumeration && HubController->RemainingDevicePath) { mUsbCore->FreeBuffer ( DevicePathSize(HubController->RemainingDevicePath), HubController->RemainingDevicePath ); HubController->RemainingDevicePath = NULL; } // // Setup the topology level // NewDevice->HubDepth = HubController->UsbDevice->HubDepth + 1; if (NewDevice->HubDepth <= 5) { // // Configure that device // Status = UsbDeviceConfiguration ( HubController, HostController, (Index - 1), NewDevice ); // // Add this device to the usb bus tree. Index is begin from 1 due to 0 is Hub self, // HubController->Children[Index - 1] = NewDevice; } else { // // The HUB depth large then 5, unsupported // Status = EFI_NOT_FOUND; } if (EFI_ERROR (Status)) { // // Keep the resource to occupy the device address if device in the address state // if (Status != EFI_UNSUPPORTED) { mUsbCore->FreeBuffer( sizeof (USB_IO_DEVICE), NewDevice ); HubController->Children[Index - 1] = NULL; if (Status == EFI_DEVICE_ERROR) { if (RetryCount == 0) { if (SyncEnumeration) UsbBusDev->AsyncRetry = 3; if (UsbBusDev->AsyncRetry) { // // Clear port enable bit to workaround some device failed on configuration issue // HubClearPortFeature ( UsbIo, Index, EfiUsbPortEnable ); AsyncRetried = TRUE; } } else { // // Port reset retry upon device configuration returns EFI_DEVICE_ERROR for below situations: // 1. Sometime the device will reconnected after first time port reset // 2. Some USB 3.0 devices plugged in USB 2.0 port after reset from OS // RetryCount --; // // Stall 10ms to waiting for signal stable // mUsbCore->Stall (10 * 1000); goto FAIL_RETRY; } } } continue; } // // Setup interfaces derive from this NewDevice // for (Index2 = 0; Index2 < NewDevice->NumOfInterfaces; Index2++) { // // If this device is hub, add to the hub index // NewController = NewDevice->UsbController[Index2]; mUsbCore->GetMode(&Mode); if (Mode == USB_CORE_RUNTIME_MODE || IsInLegacyProcess (&NewController->UsbIo)) { // // Connect Usb devices during legacy mode for hot plug // mUsbCore->ConnectUsbDevices ( &NewController->UsbIo, NewController->DevicePath ); } if (Mode != USB_CORE_RUNTIME_MODE) { // // Timer avaliable under POST time, use timer callback to connect // the device to make the connection under appropriate TPL // mUsbCore->RegisterNonSmmCallback( ConnectControllerCallback, CONNECT_CONTROLLER, NewController ); } if (IsHub (NewController)) { NewController->IsUsbHub = TRUE; // // Configure Hub // Status = DoHubConfig (NewController); if (EFI_ERROR (Status)) { continue; } // // Add request to do query hub status // change endpoint // UsbIo = &NewController->UsbIo; // // Do Hub Enumeration // NewController->StatusChangePort = 0xffff; HubEnumeration(SyncEnumeration, NewController); UsbAsyncInterruptTransfer ( UsbIo, NewController->HubEndpointAddress, TRUE, 128, (NewController->DownstreamPorts / 8) + 1, OnHubInterruptComplete, NewController ); } } if (IsSpecialStallDevice (NewDevice)) { Index = 0; } } else { // // Something disconnected from USB hub // DEBUG ((gUSBDebugLevel, "Something Device Detached on Hub port\n")); OldUsbIoDev = HubController->Children[Index - 1]; UsbDeviceDeConfiguration (OldUsbIoDev); HubController->Children[Index - 1] = NULL; } } } if (!SyncEnumeration && AsyncRetried && UsbBusDev->AsyncRetry) UsbBusDev->AsyncRetry --; } /** Allocate a structure for USB_IO_CONTROLLER_DEVICE @param UsbBusDev Pointer of USB_BUS_CONTROLLER_DEVICE @retval A pointer to a USB_IO_CONTROLLER_DEVICE structure, Or NULL. **/ STATIC USB_IO_CONTROLLER_DEVICE * CreateUsbIoControllerDevice ( IN USB_BUS_CONTROLLER_DEVICE *UsbBusDev ) { EFI_STATUS Status; USB_IO_CONTROLLER_DEVICE *UsbIoControllerDev; // // Allocate USB_IO_CONTROLLER_DEVICE structure // Status = mUsbCore->AllocateBuffer( sizeof (USB_IO_CONTROLLER_DEVICE), ALIGNMENT_32, (VOID **)&UsbIoControllerDev ); if (EFI_ERROR (Status)) { return NULL; } UsbIoControllerDev->Signature = USB_IO_CONTROLLER_SIGNATURE; return UsbIoControllerDev; } /** Resets and reconfigures the USB controller. This function will work for all USB devices except USB Hub Controllers. @param This Indicates the calling context. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER @retval EFI_DEVICE_ERROR **/ EFI_STATUS EFIAPI UsbPortReset ( IN EFI_USB_IO_PROTOCOL *This ) { USB_IO_CONTROLLER_DEVICE *UsbIoController; USB_IO_DEVICE *ParentIoDev; USB_IO_DEVICE *UsbIoDev; USB_IO_CONTROLLER_DEVICE *ParentController; USB_BUS_CONTROLLER_DEVICE *UsbBusDev; UINT8 HubPort; UINT32 Status; EFI_STATUS Result; EFI_USB_IO_PROTOCOL *UsbIo; UINT8 Address; UsbIoController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (This); if (IsHub (UsbIoController)) { return EFI_INVALID_PARAMETER; } // // Since at this time, this device has already been configured, // it needs to be re-configured. // ParentController = UsbIoController->Parent; ParentIoDev = ParentController->UsbDevice; UsbIoDev = UsbIoController->UsbDevice; HubPort = UsbIoController->ParentPort; UsbBusDev = UsbIoDev->BusController; // // Disable device (used for Xhci) before port reset to preventing some // Xhci from failed on port reset issue // CancelIo ( UsbBusDev->Usb3HCInterface, UsbIoDev->DeviceAddress, 0, 0, NULL ); // // Signal the PortResetProcessing flag to preventing unnecessary hub port enumeration // ParentController->PortResetProcessing = TRUE; if (ParentIoDev->DeviceAddress == 1) { DEBUG ((gUSBDebugLevel, "Reset from Root Hub 0x%x\n", HubPort)); ResetRootPort (ParentController, HubPort, NULL); } else { DEBUG ((gUSBDebugLevel, "Reset from Hub, Addr 0x%x\n", ParentIoDev->DeviceAddress)); ResetHubPort (ParentController, HubPort + 1, NULL); } // // Clears the PortResetProcessing flag for normal operation // ParentController->PortResetProcessing = FALSE; // // 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; } // // Get & parse all configurations for this device (used for Xhci) // Result = UsbGetAllConfigurations (UsbIoDev); if (EFI_ERROR (Result)) { return EFI_DEVICE_ERROR; } // // Stall 1ms after GetDescriptor for specific device compatibility(0.1ms minimum) // mUsbCore->Stall (1000); // // Set the device to the default configuration // Result = UsbSetDefaultConfiguration (UsbIoDev); if (EFI_ERROR (Result)) { return EFI_DEVICE_ERROR; } return EFI_SUCCESS; } /** Reset Root Hub port. @param UsbBusDev Bus controller of the device. @param PortNum The given port to be reset. @param PortStatus Pointer of the port status for output @retval EFI_SUCCESS @retval EFI_DEVICE_ERROR **/ EFI_STATUS ResetRootPort ( IN USB_IO_CONTROLLER_DEVICE *UsbIoController, IN UINT8 PortNum, OUT UINT32 *PortStatus ) { USB_BUS_CONTROLLER_DEVICE *UsbBusDev; EFI_STATUS Status; EFI_USB_PORT_STATUS HubPortStatus; UINTN Timeout; BOOLEAN PortReleased; PortReleased = FALSE; UsbBusDev = UsbIoController->UsbDevice->BusController; // // Only high/full speed device need to port reset if the device controlled by EHCI // GetRootHubPortStatus ( UsbBusDev->Usb3HCInterface, PortNum, &HubPortStatus ); // // Check is it in suspend state // if (HubPortStatus.PortStatus & USB_PORT_STAT_SUSPEND) { // // Clear suspend root port // Status = ClearRootHubPortFeature ( UsbBusDev->Usb3HCInterface, PortNum, EfiUsbPortSuspend ); mUsbCore->Stall (50 * 1000); } if (!(HubPortStatus.PortStatus & USB_PORT_STAT_OWNER) && !(HubPortStatus.PortStatus & USB_PORT_STAT_HIGH_SPEED)) { // // Release the port to CHC directly if low speed device attached in EHCI port // ReleasePortToCHC (UsbBusDev, PortNum); // // Get latest port status to know whether the HC has CHC or not // GetRootHubPortStatus ( UsbBusDev->Usb3HCInterface, PortNum, &HubPortStatus ); if (HubPortStatus.PortStatus & USB_PORT_STAT_OWNER) PortReleased = TRUE; } if (!PortReleased) { // // In order to preventing port from reset during port disconnected state, // doing port connection check(3000ms timeout) before reset to make reset workable // for (Timeout = 0; Timeout < 3000 && PortStatus != NULL; Timeout ++) { GetRootHubPortStatus ( UsbBusDev->Usb3HCInterface, PortNum, &HubPortStatus ); if (HubPortStatus.PortStatus & USB_PORT_STAT_CONNECTION) break; mUsbCore->Stall (1000); } // // Reset root port // Status = SetRootHubPortFeature ( UsbBusDev->Usb3HCInterface, PortNum, EfiUsbPortReset ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } // // Stall 50ms for the duration of port reset // mUsbCore->Stall (50 * 1000); // // For EHCI and UHCI, the port reset must be cleared by software. // Status = ClearRootHubPortFeature ( UsbBusDev->Usb3HCInterface, PortNum, EfiUsbPortReset ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } // // Stall 2ms for port reset signal stable // mUsbCore->Stall (2 * 1000); // // For XHCI and OHCI, the port reset is cleared by host controller. // It is needed to waiting for this bit for the completion of // port reset in 50ms timeout // Timeout = 50; do { GetRootHubPortStatus ( UsbBusDev->Usb3HCInterface, PortNum, &HubPortStatus ); if (!(HubPortStatus.PortStatus & USB_PORT_STAT_RESET)) break; mUsbCore->Stall (1000); Timeout --; } while (Timeout > 0); // // Clear the port reset change // Status = ClearRootHubPortFeature ( UsbBusDev->Usb3HCInterface, PortNum, EfiUsbPortResetChange ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } // // Stall 1ms after clear reset changed bit for signal stable // mUsbCore->Stall (1000); // // Get latest port status // GetRootHubPortStatus ( UsbBusDev->Usb3HCInterface, PortNum, &HubPortStatus ); if (!(HubPortStatus.PortStatus & USB_PORT_STAT_OWNER)) { // // EHC Port Owner. Proceed full speed device detection procedure // if ((HubPortStatus.PortStatus & USB_PORT_STAT_HIGH_SPEED) && !(HubPortStatus.PortStatus & USB_PORT_STAT_ENABLE)) { // // Stall 10 ms due to we just issued a "enabled test" for full speed device and need to stall awhile // mUsbCore->Stall (10 * 1000); // // Release ownership to CHC due to this is full/low speed device attached in EHC // ReleasePortToCHC (UsbBusDev, PortNum); // // Get latest port status to know whether the HC has CHC or not // GetRootHubPortStatus ( UsbBusDev->Usb3HCInterface, PortNum, &HubPortStatus ); if (HubPortStatus.PortStatus & USB_PORT_STAT_OWNER) PortReleased = TRUE; // // Full speed device attached in EHCI port // HubPortStatus.PortStatus &= ~USB_PORT_STAT_HIGH_SPEED; } } else { // // CHC Port Owner. Set the enable manually(after reset) because CHC can't enable automatically // Status = SetRootHubPortFeature ( UsbBusDev->Usb3HCInterface, PortNum, EfiUsbPortEnable ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } // // Stall 20ms after enabled the port // mUsbCore->Stall (20 * 1000); } } if (!PortReleased) { if (HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_ENABLE) { Status = ClearRootHubPortFeature ( UsbBusDev->Usb3HCInterface, PortNum, EfiUsbPortEnableChange ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } } if (HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_CONNECTION) { Status = ClearRootHubPortFeature ( UsbBusDev->Usb3HCInterface, PortNum, EfiUsbPortConnectChange ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } } } // // Return the HubPortStatus to caller // if (PortStatus) *PortStatus = *(UINT32*)&HubPortStatus; return EFI_SUCCESS; } /** Reset Hub port. @param UsbIoController The USB_IO_CONTROLLER_DEVICE instance. @param PortIndex The given port to be reset. @param PortStatus Pointer of the port status for output @retval EFI_SUCCESS @retval EFI_DEVICE_ERROR **/ EFI_STATUS ResetHubPort ( IN USB_IO_CONTROLLER_DEVICE *UsbIoController, IN UINT8 PortIndex, OUT UINT32 *PortStatus ) { EFI_STATUS Status; USB_BUS_CONTROLLER_DEVICE *UsbBusDev; EFI_USB_IO_PROTOCOL *UsbIo; EFI_USB_PORT_STATUS HubPortStatus; UINTN Number; UINTN Index; UINTN ResetRetry; ASSERT (UsbIoController->IsUsbHub == TRUE); UsbBusDev = UsbIoController->UsbDevice->BusController; UsbIo = &UsbIoController->UsbIo; // // In order to preventing port from reset during port disconnected state, // doing port connection check(3000ms timeout) before reset to make reset workable // for (Index = 0; Index < 3000 && PortStatus != NULL; Index ++) { Status = HubGetPortStatus ( UsbIo, PortIndex, (UINT32 *) &HubPortStatus ); if (EFI_ERROR (Status)) return Status; if (HubPortStatus.PortStatus & USB_PORT_STAT_CONNECTION) break; mUsbCore->Stall (1000); } ResetRetry = 0; RESET_RETRY: // // Reset hub port // HubSetPortFeature ( UsbIo, PortIndex, EfiUsbPortReset ); mUsbCore->Stall (20 * 1000); // // Wait for port reset complete // Number = 2000; do { Status = HubGetPortStatus ( UsbIo, PortIndex, (UINT32 *) &HubPortStatus ); if (Status == EFI_DEVICE_ERROR && ResetRetry < 2) { // // Reset port again to recover the babble error on Intel virtual HUB // ResetRetry ++; goto RESET_RETRY; } if (EFI_ERROR (Status) || (!EFI_ERROR (Status) && (HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET))) break; mUsbCore->Stall (1000); Number --; } while (Number > 0); if (EFI_ERROR (Status) || Number == 0) { // // Cannot reset port, return error // return EFI_DEVICE_ERROR; } Number = 10; do { for (Index = 0; Index < sizeof(mPortChangeMap) / sizeof(USB_CHANGE_FEATURE_MAP); Index ++) { if (HubPortStatus.PortChangeStatus & mPortChangeMap[Index].ChangedBit) { // // Clear Hub port change bit // HubClearPortFeature ( UsbIo, PortIndex, mPortChangeMap[Index].Feature ); } } // // Stall 50 ms to waiting for signal stable // mUsbCore->Stall (50 * 1000); if (UsbIoController->UsbDevice->DeviceDescriptor.IdVendor == 0x8087 && (HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_CONNECTION) && ResetRetry < 2) { // // Reset port again to recover the device reconnected during the HUB port reset period on Intel RMH hub // ResetRetry ++; goto RESET_RETRY; } // // Read status again // HubGetPortStatus ( UsbIo, PortIndex, (UINT32 *) &HubPortStatus ); Number --; } while (HubPortStatus.PortChangeStatus != 0 && Number > 0); // // Return the HubPortStatus to caller // if (PortStatus) { *PortStatus = *(UINT32*)&HubPortStatus; // // SuperSpeed Hub Status Conversion // if (UsbIoController->UsbDevice->DeviceSpeed == EFI_USB_SPEED_SUPER) { (*PortStatus) &= ~0x0000FFE0; if (HubPortStatus.PortStatus & USB_PORT_STAT_SUPER_SPEED_POWER) { // // Convert Port Power Status // (*PortStatus) |= USB_PORT_STAT_POWER; } if (((HubPortStatus.PortStatus & USB_PORT_STAT_SUPER_SPEED_MASK) == 0) || ((HubPortStatus.PortStatus & USB_PORT_STAT_SUPER_SPEED_MASK) == 0x800)) { // // Convert Port Speed Status // (*PortStatus) |= USB_PORT_STAT_SUPER_SPEED; } } } return EFI_SUCCESS; } /** Report a error Status code of USB bus driver controller @param UsbBusDev USB_BUS_CONTROLLER_DEVICE @param Type EFI_STATUS_CODE_TYPE @param Code EFI_STATUS_CODE_VALUE **/ STATIC EFI_STATUS ReportUsbStatusCode ( IN USB_BUS_CONTROLLER_DEVICE *UsbBusDev, IN EFI_STATUS_CODE_TYPE Type, IN EFI_STATUS_CODE_VALUE Code ) { EFI_STATUS Status; BOOLEAN InSmm; Status = EFI_SUCCESS; // // Only report status code during POST and non-SMM mode // if (!mInSmram) { mUsbCore->IsInSmm (&InSmm); if (!InSmm) { Status = ReportStatusCodeWithDevicePath ( Type, Code, UsbBusDev->DevicePath ); } } return Status; } /** Set Transaction Translator parameter @param ParentHubController Controller structure of the parent Hub device @param ParentPort Number of parent port @param Device Structure of the device @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES Cannot allocate resources **/ STATIC EFI_STATUS UsbSetTransactionTranslator ( IN USB_IO_CONTROLLER_DEVICE *ParentHubController, IN UINT8 ParentPort, IN OUT USB_IO_DEVICE *Device ) { UINT8 DeviceAddress; UINT8 Port; UINT8 MultiTT; // // Inherit Route String from parent // Device->Translator.RouteString = ParentHubController->UsbDevice->Translator.RouteString; // // Initial the route string for XHC // if (ParentHubController->IsUsbHub && Device->HubDepth != 0) { Device->Translator.RouteString &= ~(0x0f << (4 * (Device->HubDepth - 1))); Device->Translator.RouteString |= (ParentPort + 1) << (4 * (Device->HubDepth - 1)); } // // Inherit RootHub Port Number from parent // if (Device->HubDepth == 0) { // // Level 1 device, records the RootHub Port Number // Device->Translator.RootHubPortNumber = ParentPort + 1; } else { // // Not level 1 device, inherit RootHub Port Number from parent // Device->Translator.RootHubPortNumber = ParentHubController->UsbDevice->Translator.RootHubPortNumber; if (Device->DeviceSpeed == EFI_USB_SPEED_LOW || Device->DeviceSpeed == EFI_USB_SPEED_FULL) { // // Only full/low speed device need // if (EFI_USB_SPEED_HIGH == ParentHubController->UsbDevice->DeviceSpeed) { // // Parent is high speed, then parent is our translator // DeviceAddress = ParentHubController->UsbDevice->DeviceAddress; Port = ParentPort + 1; MultiTT = ParentHubController->MultiTT; } else { // // Use parent's translator. // DeviceAddress = ParentHubController->UsbDevice->Translator.TranslatorHubAddress; Port = ParentHubController->UsbDevice->Translator.TranslatorPortNumber; MultiTT = ParentHubController->UsbDevice->Translator.MultiTT; } Device->Translator.TranslatorHubAddress = DeviceAddress; Device->Translator.TranslatorPortNumber = Port; Device->Translator.MultiTT = MultiTT; } } return EFI_SUCCESS; } /** Set bit to release the port owner to CHC @param UsbBusDev UsbBus controller structure of the device @param PortNum Number of the port @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR Fail **/ STATIC EFI_STATUS ReleasePortToCHC ( USB_BUS_CONTROLLER_DEVICE *UsbBusDev, UINT8 PortNum ) { EFI_STATUS Status; // // Full/Low speed device need to stall 1 ms between last ClearRootHubPortFeature and ReleasePortToCHC // mUsbCore->Stall (1000); Status = SetRootHubPortFeature ( UsbBusDev->Usb3HCInterface, PortNum, EfiUsbPortOwner ); mUsbCore->Stall (100 * 1000); // // The EfiUsbPortOwner will cause PortConnectChange set. Clear it!!! // Status = ClearRootHubPortFeature ( UsbBusDev->Usb3HCInterface, PortNum, EfiUsbPortConnectChange ); return Status; } /** Calculate the size of device path @param DevicePath Pointer of EFI_DEVICE_PATH_PROTOCOL @retval Size of device path **/ STATIC UINTN DevicePathSize ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath ) { EFI_DEVICE_PATH_PROTOCOL *Start; if (DevicePath == NULL) { return 0; } // // Search for the end of the device path structure // Start = DevicePath; while (!IsDevicePathEnd (DevicePath)) { DevicePath = NextDevicePathNode (DevicePath); } // // Compute the size and add back in the size of the end device path structure // return ((UINTN) DevicePath - (UINTN) Start) + sizeof (EFI_DEVICE_PATH_PROTOCOL); } /** Function is used to append a Src1 and Src2 together. @param UsbCore UsbCore protocol @param Src1 A pointer to a device path data structure. @param Src2 A pointer to a device path data structure. @retval A pointer to the new device path is returned. NULL is returned if space for the new device path could not be allocated from pool. It is up to the caller to free the memory used by Src1 and Src2 if they are no longer needed. **/ STATIC EFI_DEVICE_PATH_PROTOCOL * CopyDevicePath ( IN EFI_USB_CORE_PROTOCOL *UsbCore, IN EFI_DEVICE_PATH_PROTOCOL *Src1, IN EFI_DEVICE_PATH_PROTOCOL *Src2 ) { EFI_STATUS Status; UINTN Size; UINTN Size1; UINTN Size2; EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; EFI_DEVICE_PATH_PROTOCOL *SecondDevicePath; // // Allocate space for the combined device path. It only has one end node of // length EFI_DEVICE_PATH_PROTOCOL // Size1 = DevicePathSize (Src1); Size2 = DevicePathSize (Src2); Size = Size1 + Size2; if (Size1 != 0 && Size2 != 0) { Size -= sizeof (EFI_DEVICE_PATH_PROTOCOL); } if (UsbCore == NULL) { NewDevicePath = AllocatePool (Size); if (NewDevicePath == NULL) { return NULL; } } else { Status = UsbCore->AllocateBuffer( Size, ALIGNMENT_32, (VOID **)&NewDevicePath ); if (EFI_ERROR (Status)) { return NULL; } } CopyMem (NewDevicePath, Src1, Size1); // // Over write Src1 EndNode and do the copy // if (Size1 != 0) { SecondDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) ((CHAR8 *) NewDevicePath + (Size1 - sizeof (EFI_DEVICE_PATH_PROTOCOL))); } else { SecondDevicePath = NewDevicePath; } if (Size2 != 0) { CopyMem (SecondDevicePath, Src2, Size2); } return NewDevicePath; } /** This callback got called by timer during outside of SMM @param Event Type @param Context Context **/ STATIC VOID EFIAPI ConnectControllerCallback( IN UINTN Event, IN VOID *Context ) { EFI_STATUS Status; USB_IO_CONTROLLER_DEVICE *UsbController; EFI_USB3_HC_PROTOCOL *Usb3HcProtocol; EFI_USB2_HC_PROTOCOL *Usb2HcProtocol; UINTN Index; Usb3HcProtocol = NULL; Usb2HcProtocol = NULL; UsbController = (USB_IO_CONTROLLER_DEVICE*)Context; if (Event == CONNECT_CONTROLLER) { // // In order to prevent the UEFI OS boot hang, allocate the buffer of DevicePath in BS for UEFI OS // UsbController->UefiDevicePath = CopyDevicePath(NULL, UsbController->DevicePath, NULL); Status = gBS->InstallMultipleProtocolInterfaces ( &UsbController->Handle, &gEfiDevicePathProtocolGuid, UsbController->UefiDevicePath, &gEfiUsbIoProtocolGuid, &UsbController->UsbIo, NULL ); if (!EFI_ERROR (Status)) { Status = gBS->OpenProtocol ( UsbController->HostController, &gEfiUsb3HcProtocolGuid, (VOID **)&Usb3HcProtocol, mPrivate->DriverBindingHandle, UsbController->Handle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); if (EFI_ERROR (Status)) { Status = gBS->OpenProtocol ( UsbController->HostController, &gEfiUsb2HcProtocolGuid, (VOID **)&Usb2HcProtocol, gUsbBusDriverBinding.DriverBindingHandle, UsbController->Handle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); } if (!EFI_ERROR (Status)) { gBS->ConnectController ( UsbController->Handle, NULL, UsbController->RemainingDevicePath, TRUE ); } } if (EFI_ERROR (Status)) { ReportUsbStatusCode ( UsbController->UsbDevice->BusController, EFI_ERROR_CODE | EFI_ERROR_MINOR, EFI_IO_BUS_USB | EFI_IOB_EC_INTERFACE_ERROR ); // // Remove the ptr placed in UsbIoDev table // for (Index = 0; Index < UsbController->UsbDevice->NumOfInterfaces; Index ++) { if (UsbController->UsbDevice->UsbController[Index] == UsbController) { UsbController->UsbDevice->UsbController[Index] = NULL; break; } } if (UsbController->UefiDevicePath != NULL) { gBS->FreePool(UsbController->UefiDevicePath); } // // Free IoControllerDevice due to error // FreeUsbIoControllerDevice(UsbController); } } else { if (UsbController->Handle) { gBS->DisconnectController ( UsbController->Handle, NULL, NULL ); // // remove child handle reference to the USB_HC_PROTOCOL // Status = gBS->CloseProtocol ( UsbController->HostController, &gEfiUsb3HcProtocolGuid, gUsbBusDriverBinding.DriverBindingHandle, UsbController->Handle ); if (EFI_ERROR (Status)) { Status = gBS->CloseProtocol ( UsbController->HostController, &gEfiUsb2HcProtocolGuid, gUsbBusDriverBinding.DriverBindingHandle, UsbController->Handle ); } // // Uninstall DEVICE_PATH_PROTOCOL installed on this handle // Status = gBS->UninstallProtocolInterface ( UsbController->Handle, &gEfiDevicePathProtocolGuid, UsbController->UefiDevicePath ); if (!EFI_ERROR(Status) && UsbController->UefiDevicePath != NULL) { gBS->FreePool(UsbController->UefiDevicePath); } // // Uninstall EFI_USB_IO_PROTOCOL along due to it may not be unstalled(caused by someone still opened the protocol) // gBS->UninstallProtocolInterface ( UsbController->Handle, &gEfiUsbIoProtocolGuid, &UsbController->UsbIo ); } // // Free IoControllerDevice // FreeUsbIoControllerDevice(UsbController); } } /** This callback got called by timer during outside of SMM @param Event Type @param Context Context **/ STATIC VOID EFIAPI FreeUsbIoDeviceCallback( IN UINTN Event, IN VOID *Context ) { USB_IO_DEVICE *UsbIoDev; USB_BUS_CONTROLLER_DEVICE *UsbBusDev; UsbIoDev = (USB_IO_DEVICE*)Context; UsbBusDev = UsbIoDev->BusController; // // Free address for later use // UsbFreeDeviceAddress ( UsbIoDev->DeviceAddress, UsbBusDev->AddressPool ); // // Free all resouces allocated for all its configurations // UsbDestroyAllConfiguration (UsbIoDev); mUsbCore->FreeBuffer ( sizeof (USB_IO_DEVICE), UsbIoDev ); // // Remove Smm address convert table // RemoveUsbIoDevAddressConvertTable(UsbIoDev); } /** Free IoControllerDevice @param UsbController UsbController **/ STATIC VOID FreeUsbIoControllerDevice( IN USB_IO_CONTROLLER_DEVICE *UsbController ) { if (UsbController->DevicePath != NULL) { mUsbCore->FreeBuffer ( DevicePathSize(UsbController->DevicePath), UsbController->DevicePath ); } if (UsbController->RemainingDevicePath != NULL) { mUsbCore->FreeBuffer ( DevicePathSize(UsbController->RemainingDevicePath), UsbController->RemainingDevicePath ); } mUsbCore->FreeBuffer ( sizeof (USB_IO_CONTROLLER_DEVICE), UsbController ); // // Remove Smm address convert table // RemoveUsbIoControllerDevAddressConvertTable(UsbController); } /** Insert IoDevice and attached IoControllerDevices into SMM convert table @param UsbIoDev UsbIoDevice **/ STATIC VOID InsertUsbIoDevAddressConvertTable( IN USB_IO_DEVICE *UsbIoDev, IN BOOLEAN IsRootHub ) { UINTN Index; mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_BODY, UsbIoDev, sizeof (USB_IO_DEVICE) ); if (!IsRootHub) { // // The ConfigDescList only present in non root hub // mUsbCore->InsertAddressConvertTable ( ACT_LINKING_LIST, &UsbIoDev->ConfigDescListHead, 1 ); } mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbIoDev->ActiveConfig, 1 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbIoDev->BusController, 1 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, UsbIoDev->UsbController, UsbIoDev->NumOfInterfaces ); for (Index = 0; Index < UsbIoDev->NumOfInterfaces; Index++) { if (!IsRootHub) { // // The body of UsbIo protocol only present in non root hub // mUsbCore->InsertAddressConvertTable ( ACT_FUNCTION_POINTER, &UsbIoDev->UsbController[Index]->UsbIo, sizeof(EFI_USB_IO_PROTOCOL) / sizeof(VOID*) ); } mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_BODY, UsbIoDev->UsbController[Index], sizeof (USB_IO_CONTROLLER_DEVICE) ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbIoDev->UsbController[Index]->UsbDevice, 1 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbIoDev->UsbController[Index]->Parent, 1 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, UsbIoDev->UsbController[Index]->Children, USB_MAXCHILDREN ); } } /** Remove IoDevice from SMM convert table @param UsbIoDev UsbIoDevice **/ STATIC VOID RemoveUsbIoDevAddressConvertTable( IN USB_IO_DEVICE *UsbIoDev ) { mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_BODY, UsbIoDev ); mUsbCore->RemoveAddressConvertTable ( ACT_LINKING_LIST, &UsbIoDev->ConfigDescListHead ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbIoDev->ActiveConfig ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbIoDev->BusController ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, UsbIoDev->UsbController ); } /** Remove IoControllerDevice from SMM convert table @param UsbController UsbController **/ STATIC VOID RemoveUsbIoControllerDevAddressConvertTable( IN USB_IO_CONTROLLER_DEVICE *UsbController ) { mUsbCore->RemoveAddressConvertTable ( ACT_FUNCTION_POINTER, &UsbController->UsbIo ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_BODY, UsbController ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbController->UsbDevice ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbController->Parent ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, UsbController->Children ); }