/** @file Usb Bus Driver Binding and Bus IO Protocol. Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "UsbBus.h" EFI_USB_IO_PROTOCOL mUsbIoProtocol = { UsbIoControlTransfer, UsbIoBulkTransfer, UsbIoAsyncInterruptTransfer, UsbIoSyncInterruptTransfer, UsbIoIsochronousTransfer, UsbIoAsyncIsochronousTransfer, UsbIoGetDeviceDescriptor, UsbIoGetActiveConfigDescriptor, UsbIoGetInterfaceDescriptor, UsbIoGetEndpointDescriptor, UsbIoGetStringDescriptor, UsbIoGetSupportedLanguages, UsbIoPortReset }; EFI_DRIVER_BINDING_PROTOCOL mUsbBusDriverBinding = { UsbBusControllerDriverSupported, UsbBusControllerDriverStart, UsbBusControllerDriverStop, 0xa, NULL, NULL }; /** USB_IO function to execute a control transfer. This function will execute the USB transfer. If transfer successes, it will sync the internal state of USB bus with device state. @param This The USB_IO instance @param Request The control transfer request @param Direction Direction for data stage @param Timeout The time to wait before timeout @param Data The buffer holding the data @param DataLength Then length of the data @param UsbStatus USB result @retval EFI_INVALID_PARAMETER The parameters are invalid @retval EFI_SUCCESS The control transfer succeeded. @retval Others Failed to execute the transfer **/ EFI_STATUS EFIAPI UsbIoControlTransfer ( IN EFI_USB_IO_PROTOCOL *This, IN EFI_USB_DEVICE_REQUEST *Request, IN EFI_USB_DATA_DIRECTION Direction, IN UINT32 Timeout, IN OUT VOID *Data OPTIONAL, IN UINTN DataLength OPTIONAL, OUT UINT32 *UsbStatus ) { USB_DEVICE *Dev; USB_INTERFACE *UsbIf; USB_ENDPOINT_DESC *EpDesc; EFI_TPL OldTpl; EFI_STATUS Status; UINTN RequestedDataLength; if (UsbStatus == NULL) { return EFI_INVALID_PARAMETER; } OldTpl = gBS->RaiseTPL (USB_BUS_TPL); UsbIf = USB_INTERFACE_FROM_USBIO (This); Dev = UsbIf->Device; RequestedDataLength = DataLength; Status = UsbHcControlTransfer ( Dev->Bus, Dev->Address, Dev->Speed, Dev->MaxPacket0, Request, Direction, Data, &DataLength, (UINTN)Timeout, &Dev->Translator, UsbStatus ); // // If the request completed successfully and the Direction of the request is // EfiUsbDataIn or EfiUsbDataOut, then make sure the actual number of bytes // transferred is the same as the number of bytes requested. If a different // number of bytes were transferred, then return EFI_DEVICE_ERROR. // if (!EFI_ERROR (Status)) { if ((Direction != EfiUsbNoData) && (DataLength != RequestedDataLength)) { Status = EFI_DEVICE_ERROR; goto ON_EXIT; } } if (EFI_ERROR (Status) || (*UsbStatus != EFI_USB_NOERROR)) { // // Clear TT buffer when CTRL/BULK split transaction failes // Clear the TRANSLATOR TT buffer, not parent's buffer // ASSERT (Dev->Translator.TranslatorHubAddress < Dev->Bus->MaxDevices); if (Dev->Translator.TranslatorHubAddress != 0) { UsbHubCtrlClearTTBuffer ( Dev->Bus->Devices[Dev->Translator.TranslatorHubAddress], Dev->Translator.TranslatorPortNumber, Dev->Address, 0, USB_ENDPOINT_CONTROL ); } goto ON_EXIT; } // // Some control transfer will change the device's internal // status, such as Set_Configuration and Set_Interface. // We must synchronize the bus driver's status with that in // device. We ignore the Set_Descriptor request because it's // hardly used by any device, especially in pre-boot environment // // // Reset the endpoint toggle when endpoint stall is cleared // if ((Request->Request == USB_REQ_CLEAR_FEATURE) && (Request->RequestType == USB_REQUEST_TYPE ( EfiUsbNoData, USB_REQ_TYPE_STANDARD, USB_TARGET_ENDPOINT )) && (Request->Value == USB_FEATURE_ENDPOINT_HALT)) { EpDesc = UsbGetEndpointDesc (UsbIf, (UINT8)Request->Index); if (EpDesc != NULL) { EpDesc->Toggle = 0; } } // // Select a new configuration. This is a dangerous action. Upper driver // should stop use its current UsbIo after calling this driver. The old // UsbIo will be uninstalled and new UsbIo be installed. We can't use // ReinstallProtocol since interfaces in different configuration may be // completely irrelevant. // if ((Request->Request == USB_REQ_SET_CONFIG) && (Request->RequestType == USB_REQUEST_TYPE ( EfiUsbNoData, USB_REQ_TYPE_STANDARD, USB_TARGET_DEVICE ))) { // // Don't re-create the USB interfaces if configuration isn't changed. // if ((Dev->ActiveConfig != NULL) && (Request->Value == Dev->ActiveConfig->Desc.ConfigurationValue)) { goto ON_EXIT; } DEBUG ((DEBUG_INFO, "UsbIoControlTransfer: configure changed!!! Do NOT use old UsbIo!!!\n")); if (Dev->ActiveConfig != NULL) { UsbRemoveConfig (Dev); } if (Request->Value != 0) { Status = UsbSelectConfig (Dev, (UINT8)Request->Value); } // // Exit now, Old USB_IO is invalid now // goto ON_EXIT; } // // A new alternative setting is selected for the interface. // No need to reinstall UsbIo in this case because only // underlying communication endpoints are changed. Functionality // should remains the same. // if ((Request->Request == USB_REQ_SET_INTERFACE) && (Request->RequestType == USB_REQUEST_TYPE ( EfiUsbNoData, USB_REQ_TYPE_STANDARD, USB_TARGET_INTERFACE )) && (Request->Index == UsbIf->IfSetting->Desc.InterfaceNumber)) { Status = UsbSelectSetting (UsbIf->IfDesc, (UINT8)Request->Value); if (!EFI_ERROR (Status)) { ASSERT (UsbIf->IfDesc->ActiveIndex < USB_MAX_INTERFACE_SETTING); UsbIf->IfSetting = UsbIf->IfDesc->Settings[UsbIf->IfDesc->ActiveIndex]; } } ON_EXIT: gBS->RestoreTPL (OldTpl); return Status; } /** Execute a bulk transfer to the device endpoint. @param This The USB IO instance. @param Endpoint The device endpoint. @param Data The data to transfer. @param DataLength The length of the data to transfer. @param Timeout Time to wait before timeout. @param UsbStatus The result of USB transfer. @retval EFI_SUCCESS The bulk transfer is OK. @retval EFI_INVALID_PARAMETER Some parameters are invalid. @retval Others Failed to execute transfer, reason returned in UsbStatus. **/ EFI_STATUS EFIAPI UsbIoBulkTransfer ( IN EFI_USB_IO_PROTOCOL *This, IN UINT8 Endpoint, IN OUT VOID *Data, IN OUT UINTN *DataLength, IN UINTN Timeout, OUT UINT32 *UsbStatus ) { USB_DEVICE *Dev; USB_INTERFACE *UsbIf; USB_ENDPOINT_DESC *EpDesc; UINT8 BufNum; UINT8 Toggle; EFI_TPL OldTpl; EFI_STATUS Status; if ((USB_ENDPOINT_ADDR (Endpoint) == 0) || (USB_ENDPOINT_ADDR (Endpoint) > 15) || (UsbStatus == NULL)) { return EFI_INVALID_PARAMETER; } OldTpl = gBS->RaiseTPL (USB_BUS_TPL); UsbIf = USB_INTERFACE_FROM_USBIO (This); Dev = UsbIf->Device; EpDesc = UsbGetEndpointDesc (UsbIf, Endpoint); if ((EpDesc == NULL) || (USB_ENDPOINT_TYPE (&EpDesc->Desc) != USB_ENDPOINT_BULK)) { Status = EFI_INVALID_PARAMETER; goto ON_EXIT; } BufNum = 1; Toggle = EpDesc->Toggle; Status = UsbHcBulkTransfer ( Dev->Bus, Dev->Address, Endpoint, Dev->Speed, EpDesc->Desc.MaxPacketSize, BufNum, &Data, DataLength, &Toggle, Timeout, &Dev->Translator, UsbStatus ); EpDesc->Toggle = Toggle; if (EFI_ERROR (Status) || (*UsbStatus != EFI_USB_NOERROR)) { // // Clear TT buffer when CTRL/BULK split transaction failes. // Clear the TRANSLATOR TT buffer, not parent's buffer // ASSERT (Dev->Translator.TranslatorHubAddress < Dev->Bus->MaxDevices); if (Dev->Translator.TranslatorHubAddress != 0) { UsbHubCtrlClearTTBuffer ( Dev->Bus->Devices[Dev->Translator.TranslatorHubAddress], Dev->Translator.TranslatorPortNumber, Dev->Address, 0, USB_ENDPOINT_BULK ); } } ON_EXIT: gBS->RestoreTPL (OldTpl); return Status; } /** Execute a synchronous interrupt transfer. @param This The USB IO instance. @param Endpoint The device endpoint. @param Data The data to transfer. @param DataLength The length of the data to transfer. @param Timeout Time to wait before timeout. @param UsbStatus The result of USB transfer. @retval EFI_SUCCESS The synchronous interrupt transfer is OK. @retval EFI_INVALID_PARAMETER Some parameters are invalid. @retval Others Failed to execute transfer, reason returned in UsbStatus. **/ EFI_STATUS EFIAPI UsbIoSyncInterruptTransfer ( IN EFI_USB_IO_PROTOCOL *This, IN UINT8 Endpoint, IN OUT VOID *Data, IN OUT UINTN *DataLength, IN UINTN Timeout, OUT UINT32 *UsbStatus ) { USB_DEVICE *Dev; USB_INTERFACE *UsbIf; USB_ENDPOINT_DESC *EpDesc; EFI_TPL OldTpl; UINT8 Toggle; EFI_STATUS Status; if ((USB_ENDPOINT_ADDR (Endpoint) == 0) || (USB_ENDPOINT_ADDR (Endpoint) > 15) || (UsbStatus == NULL)) { return EFI_INVALID_PARAMETER; } OldTpl = gBS->RaiseTPL (USB_BUS_TPL); UsbIf = USB_INTERFACE_FROM_USBIO (This); Dev = UsbIf->Device; EpDesc = UsbGetEndpointDesc (UsbIf, Endpoint); if ((EpDesc == NULL) || (USB_ENDPOINT_TYPE (&EpDesc->Desc) != USB_ENDPOINT_INTERRUPT)) { Status = EFI_INVALID_PARAMETER; goto ON_EXIT; } Toggle = EpDesc->Toggle; Status = UsbHcSyncInterruptTransfer ( Dev->Bus, Dev->Address, Endpoint, Dev->Speed, EpDesc->Desc.MaxPacketSize, Data, DataLength, &Toggle, Timeout, &Dev->Translator, UsbStatus ); EpDesc->Toggle = Toggle; ON_EXIT: gBS->RestoreTPL (OldTpl); return Status; } /** Queue a new asynchronous interrupt transfer, or remove the old request if (IsNewTransfer == FALSE). @param This The USB_IO instance. @param Endpoint The device endpoint. @param IsNewTransfer Whether this is a new request, if it's old, remove the request. @param PollInterval The interval to poll the transfer result, (in ms). @param DataLength The length of perodic data transfer. @param Callback The function to call periodically when transfer is ready. @param Context The context to the callback. @retval EFI_SUCCESS New transfer is queued or old request is removed. @retval EFI_INVALID_PARAMETER Some parameters are invalid. @retval Others Failed to queue the new request or remove the old request. **/ EFI_STATUS EFIAPI UsbIoAsyncInterruptTransfer ( IN EFI_USB_IO_PROTOCOL *This, IN UINT8 Endpoint, IN BOOLEAN IsNewTransfer, IN UINTN PollInterval OPTIONAL, IN UINTN DataLength OPTIONAL, IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback OPTIONAL, IN VOID *Context OPTIONAL ) { USB_DEVICE *Dev; USB_INTERFACE *UsbIf; USB_ENDPOINT_DESC *EpDesc; EFI_TPL OldTpl; UINT8 Toggle; EFI_STATUS Status; if ((USB_ENDPOINT_ADDR (Endpoint) == 0) || (USB_ENDPOINT_ADDR (Endpoint) > 15)) { return EFI_INVALID_PARAMETER; } OldTpl = gBS->RaiseTPL (USB_BUS_TPL); UsbIf = USB_INTERFACE_FROM_USBIO (This); Dev = UsbIf->Device; EpDesc = UsbGetEndpointDesc (UsbIf, Endpoint); if ((EpDesc == NULL) || (USB_ENDPOINT_TYPE (&EpDesc->Desc) != USB_ENDPOINT_INTERRUPT)) { Status = EFI_INVALID_PARAMETER; goto ON_EXIT; } Toggle = EpDesc->Toggle; Status = UsbHcAsyncInterruptTransfer ( Dev->Bus, Dev->Address, Endpoint, Dev->Speed, EpDesc->Desc.MaxPacketSize, IsNewTransfer, &Toggle, PollInterval, DataLength, &Dev->Translator, Callback, Context ); EpDesc->Toggle = Toggle; ON_EXIT: gBS->RestoreTPL (OldTpl); return Status; } /** Execute a synchronous isochronous transfer. @param This The USB IO instance. @param DeviceEndpoint The device endpoint. @param Data The data to transfer. @param DataLength The length of the data to transfer. @param UsbStatus The result of USB transfer. @retval EFI_UNSUPPORTED Currently isochronous transfer isn't supported. **/ EFI_STATUS EFIAPI UsbIoIsochronousTransfer ( IN EFI_USB_IO_PROTOCOL *This, IN UINT8 DeviceEndpoint, IN OUT VOID *Data, IN UINTN DataLength, OUT UINT32 *Status ) { return EFI_UNSUPPORTED; } /** Queue an asynchronous isochronous transfer. @param This The USB_IO instance. @param DeviceEndpoint The device endpoint. @param Data The data to transfer. @param DataLength The length of perodic data transfer. @param IsochronousCallBack The function to call periodically when transfer is ready. @param Context The context to the callback. @retval EFI_UNSUPPORTED Currently isochronous transfer isn't supported. **/ EFI_STATUS EFIAPI UsbIoAsyncIsochronousTransfer ( IN EFI_USB_IO_PROTOCOL *This, IN UINT8 DeviceEndpoint, IN OUT VOID *Data, IN UINTN DataLength, IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack, IN VOID *Context OPTIONAL ) { return EFI_UNSUPPORTED; } /** Retrieve the device descriptor of the device. @param This The USB IO instance. @param Descriptor The variable to receive the device descriptor. @retval EFI_SUCCESS The device descriptor is returned. @retval EFI_INVALID_PARAMETER The parameter is invalid. **/ EFI_STATUS EFIAPI UsbIoGetDeviceDescriptor ( IN EFI_USB_IO_PROTOCOL *This, OUT EFI_USB_DEVICE_DESCRIPTOR *Descriptor ) { USB_DEVICE *Dev; USB_INTERFACE *UsbIf; EFI_TPL OldTpl; if (Descriptor == NULL) { return EFI_INVALID_PARAMETER; } OldTpl = gBS->RaiseTPL (USB_BUS_TPL); UsbIf = USB_INTERFACE_FROM_USBIO (This); Dev = UsbIf->Device; CopyMem (Descriptor, &Dev->DevDesc->Desc, sizeof (EFI_USB_DEVICE_DESCRIPTOR)); gBS->RestoreTPL (OldTpl); return EFI_SUCCESS; } /** Return the configuration descriptor of the current active configuration. @param This The USB IO instance. @param Descriptor The USB configuration descriptor. @retval EFI_SUCCESS The active configuration descriptor is returned. @retval EFI_INVALID_PARAMETER Some parameter is invalid. @retval EFI_NOT_FOUND Currently no active configuration is selected. **/ EFI_STATUS EFIAPI UsbIoGetActiveConfigDescriptor ( IN EFI_USB_IO_PROTOCOL *This, OUT EFI_USB_CONFIG_DESCRIPTOR *Descriptor ) { USB_DEVICE *Dev; USB_INTERFACE *UsbIf; EFI_STATUS Status; EFI_TPL OldTpl; if (Descriptor == NULL) { return EFI_INVALID_PARAMETER; } Status = EFI_SUCCESS; OldTpl = gBS->RaiseTPL (USB_BUS_TPL); UsbIf = USB_INTERFACE_FROM_USBIO (This); Dev = UsbIf->Device; if (Dev->ActiveConfig == NULL) { Status = EFI_NOT_FOUND; goto ON_EXIT; } CopyMem (Descriptor, &(Dev->ActiveConfig->Desc), sizeof (EFI_USB_CONFIG_DESCRIPTOR)); ON_EXIT: gBS->RestoreTPL (OldTpl); return Status; } /** Retrieve the active interface setting descriptor for this USB IO instance. @param This The USB IO instance. @param Descriptor The variable to receive active interface setting. @retval EFI_SUCCESS The active interface setting is returned. @retval EFI_INVALID_PARAMETER Some parameter is invalid. **/ EFI_STATUS EFIAPI UsbIoGetInterfaceDescriptor ( IN EFI_USB_IO_PROTOCOL *This, OUT EFI_USB_INTERFACE_DESCRIPTOR *Descriptor ) { USB_INTERFACE *UsbIf; EFI_TPL OldTpl; if (Descriptor == NULL) { return EFI_INVALID_PARAMETER; } OldTpl = gBS->RaiseTPL (USB_BUS_TPL); UsbIf = USB_INTERFACE_FROM_USBIO (This); CopyMem (Descriptor, &(UsbIf->IfSetting->Desc), sizeof (EFI_USB_INTERFACE_DESCRIPTOR)); gBS->RestoreTPL (OldTpl); return EFI_SUCCESS; } /** Retrieve the endpoint descriptor from this interface setting. @param This The USB IO instance. @param Index The index (start from zero) of the endpoint to retrieve. @param Descriptor The variable to receive the descriptor. @retval EFI_SUCCESS The endpoint descriptor is returned. @retval EFI_INVALID_PARAMETER Some parameter is invalid. **/ EFI_STATUS EFIAPI UsbIoGetEndpointDescriptor ( IN EFI_USB_IO_PROTOCOL *This, IN UINT8 Index, OUT EFI_USB_ENDPOINT_DESCRIPTOR *Descriptor ) { USB_INTERFACE *UsbIf; EFI_TPL OldTpl; OldTpl = gBS->RaiseTPL (USB_BUS_TPL); UsbIf = USB_INTERFACE_FROM_USBIO (This); if ((Descriptor == NULL) || (Index > 15)) { gBS->RestoreTPL (OldTpl); return EFI_INVALID_PARAMETER; } if (Index >= UsbIf->IfSetting->Desc.NumEndpoints) { gBS->RestoreTPL (OldTpl); return EFI_NOT_FOUND; } CopyMem ( Descriptor, &(UsbIf->IfSetting->Endpoints[Index]->Desc), sizeof (EFI_USB_ENDPOINT_DESCRIPTOR) ); gBS->RestoreTPL (OldTpl); return EFI_SUCCESS; } /** Retrieve the supported language ID table from the device. @param This The USB IO instance. @param LangIDTable The table to return the language IDs. @param TableSize The size, in bytes, of the table LangIDTable. @retval EFI_SUCCESS The language ID is return. **/ EFI_STATUS EFIAPI UsbIoGetSupportedLanguages ( IN EFI_USB_IO_PROTOCOL *This, OUT UINT16 **LangIDTable, OUT UINT16 *TableSize ) { USB_DEVICE *Dev; USB_INTERFACE *UsbIf; EFI_TPL OldTpl; OldTpl = gBS->RaiseTPL (USB_BUS_TPL); UsbIf = USB_INTERFACE_FROM_USBIO (This); Dev = UsbIf->Device; *LangIDTable = Dev->LangId; *TableSize = (UINT16)(Dev->TotalLangId * sizeof (UINT16)); gBS->RestoreTPL (OldTpl); return EFI_SUCCESS; } /** Retrieve an indexed string in the language of LangID. @param This The USB IO instance. @param LangID The language ID of the string to retrieve. @param StringIndex The index of the string. @param String The variable to receive the string. @retval EFI_SUCCESS The string is returned. @retval EFI_NOT_FOUND No such string existed. **/ EFI_STATUS EFIAPI UsbIoGetStringDescriptor ( IN EFI_USB_IO_PROTOCOL *This, IN UINT16 LangID, IN UINT8 StringIndex, OUT CHAR16 **String ) { USB_DEVICE *Dev; USB_INTERFACE *UsbIf; EFI_USB_STRING_DESCRIPTOR *StrDesc; EFI_TPL OldTpl; UINT8 *Buf; UINT8 Index; EFI_STATUS Status; if ((StringIndex == 0) || (LangID == 0)) { return EFI_NOT_FOUND; } OldTpl = gBS->RaiseTPL (USB_BUS_TPL); UsbIf = USB_INTERFACE_FROM_USBIO (This); Dev = UsbIf->Device; // // Check whether language ID is supported // Status = EFI_NOT_FOUND; for (Index = 0; Index < Dev->TotalLangId; Index++) { ASSERT (Index < USB_MAX_LANG_ID); if (Dev->LangId[Index] == LangID) { break; } } if (Index == Dev->TotalLangId) { goto ON_EXIT; } // // Retrieve the string descriptor then allocate a buffer // to hold the string itself. // StrDesc = UsbGetOneString (Dev, StringIndex, LangID); if (StrDesc == NULL) { goto ON_EXIT; } if (StrDesc->Length <= 2) { goto FREE_STR; } Buf = AllocateZeroPool (StrDesc->Length); if (Buf == NULL) { Status = EFI_OUT_OF_RESOURCES; goto FREE_STR; } CopyMem (Buf, StrDesc->String, StrDesc->Length - 2); *String = (CHAR16 *)Buf; Status = EFI_SUCCESS; FREE_STR: gBS->FreePool (StrDesc); ON_EXIT: gBS->RestoreTPL (OldTpl); return Status; } /** Reset the device, then if that succeeds, reconfigure the device with its address and current active configuration. @param This The USB IO instance. @retval EFI_SUCCESS The device is reset and configured. @retval Others Failed to reset the device. **/ EFI_STATUS EFIAPI UsbIoPortReset ( IN EFI_USB_IO_PROTOCOL *This ) { USB_INTERFACE *UsbIf; USB_INTERFACE *HubIf; USB_DEVICE *Dev; EFI_TPL OldTpl; EFI_STATUS Status; UINT8 DevAddress; OldTpl = gBS->RaiseTPL (USB_BUS_TPL); UsbIf = USB_INTERFACE_FROM_USBIO (This); Dev = UsbIf->Device; if (UsbIf->IsHub) { Status = EFI_INVALID_PARAMETER; goto ON_EXIT; } HubIf = Dev->ParentIf; Status = HubIf->HubApi->ResetPort (HubIf, Dev->ParentPort); if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, "UsbIoPortReset: failed to reset hub port %d@hub %d - %r\n", Dev->ParentPort, Dev->ParentAddr, Status )); goto ON_EXIT; } HubIf->HubApi->ClearPortChange (HubIf, Dev->ParentPort); // // Reset the device to its current address. The device now has an address // of ZERO after port reset, so need to set Dev->Address to the device again for // host to communicate with it. // DevAddress = Dev->Address; Dev->Address = 0; Status = UsbSetAddress (Dev, DevAddress); Dev->Address = DevAddress; gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL); if (EFI_ERROR (Status)) { // // It may fail due to device disconnection or other reasons. // DEBUG (( DEBUG_ERROR, "UsbIoPortReset: failed to set address for device %d - %r\n", Dev->Address, Status )); goto ON_EXIT; } DEBUG ((DEBUG_INFO, "UsbIoPortReset: device is now ADDRESSED at %d\n", Dev->Address)); // // Reset the current active configure, after this device // is in CONFIGURED state. // if (Dev->ActiveConfig != NULL) { Status = UsbSetConfig (Dev, Dev->ActiveConfig->Desc.ConfigurationValue); if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, "UsbIoPortReset: failed to set configure for device %d - %r\n", Dev->Address, Status )); } } ON_EXIT: gBS->RestoreTPL (OldTpl); return Status; } /** Install Usb Bus Protocol on host controller, and start the Usb bus. @param This The USB bus driver binding instance. @param Controller The controller to check. @param RemainingDevicePath The remaining device patch. @retval EFI_SUCCESS The controller is controlled by the usb bus. @retval EFI_ALREADY_STARTED The controller is already controlled by the usb bus. @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. **/ EFI_STATUS EFIAPI UsbBusBuildProtocol ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { USB_BUS *UsbBus; USB_DEVICE *RootHub; USB_INTERFACE *RootIf; EFI_STATUS Status; EFI_STATUS Status2; UsbBus = AllocateZeroPool (sizeof (USB_BUS)); if (UsbBus == NULL) { return EFI_OUT_OF_RESOURCES; } UsbBus->Signature = USB_BUS_SIGNATURE; UsbBus->HostHandle = Controller; UsbBus->MaxDevices = USB_MAX_DEVICES; Status = gBS->OpenProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **)&UsbBus->DevicePath, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "UsbBusStart: Failed to open device path - %r\n", Status)); FreePool (UsbBus); return Status; } // // Get USB_HC2/USB_HC host controller protocol (EHCI/UHCI). // This is for backward compatibility with EFI 1.x. In UEFI // 2.x, USB_HC2 replaces USB_HC. We will open both USB_HC2 // and USB_HC because EHCI driver will install both protocols // (for the same reason). If we don't consume both of them, // the unconsumed one may be opened by others. // Status = gBS->OpenProtocol ( Controller, &gEfiUsb2HcProtocolGuid, (VOID **)&(UsbBus->Usb2Hc), This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); Status2 = gBS->OpenProtocol ( Controller, &gEfiUsbHcProtocolGuid, (VOID **)&(UsbBus->UsbHc), This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status) && EFI_ERROR (Status2)) { DEBUG ((DEBUG_ERROR, "UsbBusStart: Failed to open USB_HC/USB2_HC - %r\n", Status)); Status = EFI_DEVICE_ERROR; goto CLOSE_HC; } if (!EFI_ERROR (Status)) { // // The EFI_USB2_HC_PROTOCOL is produced for XHCI support. // Then its max supported devices are 256. Otherwise it's 128. // ASSERT (UsbBus->Usb2Hc != NULL); if (UsbBus->Usb2Hc->MajorRevision == 0x3) { UsbBus->MaxDevices = 256; } } // // Install an EFI_USB_BUS_PROTOCOL to host controller to identify it. // Status = gBS->InstallProtocolInterface ( &Controller, &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, &UsbBus->BusId ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "UsbBusStart: Failed to install bus protocol - %r\n", Status)); goto CLOSE_HC; } // // Initial the wanted child device path list, and add first RemainingDevicePath // InitializeListHead (&UsbBus->WantedUsbIoDPList); Status = UsbBusAddWantedUsbIoDP (&UsbBus->BusId, RemainingDevicePath); ASSERT (!EFI_ERROR (Status)); // // Create a fake usb device for root hub // RootHub = AllocateZeroPool (sizeof (USB_DEVICE)); if (RootHub == NULL) { Status = EFI_OUT_OF_RESOURCES; goto UNINSTALL_USBBUS; } RootIf = AllocateZeroPool (sizeof (USB_INTERFACE)); if (RootIf == NULL) { FreePool (RootHub); Status = EFI_OUT_OF_RESOURCES; goto FREE_ROOTHUB; } RootHub->Bus = UsbBus; RootHub->NumOfInterface = 1; RootHub->Interfaces[0] = RootIf; RootHub->Tier = 0; RootIf->Signature = USB_INTERFACE_SIGNATURE; RootIf->Device = RootHub; RootIf->DevicePath = UsbBus->DevicePath; // // Report Status Code here since we will enumerate the USB devices // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, (EFI_IO_BUS_USB | EFI_IOB_PC_DETECT), UsbBus->DevicePath ); Status = mUsbRootHubApi.Init (RootIf); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "UsbBusStart: Failed to init root hub - %r\n", Status)); goto FREE_ROOTHUB; } UsbBus->Devices[0] = RootHub; DEBUG ((DEBUG_INFO, "UsbBusStart: usb bus started on %p, root hub %p\n", Controller, RootIf)); return EFI_SUCCESS; FREE_ROOTHUB: if (RootIf != NULL) { FreePool (RootIf); } if (RootHub != NULL) { FreePool (RootHub); } UNINSTALL_USBBUS: gBS->UninstallProtocolInterface (Controller, &gEfiCallerIdGuid, &UsbBus->BusId); CLOSE_HC: if (UsbBus->Usb2Hc != NULL) { gBS->CloseProtocol ( Controller, &gEfiUsb2HcProtocolGuid, This->DriverBindingHandle, Controller ); } if (UsbBus->UsbHc != NULL) { gBS->CloseProtocol ( Controller, &gEfiUsbHcProtocolGuid, This->DriverBindingHandle, Controller ); } gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); FreePool (UsbBus); DEBUG ((DEBUG_ERROR, "UsbBusStart: Failed to start bus driver - %r\n", Status)); return Status; } /** The USB bus driver entry pointer. @param ImageHandle The driver image handle. @param SystemTable The system table. @return EFI_SUCCESS The component name protocol is installed. @return Others Failed to init the usb driver. **/ EFI_STATUS EFIAPI UsbBusDriverEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { return EfiLibInstallDriverBindingComponentName2 ( ImageHandle, SystemTable, &mUsbBusDriverBinding, ImageHandle, &mUsbBusComponentName, &mUsbBusComponentName2 ); } /** Check whether USB bus driver support this device. @param This The USB bus driver binding protocol. @param Controller The controller handle to check. @param RemainingDevicePath The remaining device path. @retval EFI_SUCCESS The bus supports this controller. @retval EFI_UNSUPPORTED This device isn't supported. **/ EFI_STATUS EFIAPI UsbBusControllerDriverSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_DEV_PATH_PTR DevicePathNode; EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; EFI_USB2_HC_PROTOCOL *Usb2Hc; EFI_USB_HC_PROTOCOL *UsbHc; EFI_STATUS Status; // // Check whether device path is valid // if (RemainingDevicePath != NULL) { // // Check if RemainingDevicePath is the End of Device Path Node, // if yes, go on checking other conditions // if (!IsDevicePathEnd (RemainingDevicePath)) { // // If RemainingDevicePath isn't the End of Device Path Node, // check its validation // DevicePathNode.DevPath = RemainingDevicePath; if ((DevicePathNode.DevPath->Type != MESSAGING_DEVICE_PATH) || ( (DevicePathNode.DevPath->SubType != MSG_USB_DP) && (DevicePathNode.DevPath->SubType != MSG_USB_CLASS_DP) && (DevicePathNode.DevPath->SubType != MSG_USB_WWID_DP) )) { return EFI_UNSUPPORTED; } } } // // Check whether USB_HC2 protocol is installed // 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)) { // // If failed to open USB_HC2, fall back to USB_HC // Status = gBS->OpenProtocol ( Controller, &gEfiUsbHcProtocolGuid, (VOID **)&UsbHc, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (Status == EFI_ALREADY_STARTED) { return EFI_SUCCESS; } if (EFI_ERROR (Status)) { return Status; } // // Close the USB_HC used to perform the supported test // gBS->CloseProtocol ( Controller, &gEfiUsbHcProtocolGuid, This->DriverBindingHandle, Controller ); } else { // // Close the USB_HC2 used to perform the supported test // gBS->CloseProtocol ( Controller, &gEfiUsb2HcProtocolGuid, This->DriverBindingHandle, Controller ); } // // Open the EFI Device Path protocol 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_SUCCESS; } if (!EFI_ERROR (Status)) { // // Close protocol, don't use device path protocol in the Support() function // gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); return EFI_SUCCESS; } return Status; } /** Start to process the controller. @param This The USB bus driver binding instance. @param Controller The controller to check. @param RemainingDevicePath The remaining device patch. @retval EFI_SUCCESS The controller is controlled by the usb bus. @retval EFI_ALREADY_STARTED The controller is already controlled by the usb bus. @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. **/ EFI_STATUS EFIAPI UsbBusControllerDriverStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_USB_BUS_PROTOCOL *UsbBusId; EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; Status = gBS->OpenProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **)&ParentDevicePath, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); ASSERT_EFI_ERROR (Status); // // Report Status Code here since we will initialize the host controller // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, (EFI_IO_BUS_USB | EFI_IOB_PC_INIT), ParentDevicePath ); // // Locate the USB bus protocol, if it is found, USB bus // is already started on this controller. // Status = gBS->OpenProtocol ( Controller, &gEfiCallerIdGuid, (VOID **)&UsbBusId, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { // // If first start, build the bus execute environment and install bus protocol // REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_IO_BUS_USB | EFI_P_PC_ENABLE)); Status = UsbBusBuildProtocol (This, Controller, RemainingDevicePath); if (EFI_ERROR (Status)) { return Status; } // // Try get the Usb Bus protocol interface again // Status = gBS->OpenProtocol ( Controller, &gEfiCallerIdGuid, (VOID **)&UsbBusId, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); ASSERT (!EFI_ERROR (Status)); } else { // // USB Bus driver need to control the recursive connect policy of the bus, only those wanted // usb child device will be recursively connected. // The RemainingDevicePath indicate the child usb device which user want to fully recursively connecte this time. // All wanted usb child devices will be remembered by the usb bus driver itself. // If RemainingDevicePath == NULL, all the usb child devices in the usb bus are wanted devices. // // Save the passed in RemainingDevicePath this time // if (RemainingDevicePath != NULL) { if (IsDevicePathEnd (RemainingDevicePath)) { // // If RemainingDevicePath is the End of Device Path Node, // skip enumerate any device and return EFI_SUCCESS // return EFI_SUCCESS; } } Status = UsbBusAddWantedUsbIoDP (UsbBusId, RemainingDevicePath); ASSERT (!EFI_ERROR (Status)); // // Ensure all wanted child usb devices are fully recursively connected // Status = UsbBusRecursivelyConnectWantedUsbIo (UsbBusId); ASSERT (!EFI_ERROR (Status)); } return EFI_SUCCESS; } /** Stop handle the controller by this USB bus driver. @param This The USB bus driver binding protocol. @param Controller The controller to release. @param NumberOfChildren The child of USB bus that opened controller BY_CHILD. @param ChildHandleBuffer The array of child handle. @retval EFI_SUCCESS The controller or children are stopped. @retval EFI_DEVICE_ERROR Failed to stop the driver. **/ EFI_STATUS EFIAPI UsbBusControllerDriverStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ) { USB_BUS *Bus; USB_DEVICE *RootHub; USB_DEVICE *UsbDev; USB_INTERFACE *RootIf; USB_INTERFACE *UsbIf; EFI_USB_BUS_PROTOCOL *BusId; EFI_USB_IO_PROTOCOL *UsbIo; EFI_TPL OldTpl; UINTN Index; EFI_STATUS Status; EFI_STATUS ReturnStatus; Status = EFI_SUCCESS; if (NumberOfChildren > 0) { // // BugBug: Raise TPL to callback level instead of USB_BUS_TPL to avoid TPL conflict // OldTpl = gBS->RaiseTPL (TPL_CALLBACK); ReturnStatus = EFI_SUCCESS; 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)) { // // It is possible that the child has already been released: // 1. For combo device, free one device will release others. // 2. If a hub is released, all devices on its down facing // ports are released also. // continue; } UsbIf = USB_INTERFACE_FROM_USBIO (UsbIo); UsbDev = UsbIf->Device; ReturnStatus = UsbRemoveDevice (UsbDev); } gBS->RestoreTPL (OldTpl); return ReturnStatus; } DEBUG ((DEBUG_INFO, "UsbBusStop: usb bus stopped on %p\n", Controller)); // // Locate USB_BUS for the current host controller // Status = gBS->OpenProtocol ( Controller, &gEfiCallerIdGuid, (VOID **)&BusId, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { return Status; } Bus = USB_BUS_FROM_THIS (BusId); // // Stop the root hub, then free all the devices // // BugBug: Raise TPL to callback level instead of USB_BUS_TPL to avoid TPL conflict // OldTpl = gBS->RaiseTPL (TPL_CALLBACK); RootHub = Bus->Devices[0]; RootIf = RootHub->Interfaces[0]; ASSERT (Bus->MaxDevices <= 256); ReturnStatus = EFI_SUCCESS; for (Index = 1; Index < Bus->MaxDevices; Index++) { if (Bus->Devices[Index] != NULL) { Status = UsbRemoveDevice (Bus->Devices[Index]); if (EFI_ERROR (Status)) { ReturnStatus = Status; } } } gBS->RestoreTPL (OldTpl); if (!EFI_ERROR (ReturnStatus)) { mUsbRootHubApi.Release (RootIf); gBS->FreePool (RootIf); gBS->FreePool (RootHub); Status = UsbBusFreeUsbDPList (&Bus->WantedUsbIoDPList); ASSERT (!EFI_ERROR (Status)); // // Uninstall the bus identifier and close USB_HC/USB2_HC protocols // gBS->UninstallProtocolInterface (Controller, &gEfiCallerIdGuid, &Bus->BusId); if (Bus->Usb2Hc != NULL) { Status = gBS->CloseProtocol ( Controller, &gEfiUsb2HcProtocolGuid, This->DriverBindingHandle, Controller ); } if (Bus->UsbHc != NULL) { Status = gBS->CloseProtocol ( Controller, &gEfiUsbHcProtocolGuid, This->DriverBindingHandle, Controller ); } if (!EFI_ERROR (Status)) { gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); gBS->FreePool (Bus); } } return Status; }