From 92870c983c6d99d31f449d8dcd729090255dda49 Mon Sep 17 00:00:00 2001 From: erictian Date: Tue, 23 Aug 2011 14:36:33 +0000 Subject: [PATCH] Enabling usb3.0 XHCI support. Signed-off-by: erictian Reviewed-by: jshi19 git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12185 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.c | 224 ++ MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.h | 146 ++ MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c | 2105 ++++++++++++++++ MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h | 362 +++ MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf | 77 + MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c | 827 ++++++ MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h | 569 +++++ MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c | 2383 ++++++++++++++++++ MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.h | 1117 ++++++++ MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.c | 18 +- MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h | 6 +- MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c | 6 +- MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c | 83 +- MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c | 169 +- MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h | 33 + MdeModulePkg/MdeModulePkg.dsc | 1 + 16 files changed, 8042 insertions(+), 84 deletions(-) create mode 100644 MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.c create mode 100644 MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.h create mode 100644 MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c create mode 100644 MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h create mode 100644 MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf create mode 100644 MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c create mode 100644 MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h create mode 100644 MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c create mode 100644 MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.h diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.c b/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.c new file mode 100644 index 0000000000..2975e5612f --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.c @@ -0,0 +1,224 @@ +/** @file + UEFI Component Name(2) protocol implementation for XHCI driver. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Xhci.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gXhciComponentName = { + XhciComponentNameGetDriverName, + XhciComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gXhciComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) XhciComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) XhciComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mXhciDriverNameTable[] = { + { "eng;en", L"Usb Xhci Driver" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +XhciComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mXhciDriverNameTable, + DriverName, + (BOOLEAN)(This == &gXhciComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +XhciComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_USB2_HC_PROTOCOL *Usb2Hc; + USB_XHCI_DEV *XhciDev; + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllerHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gXhciDriverBinding.DriverBindingHandle, + &gEfiPciIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the device context + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUsb2HcProtocolGuid, + (VOID **) &Usb2Hc, + gXhciDriverBinding.DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + XhciDev = XHC_FROM_THIS (Usb2Hc); + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + XhciDev->ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gXhciComponentName) + ); + +} diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.h b/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.h new file mode 100644 index 0000000000..c3c44ccdfb --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.h @@ -0,0 +1,146 @@ +/** @file + + This file contains the delarations for componet name routines. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_COMPONENT_NAME_H_ +#define _EFI_COMPONENT_NAME_H_ + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +XhciComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +XhciComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +#endif + diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c b/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c new file mode 100644 index 0000000000..61c4b2427f --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c @@ -0,0 +1,2105 @@ +/** @file + The XHCI controller driver. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Xhci.h" + +// +// The device context array which supports up to 255 devices, entry 0 is reserved and should not be used. +// +USB_DEV_CONTEXT UsbDevContext[256]; + +// +// Two arrays used to translate the XHCI port state (change) +// to the UEFI protocol's port state (change). +// +USB_PORT_STATE_MAP mUsbPortStateMap[] = { + {XHC_PORTSC_CCS, USB_PORT_STAT_CONNECTION}, + {XHC_PORTSC_PED, USB_PORT_STAT_ENABLE}, + {XHC_PORTSC_OCA, USB_PORT_STAT_OVERCURRENT}, + {XHC_PORTSC_RESET, USB_PORT_STAT_RESET} +}; + +USB_PORT_STATE_MAP mUsbPortChangeMap[] = { + {XHC_PORTSC_CSC, USB_PORT_STAT_C_CONNECTION}, + {XHC_PORTSC_PEC, USB_PORT_STAT_C_ENABLE}, + {XHC_PORTSC_OCC, USB_PORT_STAT_C_OVERCURRENT}, + {XHC_PORTSC_PRC, USB_PORT_STAT_C_RESET} +}; + +EFI_DRIVER_BINDING_PROTOCOL gXhciDriverBinding = { + XhcDriverBindingSupported, + XhcDriverBindingStart, + XhcDriverBindingStop, + 0x30, + NULL, + NULL +}; + +/** + Retrieves the capability of root hub ports. + + @param This The EFI_USB2_HC_PROTOCOL instance. + @param MaxSpeed Max speed supported by the controller. + @param PortNumber Number of the root hub ports. + @param Is64BitCapable Whether the controller supports 64-bit memory + addressing. + + @retval EFI_SUCCESS Host controller capability were retrieved successfully. + @retval EFI_INVALID_PARAMETER Either of the three capability pointer is NULL. + +**/ +EFI_STATUS +EFIAPI +XhcGetCapability ( + IN EFI_USB2_HC_PROTOCOL *This, + OUT UINT8 *MaxSpeed, + OUT UINT8 *PortNumber, + OUT UINT8 *Is64BitCapable + ) +{ + USB_XHCI_DEV *Xhc; + EFI_TPL OldTpl; + + if ((MaxSpeed == NULL) || (PortNumber == NULL) || (Is64BitCapable == NULL)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = XHC_FROM_THIS (This); + *MaxSpeed = EFI_USB_SPEED_SUPER; + *PortNumber = (UINT8) (Xhc->HcSParams1.Data.MaxPorts); + *Is64BitCapable = (UINT8) (Xhc->HcCParams.Data.Ac64); + DEBUG ((EFI_D_INFO, "XhcGetCapability: %d ports, 64 bit %d\n", *PortNumber, *Is64BitCapable)); + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Provides software reset for the USB host controller. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param Attributes A bit mask of the reset operation to perform. + + @retval EFI_SUCCESS The reset operation succeeded. + @retval EFI_INVALID_PARAMETER Attributes is not valid. + @retval EFI_UNSUPPOURTED The type of reset specified by Attributes is + not currently supported by the host controller. + @retval EFI_DEVICE_ERROR Host controller isn't halted to reset. + +**/ +EFI_STATUS +EFIAPI +XhcReset ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT16 Attributes + ) +{ + USB_XHCI_DEV *Xhc; + EFI_STATUS Status; + EFI_TPL OldTpl; + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = XHC_FROM_THIS (This); + + switch (Attributes) { + case EFI_USB_HC_RESET_GLOBAL: + // + // Flow through, same behavior as Host Controller Reset + // + case EFI_USB_HC_RESET_HOST_CONTROLLER: + // + // Host Controller must be Halt when Reset it + // + if (!XhcIsHalt (Xhc)) { + Status = XhcHaltHC (Xhc, XHC_GENERIC_TIMEOUT); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + } + + Status = XhcResetHC (Xhc, XHC_RESET_TIMEOUT); + ASSERT (!(XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_CNR))); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Clean up the asynchronous transfers, currently only + // interrupt supports asynchronous operation. + // + XhciDelAllAsyncIntTransfers (Xhc); + XhcFreeSched (Xhc); + + XhcInitSched (Xhc); + break; + + case EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG: + case EFI_USB_HC_RESET_HOST_WITH_DEBUG: + Status = EFI_UNSUPPORTED; + break; + + default: + Status = EFI_INVALID_PARAMETER; + } + +ON_EXIT: + DEBUG ((EFI_D_INFO, "XhcReset: status %r\n", Status)); + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Retrieve the current state of the USB host controller. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param State Variable to return the current host controller + state. + + @retval EFI_SUCCESS Host controller state was returned in State. + @retval EFI_INVALID_PARAMETER State is NULL. + @retval EFI_DEVICE_ERROR An error was encountered while attempting to + retrieve the host controller's current state. + +**/ +EFI_STATUS +EFIAPI +XhcGetState ( + IN EFI_USB2_HC_PROTOCOL *This, + OUT EFI_USB_HC_STATE *State + ) +{ + USB_XHCI_DEV *Xhc; + EFI_TPL OldTpl; + + if (State == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = XHC_FROM_THIS (This); + + if (XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT)) { + *State = EfiUsbHcStateHalt; + } else { + *State = EfiUsbHcStateOperational; + } + + DEBUG ((EFI_D_INFO, "XhcGetState: current state %d\n", *State)); + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + +/** + Sets the USB host controller to a specific state. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param State The state of the host controller that will be set. + + @retval EFI_SUCCESS The USB host controller was successfully placed + in the state specified by State. + @retval EFI_INVALID_PARAMETER State is invalid. + @retval EFI_DEVICE_ERROR Failed to set the state due to device error. + +**/ +EFI_STATUS +EFIAPI +XhcSetState ( + IN EFI_USB2_HC_PROTOCOL *This, + IN EFI_USB_HC_STATE State + ) +{ + USB_XHCI_DEV *Xhc; + EFI_STATUS Status; + EFI_USB_HC_STATE CurState; + EFI_TPL OldTpl; + + Status = XhcGetState (This, &CurState); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + if (CurState == State) { + return EFI_SUCCESS; + } + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = XHC_FROM_THIS (This); + + switch (State) { + case EfiUsbHcStateHalt: + Status = XhcHaltHC (Xhc, XHC_GENERIC_TIMEOUT); + break; + + case EfiUsbHcStateOperational: + if (XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HSE)) { + Status = EFI_DEVICE_ERROR; + break; + } + + // + // Software must not write a one to this field unless the host controller + // is in the Halted state. Doing so will yield undefined results. + // refers to Spec[XHCI1.0-2.3.1] + // + if (!XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT)) { + Status = EFI_DEVICE_ERROR; + break; + } + + Status = XhcRunHC (Xhc, XHC_GENERIC_TIMEOUT); + break; + + case EfiUsbHcStateSuspend: + Status = EFI_UNSUPPORTED; + break; + + default: + Status = EFI_INVALID_PARAMETER; + } + + DEBUG ((EFI_D_INFO, "XhcSetState: status %r\n", Status)); + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Retrieves the current status of a USB root hub port. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param PortNumber The root hub port to retrieve the state from. + This value is zero-based. + @param PortStatus Variable to receive the port state. + + @retval EFI_SUCCESS The status of the USB root hub port specified. + by PortNumber was returned in PortStatus. + @retval EFI_INVALID_PARAMETER PortNumber is invalid. + @retval EFI_DEVICE_ERROR Can't read register. + +**/ +EFI_STATUS +EFIAPI +XhcGetRootHubPortStatus ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus + ) +{ + USB_XHCI_DEV *Xhc; + UINT32 Offset; + UINT32 State; + UINT32 TotalPort; + UINTN Index; + UINTN MapSize; + EFI_STATUS Status; + USB_DEV_ROUTE ParentRouteChart; + EFI_TPL OldTpl; + + if (PortStatus == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = XHC_FROM_THIS (This); + Status = EFI_SUCCESS; + + TotalPort = Xhc->HcSParams1.Data.MaxPorts; + + if (PortNumber >= TotalPort) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Offset = (UINT32) (XHC_PORTSC_OFFSET + (0x10 * PortNumber)); + PortStatus->PortStatus = 0; + PortStatus->PortChangeStatus = 0; + + State = XhcReadOpReg (Xhc, Offset); + + // + // According to XHCI 1.0 spec, bit 10~13 of the root port status register identifies the speed of the attached device. + // + switch ((State & XHC_PORTSC_PS) >> 10) { + case 2: + PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED; + break; + + case 3: + PortStatus->PortStatus |= USB_PORT_STAT_HIGH_SPEED; + break; + + case 4: + PortStatus->PortStatus |= USB_PORT_STAT_SUPER_SPEED; + break; + + default: + break; + } + + // + // Convert the XHCI port/port change state to UEFI status + // + MapSize = sizeof (mUsbPortStateMap) / sizeof (USB_PORT_STATE_MAP); + + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (State, mUsbPortStateMap[Index].HwState)) { + PortStatus->PortStatus = (UINT16) (PortStatus->PortStatus | mUsbPortStateMap[Index].UefiState); + } + } + // + // Bit5~8 reflects its current link state. + // + if ((State & XHC_PORTSC_PLS) >> 5 == 3) { + PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND; + } + + MapSize = sizeof (mUsbPortChangeMap) / sizeof (USB_PORT_STATE_MAP); + + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (State, mUsbPortChangeMap[Index].HwState)) { + PortStatus->PortChangeStatus = (UINT16) (PortStatus->PortChangeStatus | mUsbPortChangeMap[Index].UefiState); + } + } + + // + // Poll the root port status register to enable/disable corresponding device slot if there is a device attached/detached. + // For those devices behind hub, we get its attach/detach event by hooking Get_Port_Status request at control transfer for those hub. + // + ParentRouteChart.Dword = 0; + XhcPollPortStatusChange (Xhc, ParentRouteChart, PortNumber, PortStatus); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Sets a feature for the specified root hub port. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param PortNumber Root hub port to set. + @param PortFeature Feature to set. + + @retval EFI_SUCCESS The feature specified by PortFeature was set. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + @retval EFI_DEVICE_ERROR Can't read register. + +**/ +EFI_STATUS +EFIAPI +XhcSetRootHubPortFeature ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + USB_XHCI_DEV *Xhc; + UINT32 Offset; + UINT32 State; + UINT32 TotalPort; + UINT8 SlotId; + USB_DEV_ROUTE RouteChart; + EFI_STATUS Status; + EFI_TPL OldTpl; + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = XHC_FROM_THIS (This); + Status = EFI_SUCCESS; + + TotalPort = (Xhc->HcSParams1.Data.MaxPorts); + + if (PortNumber >= TotalPort) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Offset = (UINT32) (XHC_PORTSC_OFFSET + (0x10 * PortNumber)); + State = XhcReadOpReg (Xhc, Offset); + + // + // Mask off the port status change bits, these bits are + // write clean bit + // + State &= ~ (BIT1 | BIT17 | BIT18 | BIT19 | BIT20 | BIT21 | BIT22 | BIT23); + + switch (PortFeature) { + case EfiUsbPortEnable: + // + // Ports may only be enabled by the xHC. Software cannot enable a port by writing a '1' to this flag. + // A port may be disabled by software writing a '1' to this flag. + // + Status = EFI_SUCCESS; + break; + + case EfiUsbPortSuspend: + State |= XHC_PORTSC_LWS; + XhcWriteOpReg (Xhc, Offset, State); + State &= ~XHC_PORTSC_PLS; + State |= (3 << 5) ; + XhcWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortReset: + DEBUG ((EFI_D_INFO, "XhcUsbPortReset!\n")); + // + // Make sure Host Controller not halt before reset it + // + if (XhcIsHalt (Xhc)) { + Status = XhcRunHC (Xhc, XHC_GENERIC_TIMEOUT); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "XhcSetRootHubPortFeature :failed to start HC - %r\n", Status)); + break; + } + } + + RouteChart.Field.RouteString = 0; + RouteChart.Field.RootPortNum = PortNumber + 1; + RouteChart.Field.TierNum = 1; + // + // BUGBUG: If the port reset operation happens after the usb super speed device is enabled, + // The subsequent configuration, such as getting device descriptor, will fail. + // So here a workaround is introduced to skip the reset operation if the device is enabled. + // + SlotId = XhcRouteStringToSlotId (RouteChart); + if (SlotId == 0) { + // + // 4.3.1 Resetting a Root Hub Port + // 1) Write the PORTSC register with the Port Reset (PR) bit set to '1'. + // + State |= XHC_PORTSC_RESET; + XhcWriteOpReg (Xhc, Offset, State); + XhcWaitOpRegBit(Xhc, Offset, XHC_PORTSC_PRC, TRUE, XHC_GENERIC_TIMEOUT); + } + break; + + case EfiUsbPortPower: + // + // Not supported, ignore the operation + // + Status = EFI_SUCCESS; + break; + + case EfiUsbPortOwner: + // + // XHCI root hub port don't has the owner bit, ignore the operation + // + Status = EFI_SUCCESS; + break; + + default: + Status = EFI_INVALID_PARAMETER; + } + +ON_EXIT: + DEBUG ((EFI_D_INFO, "XhcSetRootHubPortFeature: status %r\n", Status)); + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Clears a feature for the specified root hub port. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param PortNumber Specifies the root hub port whose feature is + requested to be cleared. + @param PortFeature Indicates the feature selector associated with the + feature clear request. + + @retval EFI_SUCCESS The feature specified by PortFeature was cleared + for the USB root hub port specified by PortNumber. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + @retval EFI_DEVICE_ERROR Can't read register. + +**/ +EFI_STATUS +EFIAPI +XhcClearRootHubPortFeature ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + USB_XHCI_DEV *Xhc; + UINT32 Offset; + UINT32 State; + UINT32 TotalPort; + EFI_STATUS Status; + EFI_TPL OldTpl; + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = XHC_FROM_THIS (This); + Status = EFI_SUCCESS; + + TotalPort = (Xhc->HcSParams1.Data.MaxPorts); + + if (PortNumber >= TotalPort) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Offset = XHC_PORTSC_OFFSET + (0x10 * PortNumber); + + // + // Mask off the port status change bits, these bits are + // write clean bit + // + State = XhcReadOpReg (Xhc, Offset); + State &= ~ (BIT1 | BIT17 | BIT18 | BIT19 | BIT20 | BIT21 | BIT22 | BIT23); + + switch (PortFeature) { + case EfiUsbPortEnable: + // + // Ports may only be enabled by the xHC. Software cannot enable a port by writing a '1' to this flag. + // A port may be disabled by software writing a '1' to this flag. + // + State |= XHC_PORTSC_PED; + State &= ~XHC_PORTSC_RESET; + XhcWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortSuspend: + State |= XHC_PORTSC_LWS; + XhcWriteOpReg (Xhc, Offset, State); + State &= ~XHC_PORTSC_PLS; + XhcWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortReset: + // + // PORTSC_RESET BIT(4) bit is RW1S attribute, which means Write-1-to-set status: + // Register bits indicate status when read, a clear bit may be set by + // writing a '1'. Writing a '0' to RW1S bits has no effect. + // + break; + + case EfiUsbPortOwner: + // + // XHCI root hub port don't has the owner bit, ignore the operation + // + break; + + case EfiUsbPortConnectChange: + // + // Clear connect status change + // + State |= XHC_PORTSC_CSC; + XhcWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortEnableChange: + // + // Clear enable status change + // + State |= XHC_PORTSC_PEC; + XhcWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortOverCurrentChange: + // + // Clear PortOverCurrent change + // + State |= XHC_PORTSC_OCC; + XhcWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortResetChange: + // + // Clear Port Reset change + // + State |= XHC_PORTSC_PRC; + XhcWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortPower: + case EfiUsbPortSuspendChange: + // + // Not supported or not related operation + // + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; + } + +ON_EXIT: + DEBUG ((EFI_D_INFO, "XhcClearRootHubPortFeature: status %r\n", Status)); + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Submits control transfer to a target USB device. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress The target device address. + @param DeviceSpeed Target device speed. + @param MaximumPacketLength Maximum packet size the default control transfer + endpoint is capable of sending or receiving. + @param Request USB device request to send. + @param TransferDirection Specifies the data direction for the data stage + @param Data Data buffer to be transmitted or received from USB + device. + @param DataLength The size (in bytes) of the data buffer. + @param TimeOut Indicates the maximum timeout, in millisecond. + @param Translator Transaction translator to be used by this device. + @param TransferResult Return the result of this control transfer. + + @retval EFI_SUCCESS Transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resources. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_TIMEOUT Transfer failed due to timeout. + @retval EFI_DEVICE_ERROR Transfer failed due to host controller or device error. + +**/ +EFI_STATUS +EFIAPI +XhcControlTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + USB_XHCI_DEV *Xhc; + URB *Urb; + UINT8 Endpoint; + UINT8 Index; + UINT8 XhciDevAddr; + UINT8 DescriptorType; + UINT8 SlotId; + UINT8 TTT; + UINT8 MTT; + UINT32 MaxPacket0; + EFI_USB_HUB_DESCRIPTOR *HubDesc; + EFI_TPL OldTpl; + EFI_STATUS Status; + EFI_STATUS RecoveryStatus; + UINTN MapSize; + EFI_USB_PORT_STATUS PortStatus; + UINT32 State; + + // + // Validate parameters + // + if ((Request == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection != EfiUsbDataIn) && + (TransferDirection != EfiUsbDataOut) && + (TransferDirection != EfiUsbNoData)) { + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection == EfiUsbNoData) && + ((Data != NULL) || (*DataLength != 0))) { + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection != EfiUsbNoData) && + ((Data == NULL) || (*DataLength == 0))) { + return EFI_INVALID_PARAMETER; + } + + if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) && + (MaximumPacketLength != 32) && (MaximumPacketLength != 64) && + (MaximumPacketLength != 512) + ) { + return EFI_INVALID_PARAMETER; + } + + if ((DeviceSpeed == EFI_USB_SPEED_LOW) && (MaximumPacketLength != 8)) { + return EFI_INVALID_PARAMETER; + } + + if ((DeviceSpeed == EFI_USB_SPEED_SUPER) && (MaximumPacketLength != 512)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = XHC_FROM_THIS (This); + + Status = EFI_DEVICE_ERROR; + *TransferResult = EFI_USB_ERR_SYSTEM; + + if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) { + DEBUG ((EFI_D_ERROR, "XhcControlTransfer: HC halted at entrance\n")); + goto ON_EXIT; + } + + // + // Check if the device is still enabled before every transaction. + // + SlotId = XhcBusDevAddrToSlotId (DeviceAddress); + if (SlotId == 0) { + goto ON_EXIT; + } + + // + // Acquire the actual device address assigned by XHCI's Address_Device cmd. + // + XhciDevAddr = UsbDevContext[SlotId].XhciDevAddr; + + // + // Hook the Set_Address request from UsbBus. + // According to XHCI 1.0 spec, the Set_Address request is replaced by XHCI's Address_Device cmd. + // + if (Request->Request == USB_REQ_SET_ADDRESS) { + // + // Reset the BusDevAddr field of all disabled entries in UsbDevContext array firstly. + // This way is used to clean the history to avoid using wrong device address by XhcAsyncInterruptTransfer(). + // + for (Index = 0; Index < 255; Index++) { + if (!UsbDevContext[Index + 1].Enabled && + (UsbDevContext[Index + 1].SlotId != 0) && + (UsbDevContext[Index + 1].BusDevAddr == (UINT8)Request->Value)) { + UsbDevContext[Index + 1].BusDevAddr = 0; + } + } + // + // The actual device address has been assigned by XHCI during initializing the device slot. + // So we just need establish the mapping relationship between the device address requested from UsbBus + // and the actual device address assigned by XHCI. The the following invocations through EFI_USB2_HC_PROTOCOL interface + // can find out the actual device address by it. + // + UsbDevContext[SlotId].BusDevAddr = (UINT8)Request->Value; + Status = EFI_SUCCESS; + goto ON_EXIT; + } + + // + // BUGBUG: If the port reset operation happens after the usb super speed device is enabled, + // The subsequent configuration, such as getting device descriptor, will fail. + // So here a workaround is introduced to skip the reset operation if the device is enabled. + // + if ((Request->Request == USB_REQ_SET_FEATURE) && + (Request->Value == EfiUsbPortReset)) { + if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + Status = EFI_SUCCESS; + goto ON_EXIT; + } + } + + // + // Create a new URB, insert it into the asynchronous + // schedule list, then poll the execution status. + // Note that we encode the direction in address although default control + // endpoint is bidirectional. XhcCreateUrb expects this + // combination of Ep addr and its direction. + // + Endpoint = 0 | ((TransferDirection == EfiUsbDataIn) ? 0x80 : 0); + Urb = XhcCreateUrb ( + Xhc, + XhciDevAddr, + Endpoint, + DeviceSpeed, + MaximumPacketLength, + XHC_CTRL_TRANSFER, + Request, + Data, + *DataLength, + NULL, + NULL + ); + + if (Urb == NULL) { + DEBUG ((EFI_D_ERROR, "XhcControlTransfer: failed to create URB")); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + ASSERT (Urb->EvtRing == &Xhc->CtrlTrEventRing); + Status = XhcExecTransfer (Xhc, FALSE, Urb, TimeOut); + + // + // Get the status from URB. The result is updated in XhcCheckUrbResult + // which is called by XhcExecTransfer + // + *TransferResult = Urb->Result; + *DataLength = Urb->Completed; + + if (*TransferResult == EFI_USB_NOERROR) { + Status = EFI_SUCCESS; + } else if ((*TransferResult == EFI_USB_ERR_STALL) || + (*TransferResult == EFI_USB_ERR_TIMEOUT)) { + RecoveryStatus = XhcRecoverHaltedEndpoint(Xhc, Urb); + ASSERT_EFI_ERROR (RecoveryStatus); + goto FREE_URB; + } + + // + // Hook Get_Descriptor request from UsbBus as we need evaluate context and configure endpoint. + // Hook Get_Status request form UsbBus as we need trace device attach/detach event happened at hub. + // Hook Set_Config request from UsbBus as we need configure device endpoint. + // + if (Request->Request == USB_REQ_GET_DESCRIPTOR) { + DescriptorType = (UINT8)(Request->Value >> 8); + if ((DescriptorType == USB_DESC_TYPE_DEVICE) && (*DataLength == sizeof (EFI_USB_DEVICE_DESCRIPTOR))) { + // + // Store a copy of device scriptor as hub device need this info to configure endpoint. + // + CopyMem (&UsbDevContext[SlotId].DevDesc, Data, *DataLength); + if (UsbDevContext[SlotId].DevDesc.BcdUSB == 0x0300) { + // + // If it's a usb3.0 device, then its max packet size is a 2^n. + // + MaxPacket0 = 1 << UsbDevContext[SlotId].DevDesc.MaxPacketSize0; + } else { + MaxPacket0 = UsbDevContext[SlotId].DevDesc.MaxPacketSize0; + } + UsbDevContext[SlotId].ConfDesc = AllocateZeroPool (UsbDevContext[SlotId].DevDesc.NumConfigurations * sizeof (EFI_USB_CONFIG_DESCRIPTOR *)); + Status = XhcEvaluateContext (Xhc, SlotId, MaxPacket0); + ASSERT_EFI_ERROR (Status); + } else if ((DescriptorType == USB_DESC_TYPE_CONFIG) && (*DataLength == ((UINT16 *)Data)[1])) { + // + // Get configuration value from request, Store the configuration descriptor for Configure_Endpoint cmd. + // + Index = (UINT8)Request->Value; + ASSERT (Index < UsbDevContext[SlotId].DevDesc.NumConfigurations); + UsbDevContext[SlotId].ConfDesc[Index] = AllocateZeroPool(*DataLength); + CopyMem (UsbDevContext[SlotId].ConfDesc[Index], Data, *DataLength); + } else if (((DescriptorType == USB_DESC_TYPE_HUB) || + (DescriptorType == USB_DESC_TYPE_HUB_SUPER_SPEED))) { + HubDesc = (EFI_USB_HUB_DESCRIPTOR *)Data; + // + // The bit 5,6 of HubCharacter field of Hub Descriptor is TTT. + // + TTT = (UINT8)((HubDesc->HubCharacter & (BIT5 | BIT6)) >> 5); + if (UsbDevContext[SlotId].DevDesc.DeviceProtocol == 2) { + // + // BUGBUG: Don't support multi-TT feature for super speed hub. + // + MTT = 1; + ASSERT (FALSE); + } else { + MTT = 0; + } + + Status = XhcConfigHubContext ( + Xhc, + SlotId, + HubDesc->NumPorts, + TTT, + MTT + ); + } + } else if (Request->Request == USB_REQ_SET_CONFIG) { + // + // Hook Set_Config request from UsbBus as we need configure device endpoint. + // + for (Index = 0; Index < UsbDevContext[SlotId].DevDesc.NumConfigurations; Index++) { + if (UsbDevContext[SlotId].ConfDesc[Index]->ConfigurationValue == (UINT8)Request->Value) { + XhcSetConfigCmd (Xhc, SlotId, DeviceSpeed, UsbDevContext[SlotId].ConfDesc[Index]); + break; + } + } + } else if (Request->Request == USB_REQ_GET_STATUS) { + // + // Hook Get_Status request from UsbBus to keep track of the port status change. + // + State = *(UINT32 *)Data; + PortStatus.PortStatus = 0; + PortStatus.PortChangeStatus = 0; + + if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + // + // For super speed hub, its bit10~12 presents the attached device speed. + // + if ((*(UINT32 *)Data & XHC_PORTSC_PS) >> 10 == 0) { + PortStatus.PortStatus |= USB_PORT_STAT_SUPER_SPEED; + } + } else if (DeviceSpeed == EFI_USB_SPEED_HIGH) { + // + // For high speed hub, its bit9~10 presents the attached device speed. + // + if (XHC_BIT_IS_SET (*(UINT32 *)Data, BIT9)) { + PortStatus.PortStatus |= USB_PORT_STAT_LOW_SPEED; + } else if (XHC_BIT_IS_SET (*(UINT32 *)Data, BIT10)) { + PortStatus.PortStatus |= USB_PORT_STAT_HIGH_SPEED; + } + } else { + ASSERT (0); + } + + // + // Convert the XHCI port/port change state to UEFI status + // + MapSize = sizeof (mUsbPortStateMap) / sizeof (USB_PORT_STATE_MAP); + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (*(UINT32 *)Data, mUsbPortStateMap[Index].HwState)) { + PortStatus.PortStatus = (UINT16) (PortStatus.PortStatus | mUsbPortStateMap[Index].UefiState); + } + } + MapSize = sizeof (mUsbPortChangeMap) / sizeof (USB_PORT_STATE_MAP); + + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (*(UINT32 *)Data, mUsbPortChangeMap[Index].HwState)) { + PortStatus.PortChangeStatus = (UINT16) (PortStatus.PortChangeStatus | mUsbPortChangeMap[Index].UefiState); + } + } + + XhcPollPortStatusChange (Xhc, UsbDevContext[SlotId].RouteString, (UINT8)Request->Index, &PortStatus); + } + +FREE_URB: + FreePool (Urb); + +ON_EXIT: + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcControlTransfer: error - %r, transfer - %x\n", Status, *TransferResult)); + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Submits bulk transfer to a bulk endpoint of a USB device. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and its direction in bit 7. + @param DeviceSpeed Device speed, Low speed device doesn't support bulk + transfer. + @param MaximumPacketLength Maximum packet size the endpoint is capable of + sending or receiving. + @param DataBuffersNumber Number of data buffers prepared for the transfer. + @param Data Array of pointers to the buffers of data to transmit + from or receive into. + @param DataLength The lenght of the data buffer. + @param DataToggle On input, the initial data toggle for the transfer; + On output, it is updated to to next data toggle to + use of the subsequent bulk transfer. + @param TimeOut Indicates the maximum time, in millisecond, which + the transfer is allowed to complete. + @param Translator A pointr to the transaction translator data. + @param TransferResult A pointer to the detailed result information of the + bulk transfer. + + @retval EFI_SUCCESS The transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_TIMEOUT The transfer failed due to timeout. + @retval EFI_DEVICE_ERROR The transfer failed due to host controller error. + +**/ +EFI_STATUS +EFIAPI +XhcBulkTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN UINT8 DataBuffersNumber, + IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM], + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + USB_XHCI_DEV *Xhc; + URB *Urb; + UINT8 XhciDevAddr; + UINT8 SlotId; + EFI_STATUS Status; + EFI_STATUS RecoveryStatus; + EFI_TPL OldTpl; + + // + // Validate the parameters + // + if ((DataLength == NULL) || (*DataLength == 0) || + (Data == NULL) || (Data[0] == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 0) && (*DataToggle != 1)) { + return EFI_INVALID_PARAMETER; + } + + if ((DeviceSpeed == EFI_USB_SPEED_LOW) || + ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) || + ((EFI_USB_SPEED_HIGH == DeviceSpeed) && (MaximumPacketLength > 512)) || + ((EFI_USB_SPEED_SUPER == DeviceSpeed) && (MaximumPacketLength > 1024))) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = XHC_FROM_THIS (This); + + *TransferResult = EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + + if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) { + DEBUG ((EFI_D_ERROR, "XhcBulkTransfer: HC is halted\n")); + goto ON_EXIT; + } + + // + // Check if the device is still enabled before every transaction. + // + SlotId = XhcBusDevAddrToSlotId (DeviceAddress); + if (SlotId == 0) { + goto ON_EXIT; + } + + // + // Acquire the actual device address assigned by XHCI's Address_Device cmd. + // + XhciDevAddr = UsbDevContext[SlotId].XhciDevAddr; + + // + // Create a new URB, insert it into the asynchronous + // schedule list, then poll the execution status. + // + Urb = XhcCreateUrb ( + Xhc, + XhciDevAddr, + EndPointAddress, + DeviceSpeed, + MaximumPacketLength, + XHC_BULK_TRANSFER, + NULL, + Data[0], + *DataLength, + NULL, + NULL + ); + + if (Urb == NULL) { + DEBUG ((EFI_D_ERROR, "XhcBulkTransfer: failed to create URB\n")); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + ASSERT (Urb->EvtRing == &Xhc->BulkTrEventRing); + + Status = XhcExecTransfer (Xhc, FALSE, Urb, TimeOut); + + *TransferResult = Urb->Result; + *DataLength = Urb->Completed; + + if (*TransferResult == EFI_USB_NOERROR) { + Status = EFI_SUCCESS; + } else if ((*TransferResult == EFI_USB_ERR_STALL) || + (*TransferResult == EFI_USB_ERR_TIMEOUT)) { + RecoveryStatus = XhcRecoverHaltedEndpoint(Xhc, Urb); + ASSERT_EFI_ERROR (RecoveryStatus); + } + + FreePool (Urb); + +ON_EXIT: + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcBulkTransfer: error - %r, transfer - %x\n", Status, *TransferResult)); + } + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Submits an asynchronous interrupt transfer to an + interrupt endpoint of a USB device. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and its direction encoded in bit 7 + @param DeviceSpeed Indicates device speed. + @param MaximumPacketLength Maximum packet size the target endpoint is capable + @param IsNewTransfer If TRUE, to submit an new asynchronous interrupt + transfer If FALSE, to remove the specified + asynchronous interrupt. + @param DataToggle On input, the initial data toggle to use; on output, + it is updated to indicate the next data toggle. + @param PollingInterval The he interval, in milliseconds, that the transfer + is polled. + @param DataLength The length of data to receive at the rate specified + by PollingInterval. + @param Translator Transaction translator to use. + @param CallBackFunction Function to call at the rate specified by + PollingInterval. + @param Context Context to CallBackFunction. + + @retval EFI_SUCCESS The request has been successfully submitted or canceled. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request failed due to a lack of resources. + @retval EFI_DEVICE_ERROR The transfer failed due to host controller error. + +**/ +EFI_STATUS +EFIAPI +XhcAsyncInterruptTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN BOOLEAN IsNewTransfer, + IN OUT UINT8 *DataToggle, + IN UINTN PollingInterval, + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, + IN VOID *Context OPTIONAL + ) +{ + USB_XHCI_DEV *Xhc; + URB *Urb; + EFI_STATUS Status; + UINT8 XhciDevAddr; + UINT8 SlotId; + UINT8 Index; + UINT8 *Data; + EFI_TPL OldTpl; + + // + // Validate parameters + // + if (!XHCI_IS_DATAIN (EndPointAddress)) { + return EFI_INVALID_PARAMETER; + } + + if (IsNewTransfer) { + if (DataLength == 0) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((PollingInterval > 255) || (PollingInterval < 1)) { + return EFI_INVALID_PARAMETER; + } + } + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = XHC_FROM_THIS (This); + + // + // Delete Async interrupt transfer request. + // + if (!IsNewTransfer) { + // + // The delete request may happen after device is detached. + // + for (Index = 0; Index < 255; Index++) { + if ((UsbDevContext[Index + 1].SlotId != 0) && + (UsbDevContext[Index + 1].BusDevAddr == DeviceAddress)) { + break; + } + } + + if (Index == 255) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + // + // Acquire the actual device address assigned by XHCI's Address_Device cmd. + // + XhciDevAddr = UsbDevContext[Index + 1].XhciDevAddr; + + Status = XhciDelAsyncIntTransfer (Xhc, XhciDevAddr, EndPointAddress); + DEBUG ((EFI_D_INFO, "XhcAsyncInterruptTransfer: remove old transfer, Status = %r\n", Status)); + goto ON_EXIT; + } + + Status = EFI_SUCCESS; + + if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) { + DEBUG ((EFI_D_ERROR, "XhcAsyncInterruptTransfer: HC is halt\n")); + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + // + // Check if the device is still enabled before every transaction. + // + SlotId = XhcBusDevAddrToSlotId (DeviceAddress); + if (SlotId == 0) { + goto ON_EXIT; + } + + // + // Acquire the actual device address assigned by XHCI's Address_Device cmd. + // + XhciDevAddr = UsbDevContext[SlotId].XhciDevAddr; + + Data = AllocatePool (DataLength); + + if (Data == NULL) { + DEBUG ((EFI_D_ERROR, "XhcAsyncInterruptTransfer: failed to allocate buffer\n")); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Urb = XhcCreateUrb ( + Xhc, + XhciDevAddr, + EndPointAddress, + DeviceSpeed, + MaximumPacketLength, + XHC_INT_TRANSFER_ASYNC, + NULL, + Data, + DataLength, + CallBackFunction, + Context + ); + + if (Urb == NULL) { + DEBUG ((EFI_D_ERROR, "XhcAsyncInterruptTransfer: failed to create URB\n")); + FreePool (Data); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + ASSERT (Urb->EvtRing == &Xhc->AsynIntTrEventRing); + + InsertHeadList (&Xhc->AsyncIntTransfers, &Urb->UrbList); + // + // Ring the doorbell + // + Status = RingIntTransferDoorBell (Xhc, Urb); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Submits synchronous interrupt transfer to an interrupt endpoint + of a USB device. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and its direction encoded in bit 7 + @param DeviceSpeed Indicates device speed. + @param MaximumPacketLength Maximum packet size the target endpoint is capable + of sending or receiving. + @param Data Buffer of data that will be transmitted to USB + device or received from USB device. + @param DataLength On input, the size, in bytes, of the data buffer; On + output, the number of bytes transferred. + @param DataToggle On input, the initial data toggle to use; on output, + it is updated to indicate the next data toggle. + @param TimeOut Maximum time, in second, to complete. + @param Translator Transaction translator to use. + @param TransferResult Variable to receive the transfer result. + + @return EFI_SUCCESS The transfer was completed successfully. + @return EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource. + @return EFI_INVALID_PARAMETER Some parameters are invalid. + @return EFI_TIMEOUT The transfer failed due to timeout. + @return EFI_DEVICE_ERROR The failed due to host controller or device error + +**/ +EFI_STATUS +EFIAPI +XhcSyncInterruptTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + USB_XHCI_DEV *Xhc; + URB *Urb; + UINT8 XhciDevAddr; + UINT8 SlotId; + EFI_STATUS Status; + EFI_STATUS RecoveryStatus; + EFI_TPL OldTpl; + + // + // Validates parameters + // + if ((DataLength == NULL) || (*DataLength == 0) || + (Data == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!XHCI_IS_DATAIN (EndPointAddress)) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + if (((DeviceSpeed == EFI_USB_SPEED_LOW) && (MaximumPacketLength != 8)) || + ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) || + ((DeviceSpeed == EFI_USB_SPEED_HIGH) && (MaximumPacketLength > 3072))) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = XHC_FROM_THIS (This); + + *TransferResult = EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + + if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) { + DEBUG ((EFI_D_ERROR, "EhcSyncInterruptTransfer: HC is halt\n")); + goto ON_EXIT; + } + + // + // Check if the device is still enabled before every transaction. + // + SlotId = XhcBusDevAddrToSlotId (DeviceAddress); + if (SlotId == 0) { + goto ON_EXIT; + } + + // + // Acquire the actual device address assigned by XHCI's Address_Device cmd. + // + XhciDevAddr = UsbDevContext[SlotId].XhciDevAddr; + + Urb = XhcCreateUrb ( + Xhc, + XhciDevAddr, + EndPointAddress, + DeviceSpeed, + MaximumPacketLength, + XHC_INT_TRANSFER_SYNC, + NULL, + Data, + *DataLength, + NULL, + NULL + ); + + if (Urb == NULL) { + DEBUG ((EFI_D_ERROR, "XhcSyncInterruptTransfer: failed to create URB\n")); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = XhcExecTransfer (Xhc, FALSE, Urb, TimeOut); + + *TransferResult = Urb->Result; + *DataLength = Urb->Completed; + + if (*TransferResult == EFI_USB_NOERROR) { + Status = EFI_SUCCESS; + } else if ((*TransferResult == EFI_USB_ERR_STALL) || + (*TransferResult == EFI_USB_ERR_TIMEOUT)) { + RecoveryStatus = XhcRecoverHaltedEndpoint(Xhc, Urb); + ASSERT_EFI_ERROR (RecoveryStatus); + } + + FreePool (Urb); + +ON_EXIT: + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcSyncInterruptTransfer: error - %r, transfer - %x\n", Status, *TransferResult)); + } + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Submits isochronous transfer to a target USB device. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress Target device address. + @param EndPointAddress End point address with its direction. + @param DeviceSpeed Device speed, Low speed device doesn't support this + type. + @param MaximumPacketLength Maximum packet size that the endpoint is capable of + sending or receiving. + @param DataBuffersNumber Number of data buffers prepared for the transfer. + @param Data Array of pointers to the buffers of data that will + be transmitted to USB device or received from USB + device. + @param DataLength The size, in bytes, of the data buffer. + @param Translator Transaction translator to use. + @param TransferResult Variable to receive the transfer result. + + @return EFI_UNSUPPORTED Isochronous transfer is unsupported. + +**/ +EFI_STATUS +EFIAPI +XhcIsochronousTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN UINT8 DataBuffersNumber, + IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Submits Async isochronous transfer to a target USB device. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress Target device address. + @param EndPointAddress End point address with its direction. + @param DeviceSpeed Device speed, Low speed device doesn't support this + type. + @param MaximumPacketLength Maximum packet size that the endpoint is capable of + sending or receiving. + @param DataBuffersNumber Number of data buffers prepared for the transfer. + @param Data Array of pointers to the buffers of data that will + be transmitted to USB device or received from USB + device. + @param DataLength The size, in bytes, of the data buffer. + @param Translator Transaction translator to use. + @param IsochronousCallBack Function to be called when the transfer complete. + @param Context Context passed to the call back function as + parameter. + + @return EFI_UNSUPPORTED Isochronous transfer isn't supported. + +**/ +EFI_STATUS +EFIAPI +XhcAsyncIsochronousTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN UINT8 DataBuffersNumber, + IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack, + IN VOID *Context + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Entry point for EFI drivers. + + @param ImageHandle EFI_HANDLE. + @param SystemTable EFI_SYSTEM_TABLE. + + @retval EFI_SUCCESS Success. + @retval Others Fail. + +**/ +EFI_STATUS +EFIAPI +XhcDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gXhciDriverBinding, + ImageHandle, + &gXhciComponentName, + &gXhciComponentName2 + ); +} + + +/** + Test to see if this driver supports ControllerHandle. Any + ControllerHandle that has Usb2HcProtocol installed will + be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @return EFI_SUCCESS This driver supports this device. + @return EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +XhcDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + USB_CLASSC UsbClassCReg; + + // + // Test whether there is PCI IO Protocol attached on the controller handle. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + PCI_CLASSCODE_OFFSET, + sizeof (USB_CLASSC) / sizeof (UINT8), + &UsbClassCReg + ); + + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + + // + // Test whether the controller belongs to Xhci type + // + if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || + (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) || + (UsbClassCReg.ProgInterface != PCI_IF_XHCI)) { + Status = EFI_UNSUPPORTED; + } + +ON_EXIT: + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + +/** + Create and initialize a USB_XHCI_DEV. + + @param PciIo The PciIo on this device. + @param OriginalPciAttributes Original PCI attributes. + + @return The allocated and initialized USB_XHCI_DEV structure if created, + otherwise NULL. + +**/ +USB_XHCI_DEV* +XhcCreateUsbHc ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT64 OriginalPciAttributes + ) +{ + USB_XHCI_DEV *Xhc; + EFI_STATUS Status; + UINT32 PageSize; + UINT16 ExtCapReg; + + ZeroMem (UsbDevContext, sizeof (UsbDevContext)); + + Xhc = AllocateZeroPool (sizeof (USB_XHCI_DEV)); + + if (Xhc == NULL) { + return NULL; + } + + // + // Init EFI_USB2_HC_PROTOCOL interface and private data structure + // + Xhc->Signature = USB_XHCI_DEV_SIGNATURE; + + Xhc->Usb2Hc.GetCapability = XhcGetCapability; + Xhc->Usb2Hc.Reset = XhcReset; + Xhc->Usb2Hc.GetState = XhcGetState; + Xhc->Usb2Hc.SetState = XhcSetState; + Xhc->Usb2Hc.ControlTransfer = XhcControlTransfer; + Xhc->Usb2Hc.BulkTransfer = XhcBulkTransfer; + Xhc->Usb2Hc.AsyncInterruptTransfer = XhcAsyncInterruptTransfer; + Xhc->Usb2Hc.SyncInterruptTransfer = XhcSyncInterruptTransfer; + Xhc->Usb2Hc.IsochronousTransfer = XhcIsochronousTransfer; + Xhc->Usb2Hc.AsyncIsochronousTransfer = XhcAsyncIsochronousTransfer; + Xhc->Usb2Hc.GetRootHubPortStatus = XhcGetRootHubPortStatus; + Xhc->Usb2Hc.SetRootHubPortFeature = XhcSetRootHubPortFeature; + Xhc->Usb2Hc.ClearRootHubPortFeature = XhcClearRootHubPortFeature; + Xhc->Usb2Hc.MajorRevision = 0x3; + Xhc->Usb2Hc.MinorRevision = 0x0; + + Xhc->PciIo = PciIo; + Xhc->OriginalPciAttributes = OriginalPciAttributes; + + InitializeListHead (&Xhc->AsyncIntTransfers); + + // + // Be caution that the Offset passed to XhcReadCapReg() should be Dword align + // + Xhc->CapLength = XhcReadCapReg8 (Xhc, XHC_CAPLENGTH_OFFSET); + Xhc->HcSParams1.Dword = XhcReadCapReg (Xhc, XHC_HCSPARAMS1_OFFSET); + Xhc->HcSParams2.Dword = XhcReadCapReg (Xhc, XHC_HCSPARAMS2_OFFSET); + Xhc->HcCParams.Dword = XhcReadCapReg (Xhc, XHC_HCCPARAMS_OFFSET); + Xhc->DBOff = XhcReadCapReg (Xhc, XHC_DBOFF_OFFSET); + Xhc->RTSOff = XhcReadCapReg (Xhc, XHC_RTSOFF_OFFSET); + + // + // This PageSize field defines the page size supported by the xHC implementation. + // This xHC supports a page size of 2^(n+12) if bit n is Set. For example, + // if bit 0 is Set, the xHC supports 4k byte page sizes. + // + PageSize = XhcReadOpReg(Xhc, XHC_PAGESIZE_OFFSET) & XHC_PAGESIZE_MASK; + Xhc->PageSize = 1 << (HighBitSet32(PageSize) + 12); + ASSERT (Xhc->PageSize == 0x1000); + + ExtCapReg = (UINT16) (Xhc->HcCParams.Data.ExtCapReg); + Xhc->ExtCapRegBase = ExtCapReg << 2; + Xhc->UsbLegSupOffset = XhcGetLegSupCapAddr (Xhc); + + DEBUG ((EFI_D_INFO, "XhcCreateUsb3Hc: capability length 0x%x\n", Xhc->CapLength)); + DEBUG ((EFI_D_INFO, "XhcCreateUsb3Hc: HcSParams1 0x%x\n", Xhc->HcSParams1)); + DEBUG ((EFI_D_INFO, "XhcCreateUsb3Hc: HcSParams2 0x%x\n", Xhc->HcSParams2)); + DEBUG ((EFI_D_INFO, "XhcCreateUsb3Hc: HcCParams 0x%x\n", Xhc->HcCParams)); + DEBUG ((EFI_D_INFO, "XhcCreateUsb3Hc: DBOff 0x%x\n", Xhc->DBOff)); + DEBUG ((EFI_D_INFO, "XhcCreateUsb3Hc: RTSOff 0x%x\n", Xhc->RTSOff)); + DEBUG ((EFI_D_INFO, "XhcCreateUsb3Hc: Xhc->UsbLegSupOffset 0x%x\n", Xhc->UsbLegSupOffset)); + + // + // Create AsyncRequest Polling Timer + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + XhcMonitorAsyncRequests, + Xhc, + &Xhc->PollTimer + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return Xhc; + +ON_ERROR: + FreePool (Xhc); + return NULL; +} + +/** + One notified function to stop the Host Controller when gBS->ExitBootServices() called. + + @param Event Pointer to this event + @param Context Event hanlder private data + +**/ +VOID +EFIAPI +XhcExitBootService ( + EFI_EVENT Event, + VOID *Context + ) + +{ + USB_XHCI_DEV *Xhc; + EFI_PCI_IO_PROTOCOL *PciIo; + + Xhc = (USB_XHCI_DEV*) Context; + PciIo = Xhc->PciIo; + + // + // Stop AsyncRequest Polling timer then stop the XHCI driver + // and uninstall the XHCI protocl. + // + gBS->SetTimer (Xhc->PollTimer, TimerCancel, XHC_ASYNC_POLL_INTERVAL); + XhcHaltHC (Xhc, XHC_GENERIC_TIMEOUT); + + if (Xhc->PollTimer != NULL) { + gBS->CloseEvent (Xhc->PollTimer); + } + + // + // Restore original PCI attributes + // + PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + Xhc->OriginalPciAttributes, + NULL + ); + + XhcClearBiosOwnership (Xhc); +} + +/** + Starting the Usb XHCI Driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @return EFI_SUCCESS supports this device. + @return EFI_UNSUPPORTED do not support this device. + @return EFI_DEVICE_ERROR cannot be started due to device Error. + @return EFI_OUT_OF_RESOURCES cannot allocate resources. + +**/ +EFI_STATUS +EFIAPI +XhcDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 Supports; + UINT64 OriginalPciAttributes; + BOOLEAN PciAttributesSaved; + USB_XHCI_DEV *Xhc; + + // + // Open the PciIo Protocol, then enable the USB host controller + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + PciAttributesSaved = FALSE; + // + // Save original PCI attributes + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationGet, + 0, + &OriginalPciAttributes + ); + + if (EFI_ERROR (Status)) { + goto CLOSE_PCIIO; + } + PciAttributesSaved = TRUE; + + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= EFI_PCI_DEVICE_ENABLE; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcDriverBindingStart: failed to enable controller\n")); + goto CLOSE_PCIIO; + } + + // + // Create then install USB2_HC_PROTOCOL + // + Xhc = XhcCreateUsbHc (PciIo, OriginalPciAttributes); + + if (Xhc == NULL) { + DEBUG ((EFI_D_ERROR, "XhcDriverBindingStart: failed to create USB2_HC\n")); + return EFI_OUT_OF_RESOURCES; + } + + XhcSetBiosOwnership (Xhc); + + XhcResetHC (Xhc, XHC_RESET_TIMEOUT); + ASSERT (XhcIsHalt (Xhc)); + + // + // After Chip Hardware Reset wait until the Controller Not Ready (CNR) flag + // in the USBSTS is '0' before writing any xHC Operational or Runtime registers. + // + ASSERT (!(XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_CNR))); + + // + // Initialize the schedule + // + XhcInitSched (Xhc); + + // + // Start the Host Controller + // + XhcRunHC(Xhc, XHC_GENERIC_TIMEOUT); + + // + // Start the asynchronous interrupt monitor + // + Status = gBS->SetTimer (Xhc->PollTimer, TimerPeriodic, XHC_ASYNC_POLL_INTERVAL); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcDriverBindingStart: failed to start async interrupt monitor\n")); + XhcHaltHC (Xhc, XHC_GENERIC_TIMEOUT); + goto FREE_POOL; + } + + // + // Create event to stop the HC when exit boot service. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + XhcExitBootService, + Xhc, + &gEfiEventExitBootServicesGuid, + &Xhc->ExitBootServiceEvent + ); + if (EFI_ERROR (Status)) { + goto FREE_POOL; + } + + // + // Install the component name protocol, don't fail the start + // because of something for display. + // + AddUnicodeString2 ( + "eng", + gXhciComponentName.SupportedLanguages, + &Xhc->ControllerNameTable, + L"eXtensible Host Controller (USB 3.0)", + TRUE + ); + AddUnicodeString2 ( + "en", + gXhciComponentName2.SupportedLanguages, + &Xhc->ControllerNameTable, + L"eXtensible Host Controller (USB 3.0)", + FALSE + ); + + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEfiUsb2HcProtocolGuid, + EFI_NATIVE_INTERFACE, + &Xhc->Usb2Hc + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcDriverBindingStart: failed to install USB2_HC Protocol\n")); + goto FREE_POOL; + } + + DEBUG ((EFI_D_INFO, "XhcDriverBindingStart: XHCI started for controller @ %x\n", Controller)); + return EFI_SUCCESS; + +FREE_POOL: + gBS->CloseEvent (Xhc->PollTimer); + XhcFreeSched (Xhc); + FreePool (Xhc); + +CLOSE_PCIIO: + if (PciAttributesSaved) { + // + // Restore original PCI attributes + // + PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + OriginalPciAttributes, + NULL + ); + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + 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. + + @return EFI_SUCCESS Success. + @return EFI_DEVICE_ERROR Fail. + +**/ +EFI_STATUS +EFIAPI +XhcDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_USB2_HC_PROTOCOL *Usb2Hc; + EFI_PCI_IO_PROTOCOL *PciIo; + USB_XHCI_DEV *Xhc; + + // + // Test whether the Controller handler passed in is a valid + // Usb controller handle that should be supported, if not, + // return the error status directly + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsb2HcProtocolGuid, + (VOID **) &Usb2Hc, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Xhc = XHC_FROM_THIS (Usb2Hc); + PciIo = Xhc->PciIo; + + // + // Stop AsyncRequest Polling timer then stop the XHCI driver + // and uninstall the XHCI protocl. + // + gBS->SetTimer (Xhc->PollTimer, TimerCancel, XHC_ASYNC_POLL_INTERVAL); + XhcHaltHC (Xhc, XHC_GENERIC_TIMEOUT); + XhcClearBiosOwnership (Xhc); + + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEfiUsb2HcProtocolGuid, + Usb2Hc + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (Xhc->PollTimer != NULL) { + gBS->CloseEvent (Xhc->PollTimer); + } + + if (Xhc->ExitBootServiceEvent != NULL) { + gBS->CloseEvent (Xhc->ExitBootServiceEvent); + } + + XhciDelAllAsyncIntTransfers (Xhc); + XhcFreeSched (Xhc); + + if (Xhc->ControllerNameTable) { + FreeUnicodeStringTable (Xhc->ControllerNameTable); + } + + // + // Restore original PCI attributes + // + PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + Xhc->OriginalPciAttributes, + NULL + ); + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + FreePool (Xhc); + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h b/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h new file mode 100644 index 0000000000..953ba4c5c2 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h @@ -0,0 +1,362 @@ +/** @file + + Provides some data structure definitions used by the XHCI host controller driver. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_XHCI_H_ +#define _EFI_XHCI_H_ + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef struct _USB_XHCI_DEV USB_XHCI_DEV; +typedef struct _USB_DEV_CONTEXT USB_DEV_CONTEXT; + +#include "XhciReg.h" +#include "XhciSched.h" +#include "ComponentName.h" + +// +// XHC timeout experience values +// +#define XHC_1_MICROSECOND 1 +#define XHC_1_MILLISECOND (1000 * XHC_1_MICROSECOND) +#define XHC_1_SECOND (1000 * XHC_1_MILLISECOND) + +// +// XHCI register operation timeout, set by experience +// +#define XHC_RESET_TIMEOUT (1 * XHC_1_SECOND) +#define XHC_GENERIC_TIMEOUT (10 * XHC_1_MILLISECOND) + +// +// Wait for roothub port power stable, refers to Spec[XHCI1.0-2.3.9] +// +#define XHC_ROOT_PORT_RECOVERY_STALL (20 * XHC_1_MILLISECOND) + +// +// Sync and Async transfer polling interval, set by experience, +// and the unit of Async is 100us, means 50ms as interval. +// +#define XHC_SYNC_POLL_INTERVAL (20 * XHC_1_MILLISECOND) +#define XHC_ASYNC_POLL_INTERVAL (50 * 10000U) + +// +// XHC raises TPL to TPL_NOTIFY to serialize all its operations +// to protect shared data structures. +// +#define XHC_TPL TPL_NOTIFY + +#define CMD_RING_TRB_NUMBER 0x40 +#define TR_RING_TRB_NUMBER 0x40 +#define ERST_NUMBER 0x01 +#define EVENT_RING_TRB_NUMBER 0x80 + +#define CMD_INTER 0 +#define CTRL_INTER 1 +#define BULK_INTER 2 +#define INT_INTER 3 +#define INT_INTER_ASYNC 4 + +// +// Iterate through the doule linked list. This is delete-safe. +// Don't touch NextEntry +// +#define EFI_LIST_FOR_EACH_SAFE(Entry, NextEntry, ListHead) \ + for (Entry = (ListHead)->ForwardLink, NextEntry = Entry->ForwardLink;\ + Entry != (ListHead); Entry = NextEntry, NextEntry = Entry->ForwardLink) + +#define EFI_LIST_CONTAINER(Entry, Type, Field) BASE_CR(Entry, Type, Field) + +#define XHC_LOW_32BIT(Addr64) ((UINT32)(((UINTN)(Addr64)) & 0xFFFFFFFF)) +#define XHC_HIGH_32BIT(Addr64) ((UINT32)(RShiftU64((UINTN)(Addr64), 32) & 0xFFFFFFFF)) +#define XHC_BIT_IS_SET(Data, Bit) ((BOOLEAN)(((Data) & (Bit)) == (Bit))) + +#define XHC_REG_BIT_IS_SET(Xhc, Offset, Bit) \ + (XHC_BIT_IS_SET(XhcReadOpReg ((Xhc), (Offset)), (Bit))) + +#define XHCI_IS_DATAIN(EndpointAddr) XHC_BIT_IS_SET((EndpointAddr), 0x80) + +#define USB_XHCI_DEV_SIGNATURE SIGNATURE_32 ('x', 'h', 'c', 'i') +#define XHC_FROM_THIS(a) CR(a, USB_XHCI_DEV, Usb2Hc, USB_XHCI_DEV_SIGNATURE) + +#define USB_DESC_TYPE_HUB 0x29 +#define USB_DESC_TYPE_HUB_SUPER_SPEED 0x2a + +// +// Xhci Data and Ctrl Structures +// +#pragma pack(1) +typedef struct { + UINT8 ProgInterface; + UINT8 SubClassCode; + UINT8 BaseCode; +} USB_CLASSC; + +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 NumPorts; + UINT16 HubCharacter; + UINT8 PwrOn2PwrGood; + UINT8 HubContrCurrent; + UINT8 Filler[16]; +} EFI_USB_HUB_DESCRIPTOR; +#pragma pack() + +struct _USB_XHCI_DEV { + UINT32 Signature; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 OriginalPciAttributes; + + EFI_USB2_HC_PROTOCOL Usb2Hc; + + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + // + // ExitBootServicesEvent is used to set OS semaphore and + // stop the XHC DMA operation after exit boot service. + // + EFI_EVENT ExitBootServiceEvent; + EFI_EVENT PollTimer; + LIST_ENTRY AsyncIntTransfers; + + UINT8 CapLength; ///< Capability Register Length + XHC_HCSPARAMS1 HcSParams1; ///< Structural Parameters 1 + XHC_HCSPARAMS2 HcSParams2; ///< Structural Parameters 2 + XHC_HCCPARAMS HcCParams; ///< Capability Parameters + UINT32 DBOff; ///< Doorbell Offset + UINT32 RTSOff; ///< Runtime Register Space Offset + UINT16 MaxInterrupt; + UINT32 PageSize; + UINT64 *ScratchBuf; + UINT32 MaxScratchpadBufs; + UINT32 ExtCapRegBase; + UINT32 UsbLegSupOffset; + UINT64 *DCBAA; + UINT32 MaxSlotsEn; + // + // Cmd Transfer Ring + // + TRANSFER_RING CmdRing; + // + // CmdEventRing + // + EVENT_RING CmdEventRing; + // + // ControlTREventRing + // + EVENT_RING CtrlTrEventRing; + // + // BulkTREventRing + // + EVENT_RING BulkTrEventRing; + // + // IntTREventRing + // + EVENT_RING IntTrEventRing; + // + // AsyncIntTREventRing + // + EVENT_RING AsynIntTrEventRing; + // + // Misc + // + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + +}; + +struct _USB_DEV_CONTEXT { + // + // Whether this entry in UsbDevContext array is used or not. + // + BOOLEAN Enabled; + // + // The slot id assigned to the new device through XHCI's Enable_Slot cmd. + // + UINT8 SlotId; + // + // The route string presented an attached usb device. + // + USB_DEV_ROUTE RouteString; + // + // The route string of parent device if it exists. Otherwise it's zero. + // + USB_DEV_ROUTE ParentRouteString; + // + // The actual device address assigned by XHCI through Address_Device command. + // + UINT8 XhciDevAddr; + // + // The requested device address from UsbBus driver through Set_Address standard usb request. + // As XHCI spec replaces this request with Address_Device command, we have to record the + // requested device address and establish a mapping relationship with the actual device address. + // Then UsbBus driver just need to be aware of the requested device address to access usb device + // through EFI_USB2_HC_PROTOCOL. Xhci driver would be responsible for translating it to actual + // device address and access the actual device. + // + UINT8 BusDevAddr; + // + // The pointer to the input device context. + // + VOID *InputContext; + // + // The pointer to the output device context. + // + VOID *OutputDevContxt; + // + // The transfer queue for every endpoint. + // + VOID *EndpointTransferRing[31]; + // + // The device descriptor which is stored to support XHCI's Evaluate_Context cmd. + // + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + // + // As a usb device may include multiple configuration descriptors, we dynamically allocate an array + // to store them. + // Note that every configuration descriptor stored here includes those lower level descriptors, + // such as Interface descriptor, Endpoint descriptor, and so on. + // These information is used to support XHCI's Config_Endpoint cmd. + // + EFI_USB_CONFIG_DESCRIPTOR **ConfDesc; +}; + +extern EFI_DRIVER_BINDING_PROTOCOL gXhciDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gXhciComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gXhciComponentName2; +extern USB_DEV_CONTEXT UsbDevContext[]; + +/** + Test to see if this driver supports ControllerHandle. Any + ControllerHandle that has Usb2HcProtocol installed will + be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @return EFI_SUCCESS This driver supports this device. + @return EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +XhcDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Starting the Usb XHCI Driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @return EFI_SUCCESS supports this device. + @return EFI_UNSUPPORTED do not support this device. + @return EFI_DEVICE_ERROR cannot be started due to device Error. + @return EFI_OUT_OF_RESOURCES cannot allocate resources. + +**/ +EFI_STATUS +EFIAPI +XhcDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + 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. + + @return EFI_SUCCESS Success. + @return EFI_DEVICE_ERROR Fail. + +**/ +EFI_STATUS +EFIAPI +XhcDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Sets a feature for the specified root hub port. + + @param This This EFI_USB2_HC_PROTOCOL instance. + @param PortNumber Root hub port to set. + @param PortFeature Feature to set. + + @retval EFI_SUCCESS The feature specified by PortFeature was set. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + @retval EFI_DEVICE_ERROR Can't read register. + +**/ +EFI_STATUS +EFIAPI +XhcSetRootHubPortFeature ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ); + +/** + Clears a feature for the specified root hub port. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param PortNumber Specifies the root hub port whose feature is + requested to be cleared. + @param PortFeature Indicates the feature selector associated with the + feature clear request. + + @retval EFI_SUCCESS The feature specified by PortFeature was cleared + for the USB root hub port specified by PortNumber. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + @retval EFI_DEVICE_ERROR Can't read register. + +**/ +EFI_STATUS +EFIAPI +XhcClearRootHubPortFeature ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf b/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf new file mode 100644 index 0000000000..da701a62b6 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf @@ -0,0 +1,77 @@ +## @file +# +# Component Description File For XhciDxe Module. +# +# XhciDxe driver is responsible for managing the behavior of XHCI controller. +# It implements the interfaces of monitoring the status of all ports and transferring +# Control, Bulk, Interrupt and Isochronous requests to those attached usb LS/FS/HS/SS devices. +# +# Copyright (c) 2011, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = XhciDxe + FILE_GUID = B7F50E91-A759-412c-ADE4-DCD03E7F7C28 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = XhcDriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gXhciDriverBinding +# COMPONENT_NAME = gXhciComponentName +# COMPONENT_NAME2 = gXhciComponentName2 +# + +[Sources] + Xhci.c + XhciReg.c + XhciSched.c + ComponentName.c + ComponentName.h + Xhci.h + XhciReg.h + XhciSched.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + PcdLib + +[Guids] + gEfiEventExitBootServicesGuid ## PRODUCES ## Event + +[Protocols] + gEfiPciIoProtocolGuid ## TO_START + gEfiUsb2HcProtocolGuid ## BY_START + +# [Event] +# ## +# # Periodic timer event for checking the result of interrupt transfer execution. +# # +# EVENT_TYPE_PERIODIC_TIMER ## PRODUCES +# diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c b/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c new file mode 100644 index 0000000000..f5e66e4f3b --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c @@ -0,0 +1,827 @@ +/** @file + + The XHCI register operation routines. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Xhci.h" + +/** + Read 1-byte width XHCI capability register. + + @param Xhc The XHCI device. + @param Offset The offset of the 1-byte width capability register. + + @return The register content read. + @retval If err, return 0xFF. + +**/ +UINT8 +XhcReadCapReg8 ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ) +{ + UINT8 Data; + EFI_STATUS Status; + + Status = Xhc->PciIo->Mem.Read ( + Xhc->PciIo, + EfiPciIoWidthUint8, + XHC_BAR_INDEX, + (UINT64) Offset, + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcReadCapReg: Pci Io read error - %r at %d\n", Status, Offset)); + Data = 0xFF; + } + + return Data; +} + +/** + Read 4-bytes width XHCI capability register. + + @param Xhc The XHCI device. + @param Offset The offset of the 4-bytes width capability register. + + @return The register content read. + @retval If err, return 0xFFFFFFFF. + +**/ +UINT32 +XhcReadCapReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ) +{ + UINT32 Data; + EFI_STATUS Status; + + Status = Xhc->PciIo->Mem.Read ( + Xhc->PciIo, + EfiPciIoWidthUint32, + XHC_BAR_INDEX, + (UINT64) Offset, + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcReadCapReg: Pci Io read error - %r at %d\n", Status, Offset)); + Data = 0xFFFFFFFF; + } + + return Data; +} + +/** + Read 4-bytes width XHCI Operational register. + + @param Xhc The XHCI device. + @param Offset The offset of the 4-bytes width operational register. + + @return The register content read. + @retval If err, return 0xFFFFFFFF. + +**/ +UINT32 +XhcReadOpReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ) +{ + UINT32 Data; + EFI_STATUS Status; + + ASSERT (Xhc->CapLength != 0); + + Status = Xhc->PciIo->Mem.Read ( + Xhc->PciIo, + EfiPciIoWidthUint32, + XHC_BAR_INDEX, + (UINT64) (Xhc->CapLength + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcReadOpReg: Pci Io Read error - %r at %d\n", Status, Offset)); + Data = 0xFFFFFFFF; + } + + return Data; +} + +/** + Write the data to the 4-bytes width XHCI operational register. + + @param Xhc The XHCI device. + @param Offset The offset of the 4-bytes width operational register. + @param Data The data to write. + +**/ +VOID +XhcWriteOpReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ) +{ + EFI_STATUS Status; + + ASSERT (Xhc->CapLength != 0); + + Status = Xhc->PciIo->Mem.Write ( + Xhc->PciIo, + EfiPciIoWidthUint32, + XHC_BAR_INDEX, + (UINT64) (Xhc->CapLength + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcWriteOpReg: Pci Io Write error: %r at %d\n", Status, Offset)); + } +} + +/** + Write the data to the 2-bytes width XHCI operational register. + + @param Xhc The XHCI device. + @param Offset The offset of the 2-bytes width operational register. + @param Data The data to write. + +**/ +VOID +XhcWriteOpReg16 ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT16 Data + ) +{ + EFI_STATUS Status; + + ASSERT (Xhc->CapLength != 0); + + Status = Xhc->PciIo->Mem.Write ( + Xhc->PciIo, + EfiPciIoWidthUint16, + XHC_BAR_INDEX, + (UINT64) (Xhc->CapLength + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcWriteOpReg16: Pci Io Write error: %r at %d\n", Status, Offset)); + } +} + +/** + Write the data to the 8-bytes width XHCI operational register. + + @param Xhc The XHCI device. + @param Offset The offset of the 8-bytes width operational register. + @param Data The data to write. + +**/ +VOID +XhcWriteOpReg64 ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT64 Data + ) +{ + EFI_STATUS Status; + + ASSERT (Xhc->CapLength != 0); + + Status = Xhc->PciIo->Mem.Write ( + Xhc->PciIo, + EfiPciIoWidthUint64, + XHC_BAR_INDEX, + (UINT64) (Xhc->CapLength + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcWriteOpReg64: Pci Io Write error: %r at %d\n", Status, Offset)); + } +} + +/** + Read XHCI door bell register. + + @param Xhc The XHCI device. + @param Offset The offset of the door bell register. + + @return The register content read + +**/ +UINT32 +XhcReadDoorBellReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ) +{ + UINT32 Data; + EFI_STATUS Status; + + ASSERT (Xhc->DBOff != 0); + + Status = Xhc->PciIo->Mem.Read ( + Xhc->PciIo, + EfiPciIoWidthUint32, + XHC_BAR_INDEX, + (UINT64) (Xhc->DBOff + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcReadDoorBellReg: Pci Io Read error - %r at %d\n", Status, Offset)); + Data = 0xFFFFFFFF; + } + + return Data; +} + +/** + Write the data to the XHCI door bell register. + + @param Xhc The XHCI device. + @param Offset The offset of the door bell register. + @param Data The data to write. + +**/ +VOID +XhcWriteDoorBellReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ) +{ + EFI_STATUS Status; + + ASSERT (Xhc->DBOff != 0); + + Status = Xhc->PciIo->Mem.Write ( + Xhc->PciIo, + EfiPciIoWidthUint32, + XHC_BAR_INDEX, + (UINT64) (Xhc->DBOff + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcWriteOpReg: Pci Io Write error: %r at %d\n", Status, Offset)); + } +} + +/** + Read XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + + @return The register content read + +**/ +UINT32 +XhcReadRuntimeReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ) +{ + UINT32 Data; + EFI_STATUS Status; + + ASSERT (Xhc->RTSOff != 0); + + Status = Xhc->PciIo->Mem.Read ( + Xhc->PciIo, + EfiPciIoWidthUint32, + XHC_BAR_INDEX, + (UINT64) (Xhc->RTSOff + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcReadRuntimeReg: Pci Io Read error - %r at %d\n", Status, Offset)); + Data = 0xFFFFFFFF; + } + + return Data; +} + +/** + Read 8-bytes width XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the 8-bytes width runtime register. + + @return The register content read + +**/ +UINT64 +XhcReadRuntimeReg64 ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ) +{ + UINT64 Data; + EFI_STATUS Status; + + ASSERT (Xhc->RTSOff != 0); + + Status = Xhc->PciIo->Mem.Read ( + Xhc->PciIo, + EfiPciIoWidthUint64, + XHC_BAR_INDEX, + (UINT64) (Xhc->RTSOff + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcReadRuntimeReg64: Pci Io Read error - %r at %d\n", Status, Offset)); + Data = 0xFFFFFFFFFFFFFFFF; + } + + return Data; +} + +/** + Write the data to the XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Data The data to write. + +**/ +VOID +XhcWriteRuntimeReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ) +{ + EFI_STATUS Status; + + ASSERT (Xhc->RTSOff != 0); + + Status = Xhc->PciIo->Mem.Write ( + Xhc->PciIo, + EfiPciIoWidthUint32, + XHC_BAR_INDEX, + (UINT64) (Xhc->RTSOff + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcWriteRuntimeReg: Pci Io Write error: %r at %d\n", Status, Offset)); + } +} + +/** + Write the data to the 8-bytes width XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the 8-bytes width runtime register. + @param Data The data to write. + +**/ +VOID +XhcWriteRuntimeReg64 ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT64 Data + ) +{ + EFI_STATUS Status; + + ASSERT (Xhc->RTSOff != 0); + + Status = Xhc->PciIo->Mem.Write ( + Xhc->PciIo, + EfiPciIoWidthUint64, + XHC_BAR_INDEX, + (UINT64) (Xhc->RTSOff + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcWriteRuntimeReg64: Pci Io Write error: %r at %d\n", Status, Offset)); + } +} + +/** + Read XHCI extended capability register. + + @param Xhc The XHCI device. + @param Offset The offset of the extended capability register. + + @return The register content read + +**/ +UINT32 +XhcReadExtCapReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ) +{ + UINT32 Data; + EFI_STATUS Status; + + ASSERT (Xhc->ExtCapRegBase != 0); + + Status = Xhc->PciIo->Mem.Read ( + Xhc->PciIo, + EfiPciIoWidthUint32, + XHC_BAR_INDEX, + (UINT64) (Xhc->ExtCapRegBase + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcReadExtCapReg: Pci Io Read error - %r at %d\n", Status, Offset)); + Data = 0xFFFFFFFF; + } + + return Data; +} + +/** + Write the data to the XHCI extended capability register. + + @param Xhc The XHCI device. + @param Offset The offset of the extended capability register. + @param Data The data to write. + +**/ +VOID +XhcWriteExtCapReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ) +{ + EFI_STATUS Status; + + ASSERT (Xhc->ExtCapRegBase != 0); + + Status = Xhc->PciIo->Mem.Write ( + Xhc->PciIo, + EfiPciIoWidthUint32, + XHC_BAR_INDEX, + (UINT64) (Xhc->ExtCapRegBase + Offset), + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcWriteExtCapReg: Pci Io Write error: %r at %d\n", Status, Offset)); + } +} + + +/** + Set one bit of the runtime register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcSetRuntimeRegBit ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = XhcReadRuntimeReg (Xhc, Offset); + Data |= Bit; + XhcWriteRuntimeReg (Xhc, Offset, Data); +} + +/** + Clear one bit of the runtime register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcClearRuntimeRegBit ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = XhcReadRuntimeReg (Xhc, Offset); + Data &= ~Bit; + XhcWriteRuntimeReg (Xhc, Offset, Data); +} + +/** + Set one bit of the operational register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcSetOpRegBit ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = XhcReadOpReg (Xhc, Offset); + Data |= Bit; + XhcWriteOpReg (Xhc, Offset, Data); +} + + +/** + Clear one bit of the operational register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to clear. + +**/ +VOID +XhcClearOpRegBit ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = XhcReadOpReg (Xhc, Offset); + Data &= ~Bit; + XhcWriteOpReg (Xhc, Offset, Data); +} + +/** + Wait the operation register's bit as specified by Bit + to become set (or clear). + + @param Xhc The XHCI device. + @param Offset The offset of the operation register. + @param Bit The bit of the register to wait for. + @param WaitToSet Wait the bit to set or clear. + @param Timeout The time to wait before abort (in millisecond, ms). + + @retval EFI_SUCCESS The bit successfully changed by host controller. + @retval EFI_TIMEOUT The time out occurred. + +**/ +EFI_STATUS +XhcWaitOpRegBit ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit, + IN BOOLEAN WaitToSet, + IN UINT32 Timeout + ) +{ + UINT32 Index; + + for (Index = 0; Index < Timeout / XHC_SYNC_POLL_INTERVAL + 1; Index++) { + if (XHC_REG_BIT_IS_SET (Xhc, Offset, Bit) == WaitToSet) { + return EFI_SUCCESS; + } + + gBS->Stall (XHC_SYNC_POLL_INTERVAL); + } + + return EFI_TIMEOUT; +} + +/** + Set Bios Ownership + + @param Xhc The XHCI device. + +**/ +VOID +XhcSetBiosOwnership ( + IN USB_XHCI_DEV *Xhc + ) +{ + UINT32 Buffer; + + DEBUG ((EFI_D_INFO, "XhcSetBiosOwnership: called to set BIOS ownership\n")); + + Buffer = XhcReadExtCapReg (Xhc, Xhc->UsbLegSupOffset); + Buffer = ((Buffer & (~USBLEGSP_OS_SEMAPHORE)) | USBLEGSP_BIOS_SEMAPHORE); + XhcWriteExtCapReg (Xhc, Xhc->UsbLegSupOffset, Buffer); +} + +/** + Clear Bios Ownership + + @param Xhc The XHCI device. + +**/ +VOID +XhcClearBiosOwnership ( + IN USB_XHCI_DEV *Xhc + ) +{ + UINT32 Buffer; + + DEBUG ((EFI_D_INFO, "XhcClearBiosOwnership: called to clear BIOS ownership\n")); + + Buffer = XhcReadExtCapReg (Xhc, Xhc->UsbLegSupOffset); + Buffer = ((Buffer & (~USBLEGSP_BIOS_SEMAPHORE)) | USBLEGSP_OS_SEMAPHORE); + XhcWriteExtCapReg (Xhc, Xhc->UsbLegSupOffset, Buffer); +} + +/** + Calculate the XHCI legacy support capability register offset. + + @param Xhc The XHCI device. + + @return The offset of XHCI legacy support capability register. + +**/ +UINT32 +XhcGetLegSupCapAddr ( + IN USB_XHCI_DEV *Xhc + ) +{ + UINT32 ExtCapOffset; + UINT8 NextExtCapReg; + UINT32 Data; + + ExtCapOffset = 0; + + do { + // + // Check if the extended capability register's capability id is USB Legacy Support. + // + Data = XhcReadExtCapReg (Xhc, ExtCapOffset); + if ((Data & 0xFF) == 0x1) { + return ExtCapOffset; + } + // + // If not, then traverse all of the ext capability registers till finding out it. + // + NextExtCapReg = (Data >> 8) & 0xFF; + ExtCapOffset += (NextExtCapReg << 2); + } while (NextExtCapReg != 0); + + return 0; +} + +/** + Whether the XHCI host controller is halted. + + @param Xhc The XHCI device. + + @retval TRUE The controller is halted. + @retval FALSE It isn't halted. + +**/ +BOOLEAN +XhcIsHalt ( + IN USB_XHCI_DEV *Xhc + ) +{ + return XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT); +} + + +/** + Whether system error occurred. + + @param Xhc The XHCI device. + + @retval TRUE System error happened. + @retval FALSE No system error. + +**/ +BOOLEAN +XhcIsSysError ( + IN USB_XHCI_DEV *Xhc + ) +{ + return XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HSE); +} + +/** + Reset the XHCI host controller. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort (in millisecond, ms). + + @retval EFI_SUCCESS The XHCI host controller is reset. + @return Others Failed to reset the XHCI before Timeout. + +**/ +EFI_STATUS +XhcResetHC ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + + DEBUG ((EFI_D_INFO, "XhcResetHC!\n")); + // + // Host can only be reset when it is halt. If not so, halt it + // + if (!XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT)) { + Status = XhcHaltHC (Xhc, Timeout); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + XhcSetOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RESET); + Status = XhcWaitOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RESET, FALSE, Timeout); + return Status; +} + + +/** + Halt the XHCI host controller. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort (in millisecond, ms). + + @return EFI_SUCCESS The XHCI host controller is halt. + @return EFI_TIMEOUT Failed to halt the XHCI before Timeout. + +**/ +EFI_STATUS +XhcHaltHC ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + + XhcClearOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RUN); + Status = XhcWaitOpRegBit (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT, TRUE, Timeout); + return Status; +} + + +/** + Set the XHCI host controller to run. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort (in millisecond, ms). + + @return EFI_SUCCESS The XHCI host controller is running. + @return EFI_TIMEOUT Failed to set the XHCI to run before Timeout. + +**/ +EFI_STATUS +XhcRunHC ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + + XhcSetOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RUN); + Status = XhcWaitOpRegBit (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT, FALSE, Timeout); + return Status; +} + diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h b/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h new file mode 100644 index 0000000000..d004bf989a --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h @@ -0,0 +1,569 @@ +/** @file + + This file contains the register definition of XHCI host controller. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_XHCI_REG_H_ +#define _EFI_XHCI_REG_H_ + +#define PCI_IF_XHCI 0x30 + +// +// PCI Configuration Registers +// +#define XHC_BAR_INDEX 0x00 + +#define XHC_PCI_BAR_OFFSET 0x10 // Memory Bar Register Offset +#define XHC_PCI_BAR_MASK 0xFFFF // Memory Base Address Mask + +#define USB_HUB_CLASS_CODE 0x09 +#define USB_HUB_SUBCLASS_CODE 0x00 + +//============================================// +// XHCI register offset // +//============================================// + +// +// Capability registers offset +// +#define XHC_CAPLENGTH_OFFSET 0x00 // Capability register length offset +#define XHC_HCIVERSION_OFFSET 0x02 // Interface Version Number 02-03h +#define XHC_HCSPARAMS1_OFFSET 0x04 // Structural Parameters 1 +#define XHC_HCSPARAMS2_OFFSET 0x08 // Structural Parameters 2 +#define XHC_HCSPARAMS3_OFFSET 0x0c // Structural Parameters 3 +#define XHC_HCCPARAMS_OFFSET 0x10 // Capability Parameters +#define XHC_DBOFF_OFFSET 0x14 // Doorbell Offset +#define XHC_RTSOFF_OFFSET 0x18 // Runtime Register Space Offset + +// +// Operational registers offset +// +#define XHC_USBCMD_OFFSET 0x0000 // USB Command Register Offset +#define XHC_USBSTS_OFFSET 0x0004 // USB Status Register Offset +#define XHC_PAGESIZE_OFFSET 0x0008 // USB Page Size Register Offset +#define XHC_DNCTRL_OFFSET 0x0014 // Device Notification Control Register Offset +#define XHC_CRCR_OFFSET 0x0018 // Command Ring Control Register Offset +#define XHC_DCBAAP_OFFSET 0x0030 // Device Context Base Address Array Pointer Register Offset +#define XHC_CONFIG_OFFSET 0x0038 // Configure Register Offset +#define XHC_PORTSC_OFFSET 0x0400 // Port Status and Control Register Offset + +// +// Runtime registers offset +// +#define XHC_MFINDEX_OFFSET 0x00 // Microframe Index Register Offset +#define XHC_IMAN_OFFSET 0x20 // Interrupter X Management Register Offset +#define XHC_IMOD_OFFSET 0x24 // Interrupter X Moderation Register Offset +#define XHC_ERSTSZ_OFFSET 0x28 // Event Ring Segment Table Size Register Offset +#define XHC_ERSTBA_OFFSET 0x30 // Event Ring Segment Table Base Address Register Offset +#define XHC_ERDP_OFFSET 0x38 // Event Ring Dequeue Pointer Register Offset + +#define USBLEGSP_BIOS_SEMAPHORE BIT16 // HC BIOS Owned Semaphore +#define USBLEGSP_OS_SEMAPHORE BIT24 // HC OS Owned Semaphore + +#pragma pack (1) +// +// Structural Parameters 1 Register Bitmap Definition +// +typedef union _XHC_HCSPARAMS1 { + UINT32 Dword; + struct { + UINT8 MaxSlots; // Number of Device Slots + UINT16 MaxIntrs:11; // Number of Interrupters + UINT16 Rsvd:5; + UINT8 MaxPorts; // Number of Ports + } Data; +} XHC_HCSPARAMS1; + +// +// Structural Parameters 2 Register Bitmap Definition +// +typedef union _XHC_HCSPARAMS2 { + UINT32 Dword; + struct { + UINT32 Ist:4; // Isochronous Scheduling Threshold + UINT32 Erst:4; // Event Ring Segment Table Max + UINT32 Rsvd:13; + UINT32 ScratchBufHi:5; // Max Scratchpad Buffers Hi + UINT32 Spr:1; // Scratchpad Restore + UINT32 ScratchBufLo:5; // Max Scratchpad Buffers Lo + } Data; +} XHC_HCSPARAMS2; + +// +// Capability Parameters Register Bitmap Definition +// +typedef union _XHC_HCCPARAMS { + UINT32 Dword; + struct { + UINT16 Ac64:1; // 64-bit Addressing Capability + UINT16 Bnc:1; // BW Negotiation Capability + UINT16 Csz:1; // Context Size + UINT16 Ppc:1; // Port Power Control + UINT16 Pind:1; // Port Indicators + UINT16 Lhrc:1; // Light HC Reset Capability + UINT16 Ltc:1; // Latency Tolerance Messaging Capability + UINT16 Nss:1; // No Secondary SID Support + UINT16 Pae:1; // Parse All Event Data + UINT16 Rsvd:3; + UINT16 MaxPsaSize:4; // Maximum Primary Stream Array Size + UINT16 ExtCapReg; // xHCI Extended Capabilities Pointer + } Data; +} XHC_HCCPARAMS; + +#pragma pack () + +// +// Register Bit Definition +// +#define XHC_USBCMD_RUN BIT0 // Run/Stop +#define XHC_USBCMD_RESET BIT1 // Host Controller Reset +#define XHC_USBCMD_INTE BIT2 // Interrupter Enable +#define XHC_USBCMD_HSEE BIT3 // Host System Error Enable + +#define XHC_USBSTS_HALT BIT0 // Host Controller Halted +#define XHC_USBSTS_HSE BIT2 // Host System Error +#define XHC_USBSTS_EINT BIT3 // Event Interrupt +#define XHC_USBSTS_PCD BIT4 // Port Change Detect +#define XHC_USBSTS_SSS BIT8 // Save State Status +#define XHC_USBSTS_RSS BIT9 // Restore State Status +#define XHC_USBSTS_SRE BIT10 // Save/Restore Error +#define XHC_USBSTS_CNR BIT11 // Host Controller Not Ready +#define XHC_USBSTS_HCE BIT12 // Host Controller Error + +#define XHC_PAGESIZE_MASK 0xFFFF // Page Size + +#define XHC_CRCR_RCS BIT0 // Ring Cycle State +#define XHC_CRCR_CS BIT1 // Command Stop +#define XHC_CRCR_CA BIT2 // Command Abort +#define XHC_CRCR_CRR BIT3 // Command Ring Running + +#define XHC_CONFIG_MASK 0xFF // Command Ring Running + +#define XHC_PORTSC_CCS BIT0 // Current Connect Status +#define XHC_PORTSC_PED BIT1 // Port Enabled/Disabled +#define XHC_PORTSC_OCA BIT3 // Over-current Active +#define XHC_PORTSC_RESET BIT4 // Port Reset +#define XHC_PORTSC_PLS (BIT5|BIT6|BIT7|BIT8) // Port Link State +#define XHC_PORTSC_PP BIT9 // Port Power +#define XHC_PORTSC_PS (BIT10|BIT11|BIT12|BIT13) // Port Speed +#define XHC_PORTSC_LWS BIT16 // Port Link State Write Strobe +#define XHC_PORTSC_CSC BIT17 // Connect Status Change +#define XHC_PORTSC_PEC BIT18 // Port Enabled/Disabled Change +#define XHC_PORTSC_WRC BIT19 // Warm Port Reset Change +#define XHC_PORTSC_OCC BIT20 // Over-Current Change +#define XHC_PORTSC_PRC BIT21 // Port Reset Change +#define XHC_PORTSC_PLC BIT22 // Port Link State Change +#define XHC_PORTSC_CEC BIT23 // Port Config Error Change +#define XHC_PORTSC_CAS BIT24 // Cold Attach Status + +#define XHC_IMAN_IP BIT0 // Interrupt Pending +#define XHC_IMAN_IE BIT1 // Interrupt Enable + +#define XHC_IMODI_MASK 0x0000FFFF // Interrupt Moderation Interval +#define XHC_IMODC_MASK 0xFFFF0000 // Interrupt Moderation Counter + +// +// Structure to map the hardware port states to the +// UEFI's port states. +// +typedef struct { + UINT32 HwState; + UINT16 UefiState; +} USB_PORT_STATE_MAP; + +/** + Read 1-byte width XHCI capability register. + + @param Xhc The XHCI device. + @param Offset The offset of the 1-byte width capability register. + + @return The register content read. + @retval If err, return 0xFFFF. + +**/ +UINT8 +XhcReadCapReg8 ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ); + +/** + Read 4-bytes width XHCI capability register. + + @param Xhc The XHCI device. + @param Offset The offset of the 4-bytes width capability register. + + @return The register content read. + @retval If err, return 0xFFFFFFFF. + +**/ +UINT32 +XhcReadCapReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ); + +/** + Read 4-bytes width XHCI Operational register. + + @param Xhc The XHCI device. + @param Offset The offset of the 4-bytes width operational register. + + @return The register content read. + @retval If err, return 0xFFFFFFFF. + +**/ +UINT32 +XhcReadOpReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ); + +/** + Write the data to the 4-bytes width XHCI operational register. + + @param Xhc The XHCI device. + @param Offset The offset of the 4-bytes width operational register. + @param Data The data to write. + +**/ +VOID +XhcWriteOpReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ); + +/** + Write the data to the 2-bytes width XHCI operational register. + + @param Xhc The XHCI device. + @param Offset The offset of the 2-bytes width operational register. + @param Data The data to write. + +**/ +VOID +XhcWriteOpReg16 ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT16 Data + ); + +/** + Write the data to the 8-bytes width XHCI operational register. + + @param Xhc The XHCI device. + @param Offset The offset of the 8-bytes width operational register. + @param Data The data to write. + +**/ +VOID +XhcWriteOpReg64 ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT64 Data + ); + +/** + Read XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + + @return The register content read + +**/ +UINT32 +XhcReadRuntimeReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ); + +/** + Read 8-bytes width XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the 8-bytes width runtime register. + + @return The register content read + +**/ +UINT64 +XhcReadRuntimeReg64 ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ); + +/** + Write the data to the XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Data The data to write. + +**/ +VOID +XhcWriteRuntimeReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ); + +/** + Write the data to the 8-bytes width XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the 8-bytes width runtime register. + @param Data The data to write. + +**/ +VOID +XhcWriteRuntimeReg64 ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT64 Data + ); + +/** + Read XHCI door bell register. + + @param Xhc The XHCI device. + @param Offset The offset of the door bell register. + + @return The register content read + +**/ +UINT32 +XhcReadDoorBellReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ); + +/** + Write the data to the XHCI door bell register. + + @param Xhc The XHCI device. + @param Offset The offset of the door bell register. + @param Data The data to write. + +**/ +VOID +XhcWriteDoorBellReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ); + +/** + Set one bit of the operational register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcSetOpRegBit ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ); + +/** + Clear one bit of the operational register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to clear. + +**/ +VOID +XhcClearOpRegBit ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ); + +/** + Wait the operation register's bit as specified by Bit + to be set (or clear). + + @param Xhc The XHCI device. + @param Offset The offset of the operational register. + @param Bit The bit of the register to wait for. + @param WaitToSet Wait the bit to set or clear. + @param Timeout The time to wait before abort (in millisecond, ms). + + @retval EFI_SUCCESS The bit successfully changed by host controller. + @retval EFI_TIMEOUT The time out occurred. + +**/ +EFI_STATUS +XhcWaitOpRegBit ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit, + IN BOOLEAN WaitToSet, + IN UINT32 Timeout + ); + +/** + Read XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + + @return The register content read + +**/ +UINT32 +XhcReadRuntimeReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset + ); + +/** + Write the data to the XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Data The data to write. + +**/ +VOID +XhcWriteRuntimeReg ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ); + +/** + Set one bit of the runtime register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcSetRuntimeRegBit ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ); + +/** + Clear one bit of the runtime register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcClearRuntimeRegBit ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ); + +/** + Whether the XHCI host controller is halted. + + @param Xhc The XHCI device. + + @retval TRUE The controller is halted. + @retval FALSE It isn't halted. + +**/ +BOOLEAN +XhcIsHalt ( + IN USB_XHCI_DEV *Xhc + ); + +/** + Whether system error occurred. + + @param Xhc The XHCI device. + + @retval TRUE System error happened. + @retval FALSE No system error. + +**/ +BOOLEAN +XhcIsSysError ( + IN USB_XHCI_DEV *Xhc + ); + +/** + Reset the XHCI host controller. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort (in millisecond, ms). + + @retval EFI_SUCCESS The XHCI host controller is reset. + @return Others Failed to reset the XHCI before Timeout. + +**/ +EFI_STATUS +XhcResetHC ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Timeout + ); + +/** + Halt the XHCI host controller. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort (in millisecond, ms). + + @return EFI_SUCCESS The XHCI host controller is halt. + @return EFI_TIMEOUT Failed to halt the XHCI before Timeout. + +**/ +EFI_STATUS +XhcHaltHC ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Timeout + ); + +/** + Set the XHCI host controller to run. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort (in millisecond, ms). + + @return EFI_SUCCESS The XHCI host controller is running. + @return EFI_TIMEOUT Failed to set the XHCI to run before Timeout. + +**/ +EFI_STATUS +XhcRunHC ( + IN USB_XHCI_DEV *Xhc, + IN UINT32 Timeout + ); + +/** + Calculate the XHCI legacy support capability register offset. + + @param Xhc The XHCI device. + + @return The offset of XHCI legacy support capability register. + +**/ +UINT32 +XhcGetLegSupCapAddr ( + IN USB_XHCI_DEV *Xhc + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c new file mode 100644 index 0000000000..f160a1d2ec --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c @@ -0,0 +1,2383 @@ +/** @file + + XHCI transfer scheduling routines. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Xhci.h" + +/** + Allocates a buffer of a certain pool type at a specified alignment. + + Allocates the number bytes specified by AllocationSize of a certain pool type with an alignment + specified by Alignment. The allocated buffer is returned. If AllocationSize is 0, then a valid + buffer of 0 size is returned. If there is not enough memory at the specified alignment remaining + to satisfy the request, then NULL is returned. + If Alignment is not a power of two and Alignment is not zero, then ASSERT(). + + @param PoolType The type of pool to allocate. + @param AllocationSize The number of bytes to allocate. + @param Alignment The requested alignment of the allocation. Must be a power of two. + If Alignment is zero, then byte alignment is used. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +InternalAllocateAlignedPool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN AllocationSize, + IN UINTN Alignment + ) +{ + VOID *RawAddress; + UINTN AlignedAddress; + UINTN AlignmentMask; + UINTN OverAllocationSize; + UINTN RealAllocationSize; + VOID **FreePointer; + + // + // Alignment must be a power of two or zero. + // + ASSERT ((Alignment & (Alignment - 1)) == 0); + + if (Alignment == 0) { + AlignmentMask = Alignment; + } else { + AlignmentMask = Alignment - 1; + } + // + // Calculate the extra memory size, over-allocate memory pool and get the aligned memory address. + // + OverAllocationSize = sizeof (RawAddress) + AlignmentMask; + RealAllocationSize = AllocationSize + OverAllocationSize; + // + // Make sure that AllocationSize plus OverAllocationSize does not overflow. + // + ASSERT (RealAllocationSize > AllocationSize); + + RawAddress = NULL; + gBS->AllocatePool (PoolType, RealAllocationSize, &RawAddress); + if (RawAddress == NULL) { + return NULL; + } + AlignedAddress = ((UINTN) RawAddress + OverAllocationSize) & ~AlignmentMask; + // + // Save the original memory address just before the aligned address. + // + FreePointer = (VOID **)(AlignedAddress - sizeof (RawAddress)); + *FreePointer = RawAddress; + + return (VOID *) AlignedAddress; +} + +/** + Allocates and zeros a buffer of a certain pool type at a specified alignment. + + Allocates the number bytes specified by AllocationSize of a certain pool type with an alignment + specified by Alignment, clears the buffer with zeros, and returns a pointer to the allocated + buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there is not + enough memory at the specified alignment remaining to satisfy the request, then NULL is returned. + If Alignment is not a power of two and Alignment is not zero, then ASSERT(). + + @param PoolType The type of pool to allocate. + @param AllocationSize The number of bytes to allocate. + @param Alignment The requested alignment of the allocation. Must be a power of two. + If Alignment is zero, then byte alignment is used. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +InternalAllocateAlignedZeroPool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN AllocationSize, + IN UINTN Alignment + ) +{ + VOID *Memory; + Memory = InternalAllocateAlignedPool (PoolType, AllocationSize, Alignment); + if (Memory != NULL) { + ZeroMem (Memory, AllocationSize); + } + return Memory; +} + +/** + Allocates and zeros a buffer of type EfiBootServicesData at a specified alignment. + + Allocates the number bytes specified by AllocationSize of type EfiBootServicesData with an + alignment specified by Alignment, clears the buffer with zeros, and returns a pointer to the + allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there + is not enough memory at the specified alignment remaining to satisfy the request, then NULL is + returned. + If Alignment is not a power of two and Alignment is not zero, then ASSERT(). + + @param AllocationSize The number of bytes to allocate. + @param Alignment The requested alignment of the allocation. Must be a power of two. + If Alignment is zero, then byte alignment is used. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateAlignedZeroPool ( + IN UINTN AllocationSize, + IN UINTN Alignment + ) +{ + return InternalAllocateAlignedZeroPool (EfiBootServicesData, AllocationSize, Alignment); +} + +/** + Frees a buffer that was previously allocated with one of the aligned pool allocation functions + in the Memory Allocation Library. + + Frees the buffer specified by Buffer. Buffer must have been allocated on a previous call to the + aligned pool allocation services of the Memory Allocation Library. + If Buffer was not allocated with an aligned pool allocation function in the Memory Allocation + Library, then ASSERT(). + + @param Buffer Pointer to the buffer to free. + +**/ +VOID +EFIAPI +FreeAlignedPool ( + IN VOID *Buffer + ) +{ + VOID *RawAddress; + VOID **FreePointer; + EFI_STATUS Status; + + // + // Get the pre-saved original address in the over-allocate pool. + // + FreePointer = (VOID **)((UINTN) Buffer - sizeof (RawAddress)); + RawAddress = *FreePointer; + + Status = gBS->FreePool (RawAddress); + ASSERT_EFI_ERROR (Status); +} + +/** + Create a command transfer TRB to support XHCI command interfaces. + + @param Xhc The XHCI device. + @param CmdTrb The cmd TRB to be executed. + + @return Created URB or NULL. + +**/ +URB* +XhcCreateCmdTrb ( + IN USB_XHCI_DEV *Xhc, + IN TRB *CmdTrb + ) +{ + URB *Urb; + + Urb = AllocateZeroPool (sizeof (URB)); + if (Urb == NULL) { + return NULL; + } + + Urb->Signature = XHC_URB_SIG; + + Urb->Ring = &Xhc->CmdRing; + XhcSyncTrsRing (Xhc, Urb->Ring); + Urb->TrbNum = 1; + Urb->TrbStart = Urb->Ring->RingEnqueue; + CopyMem (Urb->TrbStart, CmdTrb, sizeof (TRB)); + Urb->TrbStart->CycleBit = Urb->Ring->RingPCS & BIT0; + Urb->TrbEnd = Urb->TrbStart; + + Urb->EvtRing = &Xhc->CmdEventRing; + XhcSyncEventRing (Xhc, Urb->EvtRing); + Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue; + + return Urb; +} + +/** + Execute a XHCI cmd TRB pointed by CmdTrb. + + @param Xhc The XHCI device. + @param CmdTrb The cmd TRB to be executed. + @param TimeOut Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + @param EvtTrb The event TRB corresponding to the cmd TRB. + + @retval EFI_SUCCESS The transfer was completed successfully. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_TIMEOUT The transfer failed due to timeout. + @retval EFI_DEVICE_ERROR The transfer failed due to host controller error. + +**/ +EFI_STATUS +EFIAPI +XhcCmdTransfer ( + IN USB_XHCI_DEV *Xhc, + IN TRB *CmdTrb, + IN UINTN TimeOut, + OUT TRB **EvtTrb + ) +{ + EFI_STATUS Status; + URB *Urb; + + // + // Validate the parameters + // + if ((Xhc == NULL) || (CmdTrb == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_DEVICE_ERROR; + + if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) { + DEBUG ((EFI_D_ERROR, "XhcCmdTransfer: HC is halted\n")); + goto ON_EXIT; + } + + // + // Create a new URB, then poll the execution status. + // + Urb = XhcCreateCmdTrb (Xhc, CmdTrb); + + if (Urb == NULL) { + DEBUG ((EFI_D_ERROR, "XhcCmdTransfer: failed to create URB\n")); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + ASSERT (Urb->EvtRing == &Xhc->CmdEventRing); + + Status = XhcExecTransfer (Xhc, TRUE, Urb, TimeOut); + *EvtTrb = Urb->EvtTrbStart; + + if (Urb->Result == EFI_USB_NOERROR) { + Status = EFI_SUCCESS; + } + + FreePool (Urb); + +ON_EXIT: + return Status; +} + +/** + Create a new URB for a new transaction. + + @param Xhc The XHCI device + @param DevAddr The device address + @param EpAddr Endpoint addrress + @param DevSpeed The device speed + @param MaxPacket The max packet length of the endpoint + @param Type The transaction type + @param Request The standard USB request for control transfer + @param Data The user data to transfer + @param DataLen The length of data buffer + @param Callback The function to call when data is transferred + @param Context The context to the callback + + @return Created URB or NULL + +**/ +URB* +XhcCreateUrb ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN UINTN Type, + IN EFI_USB_DEVICE_REQUEST *Request, + IN VOID *Data, + IN UINTN DataLen, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context + ) +{ + USB_ENDPOINT *Ep; + EFI_STATUS Status; + URB *Urb; + + Urb = AllocateZeroPool (sizeof (URB)); + if (Urb == NULL) { + return NULL; + } + + Urb->Signature = XHC_URB_SIG; + InitializeListHead (&Urb->UrbList); + + Ep = &Urb->Ep; + Ep->DevAddr = DevAddr; + Ep->EpAddr = EpAddr & 0x0F; + Ep->Direction = ((EpAddr & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut; + Ep->DevSpeed = DevSpeed; + Ep->MaxPacket = MaxPacket; + Ep->Type = Type; + + Urb->Request = Request; + Urb->Data = Data; + Urb->DataLen = DataLen; + Urb->Callback = Callback; + Urb->Context = Context; + + Status = XhcCreateTransferTrb (Xhc, Urb); + + return Urb; +} + +/** + Create a transfer TRB. + + @param Xhc The XHCI device + @param Urb The urb used to construct the transfer TRB. + + @return Created TRB or NULL + +**/ +EFI_STATUS +EFIAPI +XhcCreateTransferTrb ( + IN USB_XHCI_DEV *Xhc, + IN URB *Urb + ) +{ + DEVICE_CONTEXT *OutputDevContxt; + TRANSFER_RING *EPRing; + UINT8 EPType; + UINT8 SlotId; + UINT8 Dci; + TRB *TrbStart; + UINTN TotalLen; + UINTN Len; + UINTN TrbNum; + + SlotId = XhcDevAddrToSlotId(Urb->Ep.DevAddr); + Dci = XhcEndpointToDci (Urb->Ep.EpAddr, Urb->Ep.Direction); + EPRing = (TRANSFER_RING *)(UINTN) UsbDevContext[SlotId].EndpointTransferRing[Dci-1]; + Urb->Ring = EPRing; + OutputDevContxt = (DEVICE_CONTEXT *)(UINTN) Xhc->DCBAA[SlotId]; + EPType = (UINT8) OutputDevContxt->EP[Dci-1].EPType; + + // + // Construct the TRB + // + XhcSyncTrsRing (Xhc, EPRing); + Urb->TrbStart = EPRing->RingEnqueue; + switch (EPType) { + case ED_CONTROL_BIDIR: + Urb->EvtRing = &Xhc->CtrlTrEventRing; + XhcSyncEventRing (Xhc, Urb->EvtRing); + Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue; + // + // For control transfer, create SETUP_STAGE_TRB first. + // + TrbStart = EPRing->RingEnqueue; + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->bmRequestType = Urb->Request->RequestType; + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->bRequest = Urb->Request->Request; + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->wValue = Urb->Request->Value; + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->wIndex = Urb->Request->Index; + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->wLength = Urb->Request->Length; + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->Lenth = 8; + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->IntTarget = Urb->EvtRing->EventInterrupter; + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->IOC = 1; + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->IDT = 1; + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->Type = TRB_TYPE_SETUP_STAGE; + if (Urb->Ep.Direction == EfiUsbDataIn) { + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->TRT = 3; + } else if (Urb->Ep.Direction == EfiUsbDataOut) { + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->TRT = 2; + } else { + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->TRT = 0; + } + // + // Update the cycle bit + // + ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->CycleBit = EPRing->RingPCS & BIT0; + Urb->TrbNum++; + + // + // For control transfer, create DATA_STAGE_TRB. + // + if (Urb->DataLen > 0) { + XhcSyncTrsRing (Xhc, EPRing); + TrbStart = EPRing->RingEnqueue; + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->TRBPtrLo = XHC_LOW_32BIT(Urb->Data); + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->TRBPtrHi = XHC_HIGH_32BIT(Urb->Data); + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->Lenth = (UINT32) Urb->DataLen; + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->TDSize = 0; + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->IntTarget = Urb->EvtRing->EventInterrupter; + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->ISP = 1; + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->IOC = 1; + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->IDT = 0; + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->CH = 0; + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->Type = TRB_TYPE_DATA_STAGE; + if (Urb->Ep.Direction == EfiUsbDataIn) { + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->DIR = 1; + } else if (Urb->Ep.Direction == EfiUsbDataOut) { + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->DIR = 0; + } else { + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->DIR = 0; + } + // + // Update the cycle bit + // + ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->CycleBit = EPRing->RingPCS & BIT0; + Urb->TrbNum++; + } + // + // For control transfer, create STATUS_STAGE_TRB. + // Get the pointer to next TRB for status stage use + // + XhcSyncTrsRing (Xhc, EPRing); + TrbStart = EPRing->RingEnqueue; + ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->IntTarget = Urb->EvtRing->EventInterrupter; + ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->IOC = 1; + ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->CH = 0; + ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->Type = TRB_TYPE_STATUS_STAGE; + if (Urb->Ep.Direction == EfiUsbDataIn) { + ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->DIR = 0; + } else if (Urb->Ep.Direction == EfiUsbDataOut) { + ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->DIR = 1; + } else { + ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->DIR = 0; + } + // + // Update the cycle bit + // + ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->CycleBit = EPRing->RingPCS & BIT0; + // + // Update the enqueue pointer + // + XhcSyncTrsRing (Xhc, EPRing); + Urb->TrbNum++; + Urb->TrbEnd = TrbStart; + + break; + + case ED_BULK_OUT: + case ED_BULK_IN: + Urb->EvtRing = &Xhc->BulkTrEventRing; + XhcSyncEventRing (Xhc, Urb->EvtRing); + Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue; + + TotalLen = 0; + Len = 0; + TrbNum = 0; + TrbStart = EPRing->RingEnqueue; + while (TotalLen < Urb->DataLen) { + if ((TotalLen + 0x10000) >= Urb->DataLen) { + Len = Urb->DataLen - TotalLen; + } else { + Len = 0x10000; + } + TrbStart = EPRing->RingEnqueue; + ((TRANSFER_TRB_NORMAL *) TrbStart)->TRBPtrLo = XHC_LOW_32BIT((UINT8 *) Urb->Data + TotalLen); + ((TRANSFER_TRB_NORMAL *) TrbStart)->TRBPtrHi = XHC_HIGH_32BIT((UINT8 *) Urb->Data + TotalLen); + ((TRANSFER_TRB_NORMAL *) TrbStart)->Lenth = (UINT32) Len; + ((TRANSFER_TRB_NORMAL *) TrbStart)->TDSize = 0; + ((TRANSFER_TRB_NORMAL *) TrbStart)->IntTarget = Urb->EvtRing->EventInterrupter; + ((TRANSFER_TRB_NORMAL *) TrbStart)->ISP = 1; + ((TRANSFER_TRB_NORMAL *) TrbStart)->IOC = 1; + ((TRANSFER_TRB_NORMAL *) TrbStart)->Type = TRB_TYPE_NORMAL; + // + // Update the cycle bit + // + ((TRANSFER_TRB_NORMAL *) TrbStart)->CycleBit = EPRing->RingPCS & BIT0; + + XhcSyncTrsRing (Xhc, EPRing); + TrbNum++; + TotalLen += Len; + } + + Urb->TrbNum = TrbNum; + Urb->TrbEnd = TrbStart; + break; + + case ED_INTERRUPT_OUT: + case ED_INTERRUPT_IN: + if (Urb->Ep.Type == XHC_INT_TRANSFER_ASYNC) { + Urb->EvtRing = &Xhc->AsynIntTrEventRing; + } else if(Urb->Ep.Type == XHC_INT_TRANSFER_SYNC){ + Urb->EvtRing = &Xhc->IntTrEventRing; + } else { + DEBUG ((EFI_D_ERROR, "EP Interrupt type error!\n")); + ASSERT(FALSE); + } + XhcSyncEventRing (Xhc, Urb->EvtRing); + Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue; + + TotalLen = 0; + Len = 0; + TrbNum = 0; + TrbStart = EPRing->RingEnqueue; + while (TotalLen < Urb->DataLen) { + if ((TotalLen + 0x10000) >= Urb->DataLen) { + Len = Urb->DataLen - TotalLen; + } else { + Len = 0x10000; + } + TrbStart = EPRing->RingEnqueue; + ((TRANSFER_TRB_NORMAL *) TrbStart)->TRBPtrLo = XHC_LOW_32BIT((UINT8 *) Urb->Data + TotalLen); + ((TRANSFER_TRB_NORMAL *) TrbStart)->TRBPtrHi = XHC_HIGH_32BIT((UINT8 *) Urb->Data + TotalLen); + ((TRANSFER_TRB_NORMAL *) TrbStart)->Lenth = (UINT32) Len; + ((TRANSFER_TRB_NORMAL *) TrbStart)->TDSize = 0; + ((TRANSFER_TRB_NORMAL *) TrbStart)->IntTarget = Urb->EvtRing->EventInterrupter; + ((TRANSFER_TRB_NORMAL *) TrbStart)->ISP = 1; + ((TRANSFER_TRB_NORMAL *) TrbStart)->IOC = 1; + ((TRANSFER_TRB_NORMAL *) TrbStart)->Type = TRB_TYPE_NORMAL; + // + // Update the cycle bit + // + ((TRANSFER_TRB_NORMAL *) TrbStart)->CycleBit = EPRing->RingPCS & BIT0; + + XhcSyncTrsRing (Xhc, EPRing); + TrbNum++; + TotalLen += Len; + } + + Urb->TrbNum = TrbNum; + Urb->TrbEnd = TrbStart; + break; + + default: + DEBUG ((EFI_D_INFO, "Not supported EPType 0x%x!\n",EPType)); + ASSERT (FALSE); + break; + } + + return EFI_SUCCESS; +} + + +/** + Initialize the XHCI host controller for schedule. + + @param Xhc The XHCI device to be initialized. + +**/ +VOID +XhcInitSched ( + IN USB_XHCI_DEV *Xhc + ) +{ + VOID *Dcbaa; + UINT64 CmdRing; + UINTN Entries; + UINT32 MaxScratchpadBufs; + UINT64 *ScratchBuf; + UINT64 *ScratchEntryBuf; + UINT32 Index; + + // + // Program the Max Device Slots Enabled (MaxSlotsEn) field in the CONFIG register (5.4.7) + // to enable the device slots that system software is going to use. + // + Xhc->MaxSlotsEn = Xhc->HcSParams1.Data.MaxSlots; + ASSERT (Xhc->MaxSlotsEn >= 1 && Xhc->MaxSlotsEn <= 255); + XhcWriteOpReg (Xhc, XHC_CONFIG_OFFSET, Xhc->MaxSlotsEn); + + // + // The Device Context Base Address Array entry associated with each allocated Device Slot + // shall contain a 64-bit pointer to the base of the associated Device Context. + // The Device Context Base Address Array shall contain MaxSlotsEn + 1 entries. + // Software shall set Device Context Base Address Array entries for unallocated Device Slots to '0'. + // + Entries = (Xhc->MaxSlotsEn + 1) * sizeof(UINT64); + Dcbaa = AllocateAlignedZeroPool(Entries, 64); + ASSERT (Dcbaa != NULL); + + // + // A Scratchpad Buffer is a PAGESIZE block of system memory located on a PAGESIZE boundary. + // System software shall allocate the Scratchpad Buffer(s) before placing the xHC in to Run + // mode (Run/Stop(R/S) ='1'). + // + MaxScratchpadBufs = ((Xhc->HcSParams2.Data.ScratchBufHi) << 5) | (Xhc->HcSParams2.Data.ScratchBufLo); + Xhc->MaxScratchpadBufs = MaxScratchpadBufs; + ASSERT (MaxScratchpadBufs >= 0 && MaxScratchpadBufs <= 1023); + if (MaxScratchpadBufs != 0) { + ScratchBuf = AllocateAlignedZeroPool(MaxScratchpadBufs * sizeof (UINT64), Xhc->PageSize); + ASSERT (ScratchBuf != NULL); + Xhc->ScratchBuf = ScratchBuf; + + for (Index = 0; Index < MaxScratchpadBufs; Index++) { + ScratchEntryBuf = AllocateAlignedZeroPool(Xhc->PageSize, Xhc->PageSize); + *ScratchBuf++ = (UINT64)(UINTN)ScratchEntryBuf; + } + + // + // The Scratchpad Buffer Array contains pointers to the Scratchpad Buffers. Entry 0 of the + // Device Context Base Address Array points to the Scratchpad Buffer Array. + // + *(UINT64 *)Dcbaa = (UINT64)(UINTN)Xhc->ScratchBuf; + } + + // + // Program the Device Context Base Address Array Pointer (DCBAAP) register (5.4.6) with + // a 64-bit address pointing to where the Device Context Base Address Array is located. + // + Xhc->DCBAA = (UINT64 *)(UINTN)Dcbaa; + XhcWriteOpReg64 (Xhc, XHC_DCBAAP_OFFSET, (UINT64)Xhc->DCBAA); + DEBUG ((EFI_D_INFO, "XhcInitSched:DCBAA=0x%x\n", (UINT64)Xhc->DCBAA)); + + // + // Define the Command Ring Dequeue Pointer by programming the Command Ring Control Register + // (5.4.5) with a 64-bit address pointing to the starting address of the first TRB of the Command Ring. + // Note: The Command Ring is 64 byte aligned, so the low order 6 bits of the Command Ring Pointer shall + // always be '0'. + // + CreateTransferRing (Xhc, CMD_RING_TRB_NUMBER, &Xhc->CmdRing); + // + // The xHC uses the Enqueue Pointer to determine when a Transfer Ring is empty. As it fetches TRBs from a + // Transfer Ring it checks for a Cycle bit transition. If a transition detected, the ring is empty. + // So we set RCS as inverted PCS init value to let Command Ring empty + // + CmdRing = (UINT64)(UINTN)Xhc->CmdRing.RingSeg0; + ASSERT ((CmdRing & 0x3F) == 0); + CmdRing |= XHC_CRCR_RCS; + XhcWriteOpReg64 (Xhc, XHC_CRCR_OFFSET, CmdRing); + + DEBUG ((EFI_D_INFO, "XhcInitSched:XHC_CRCR=0x%x\n", Xhc->CmdRing.RingSeg0)); + + // + // Disable the 'interrupter enable' bit in USB_CMD + // and clear IE & IP bit in all Interrupter X Management Registers. + // + XhcClearOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_INTE); + for (Index = 0; Index < (UINT16)(Xhc->HcSParams1.Data.MaxIntrs); Index++) { + XhcClearRuntimeRegBit (Xhc, XHC_IMAN_OFFSET + (Index * 32), XHC_IMAN_IE); + XhcSetRuntimeRegBit (Xhc, XHC_IMAN_OFFSET + (Index * 32), XHC_IMAN_IP); + } + + // + // Allocate EventRing for Cmd, Ctrl, Bulk, Interrupt, AsynInterrupt transfer + // + CreateEventRing (Xhc, CMD_INTER, &Xhc->CmdEventRing); + CreateEventRing (Xhc, CTRL_INTER, &Xhc->CtrlTrEventRing); + CreateEventRing (Xhc, BULK_INTER, &Xhc->BulkTrEventRing); + CreateEventRing (Xhc, INT_INTER, &Xhc->IntTrEventRing); + CreateEventRing (Xhc, INT_INTER_ASYNC, &Xhc->AsynIntTrEventRing); +} + +/** + System software shall use a Reset Endpoint Command (section 4.11.4.7) to remove the Halted + condition in the xHC. After the successful completion of the Reset Endpoint Command, the Endpoint + Context is transitioned from the Halted to the Stopped state and the Transfer Ring of the endpoint is + reenabled. The next write to the Doorbell of the Endpoint will transition the Endpoint Context from the + Stopped to the Running state. + + @param Xhc The XHCI device. + @param Urb The urb which makes the endpoint halted. + + @retval EFI_SUCCESS The recovery is successful. + @retval Others Failed to recovery halted endpoint. + +**/ +EFI_STATUS +EFIAPI +XhcRecoverHaltedEndpoint ( + IN USB_XHCI_DEV *Xhc, + IN URB *Urb + ) +{ + EFI_STATUS Status; + EVT_TRB_COMMAND *EvtTrb; + CMD_TRB_RESET_ED CmdTrbResetED; + CMD_SET_TR_DEQ CmdSetTRDeq; + UINT8 Dci; + UINT8 SlotId; + + Status = EFI_SUCCESS; + SlotId = XhcDevAddrToSlotId(Urb->Ep.DevAddr); + Dci = XhcEndpointToDci(Urb->Ep.EpAddr, Urb->Ep.Direction); + + DEBUG ((EFI_D_INFO, "Recovery Halted Slot = %x,Dci = %x\n", SlotId, Dci)); + + // + // 1) Send Reset endpoint command to transit from halt to stop state + // + ZeroMem (&CmdTrbResetED, sizeof (CmdTrbResetED)); + CmdTrbResetED.CycleBit = 1; + CmdTrbResetED.Type = TRB_TYPE_RESET_ENDPOINT; + CmdTrbResetED.EDID = Dci; + CmdTrbResetED.SlotId = SlotId; + Status = XhcCmdTransfer ( + Xhc, + (TRB *) (UINTN) &CmdTrbResetED, + XHC_GENERIC_TIMEOUT, + (TRB **) (UINTN) &EvtTrb + ); + ASSERT (!EFI_ERROR(Status)); + + // + // 2)Set dequeue pointer + // + ZeroMem (&CmdSetTRDeq, sizeof (CmdSetTRDeq)); + CmdSetTRDeq.PtrLo = XHC_LOW_32BIT (Urb->Ring->RingEnqueue) | Urb->Ring->RingPCS; + CmdSetTRDeq.PtrHi = XHC_HIGH_32BIT (Urb->Ring->RingEnqueue); + CmdSetTRDeq.CycleBit = 1; + CmdSetTRDeq.Type = TRB_TYPE_SET_TR_DEQUE; + CmdSetTRDeq.Endpoint = Dci; + CmdSetTRDeq.SlotId = SlotId; + Status = XhcCmdTransfer ( + Xhc, + (TRB *) (UINTN) &CmdSetTRDeq, + XHC_GENERIC_TIMEOUT, + (TRB **) (UINTN) &EvtTrb + ); + ASSERT (!EFI_ERROR(Status)); + + // + // 3)Ring the doorbell to transit from stop to active + // + XhcRingDoorBell (Xhc, SlotId, Dci); + + return Status; +} + +/** + Create XHCI event ring. + + @param Xhc The XHCI device. + @param EventInterrupter The interrupter of event. + @param EventRing The created event ring. + +**/ +VOID +EFIAPI +CreateEventRing ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 EventInterrupter, + OUT EVENT_RING *EventRing + ) +{ + VOID *Buf; + EVENT_RING_SEG_TABLE_ENTRY *ERSTBase; + + ASSERT (EventRing != NULL); + + Buf = AllocateAlignedZeroPool(sizeof (TRB) * EVENT_RING_TRB_NUMBER, 64); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0x3F) == 0); + + EventRing->EventRingSeg0 = Buf; + EventRing->EventInterrupter = EventInterrupter; + EventRing->TrbNumber = EVENT_RING_TRB_NUMBER; + EventRing->EventRingDequeue = (TRB *) EventRing->EventRingSeg0; + EventRing->EventRingEnqueue = (TRB *) EventRing->EventRingSeg0; + // + // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1' + // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring. + // + EventRing->EventRingCCS = 1; + + Buf = AllocateAlignedZeroPool(sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER, 64); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0x3F) == 0); + + ERSTBase = (EVENT_RING_SEG_TABLE_ENTRY *) Buf; + EventRing->ERSTBase = ERSTBase; + ERSTBase->PtrLo = XHC_LOW_32BIT (EventRing->EventRingSeg0); + ERSTBase->PtrHi = XHC_HIGH_32BIT (EventRing->EventRingSeg0); + ERSTBase->RingTrbSize = EVENT_RING_TRB_NUMBER; + + // + // Program the Interrupter Event Ring Segment Table Size (ERSTSZ) register (5.5.2.3.1) + // + XhcWriteRuntimeReg ( + Xhc, + XHC_ERSTSZ_OFFSET + (32 * EventRing->EventInterrupter), + ERST_NUMBER + ); + // + // Program the Interrupter Event Ring Dequeue Pointer (ERDP) register (5.5.2.3.3) + // + XhcWriteRuntimeReg64 ( + Xhc, + XHC_ERDP_OFFSET + (32 * EventRing->EventInterrupter), + (UINT64)EventRing->EventRingDequeue + ); + // + // Program the Interrupter Event Ring Segment Table Base Address (ERSTBA) register(5.5.2.3.2) + // + XhcWriteRuntimeReg64 ( + Xhc, + XHC_ERSTBA_OFFSET + (32 * EventRing->EventInterrupter), + (UINT64) ERSTBase + ); + // + // Need set IMAN IE bit to enble the ring interrupt + // + XhcSetRuntimeRegBit (Xhc, XHC_IMAN_OFFSET + (32 * EventRing->EventInterrupter), XHC_IMAN_IE); +} + +/** + Create XHCI transfer ring. + + @param Xhc The XHCI device. + @param TrbNum The number of TRB in the ring. + @param TransferRing The created transfer ring. + +**/ +VOID +EFIAPI +CreateTransferRing ( + IN USB_XHCI_DEV *Xhc, + IN UINTN TrbNum, + OUT TRANSFER_RING *TransferRing + ) +{ + VOID *Buf; + LNK_TRB *EndTrb; + + Buf = AllocateAlignedZeroPool(sizeof (TRB) * TrbNum, 64); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0x3F) == 0); + + TransferRing->RingSeg0 = Buf; + TransferRing->TrbNumber = TrbNum; + TransferRing->RingEnqueue = (TRB *) TransferRing->RingSeg0; + TransferRing->RingDequeue = (TRB *) TransferRing->RingSeg0; + TransferRing->RingPCS = 1; + // + // 4.9.2 Transfer Ring Management + // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to + // point to the first TRB in the ring. + // + EndTrb = (LNK_TRB*) ((UINTN)Buf + sizeof (TRB) * (TrbNum - 1)); + EndTrb->Type = TRB_TYPE_LINK; + EndTrb->PtrLo = XHC_LOW_32BIT (Buf); + EndTrb->PtrHi = XHC_HIGH_32BIT (Buf); + // + // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit. + // + EndTrb->TC = 1; + // + // Set Cycle bit as other TRB PCS init value + // + EndTrb->CycleBit = 0; +} + +/** + Free XHCI event ring. + + @param Xhc The XHCI device. + @param EventRing The event ring to be freed. + +**/ +EFI_STATUS +EFIAPI +XhcFreeEventRing ( + IN USB_XHCI_DEV *Xhc, + IN EVENT_RING *EventRing +) +{ + UINT8 Index; + EVENT_RING_SEG_TABLE_ENTRY *TablePtr; + VOID *RingBuf; + EVENT_RING_SEG_TABLE_ENTRY *EventRingPtr; + UINTN InterrupterTarget; + + if(EventRing->EventRingSeg0 == NULL) { + return EFI_SUCCESS; + } + + InterrupterTarget = EventRing->EventInterrupter; + // + // Get the Event Ring Segment Table base address + // + TablePtr = (EVENT_RING_SEG_TABLE_ENTRY *)(EventRing->ERSTBase); + + // + // Get all the TRBs Ring and release + // + for (Index = 0; Index < ERST_NUMBER; Index++) { + EventRingPtr = TablePtr + Index; + RingBuf = (VOID *)(UINTN)(EventRingPtr->PtrLo | ((UINT64)EventRingPtr->PtrHi << 32)); + + if(RingBuf != NULL) { + FreeAlignedPool (RingBuf); + ZeroMem (EventRingPtr, sizeof (EVENT_RING_SEG_TABLE_ENTRY)); + } + } + + FreeAlignedPool (TablePtr); + return EFI_SUCCESS; +} + +/** + Free the resouce allocated at initializing schedule. + + @param Xhc The XHCI device. + +**/ +VOID +XhcFreeSched ( + IN USB_XHCI_DEV *Xhc + ) +{ + UINT32 Index; + + if (Xhc->ScratchBuf != NULL) { + for (Index = 0; Index < Xhc->MaxScratchpadBufs; Index++) { + FreeAlignedPool ((VOID*)(UINTN)*Xhc->ScratchBuf++); + } + } + + if (Xhc->DCBAA != NULL) { + FreeAlignedPool (Xhc->DCBAA); + Xhc->DCBAA = NULL; + } + + if (Xhc->CmdRing.RingSeg0 != NULL){ + FreeAlignedPool (Xhc->CmdRing.RingSeg0); + Xhc->CmdRing.RingSeg0 = NULL; + } + XhcFreeEventRing (Xhc,&Xhc->CmdEventRing); + XhcFreeEventRing (Xhc,&Xhc->CtrlTrEventRing); + XhcFreeEventRing (Xhc,&Xhc->BulkTrEventRing); + XhcFreeEventRing (Xhc,&Xhc->AsynIntTrEventRing); + XhcFreeEventRing (Xhc,&Xhc->IntTrEventRing); +} + +/** + Check if it is ring TRB. + + @param Ring The transfer ring + @param Trb The TRB to check if it's in the transfer ring + + @retval TRUE It is in the ring + @retval FALSE It is not in the ring + +**/ +BOOLEAN +IsTransferRingTrb ( + IN TRANSFER_RING *Ring, + IN TRB *Trb + ) +{ + BOOLEAN Flag; + TRB *Trb1; + UINTN Index; + + Trb1 = Ring->RingSeg0; + Flag = FALSE; + + ASSERT (Ring->TrbNumber == CMD_RING_TRB_NUMBER || Ring->TrbNumber == TR_RING_TRB_NUMBER); + + for (Index = 0; Index < Ring->TrbNumber; Index++) { + if (Trb == Trb1) { + Flag = TRUE; + break; + } + Trb1++; + } + + return Flag; +} + +/** + Check the URB's execution result and update the URB's + result accordingly. + + @param Xhc The XHCI device. + @param Urb The URB to check result. + + @return Whether the result of URB transfer is finialized. + +**/ +EFI_STATUS +XhcCheckUrbResult ( + IN USB_XHCI_DEV *Xhc, + IN URB *Urb + ) +{ + BOOLEAN StartDone; + BOOLEAN EndDone; + EVT_TRB_TRANSFER *EvtTrb; + TRB *TRBPtr; + UINTN Index; + UINT8 TRBType; + EFI_STATUS Status; + + ASSERT ((Xhc != NULL) && (Urb != NULL)); + + Urb->Completed = 0; + Urb->Result = EFI_USB_NOERROR; + Status = EFI_SUCCESS; + EvtTrb = NULL; + + if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) { + Urb->Result |= EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + goto EXIT; + } + + // + // Restore the EventRingDequeue and poll the transfer event ring from beginning + // + StartDone = FALSE; + EndDone = FALSE; + Urb->EvtRing->EventRingDequeue = Urb->EvtTrbStart; + for (Index = 0; Index < Urb->EvtRing->TrbNumber; Index++) { + XhcSyncEventRing (Xhc, Urb->EvtRing); + Status = XhcCheckNewEvent (Xhc, Urb->EvtRing, &(TRB *)EvtTrb); + if (Status == EFI_NOT_READY) { + Urb->Result |= EFI_USB_ERR_TIMEOUT; + goto EXIT; + } + + TRBPtr = (TRB *)(UINTN)(EvtTrb->TRBPtrLo | (UINT64) EvtTrb->TRBPtrHi << 32); + + switch (EvtTrb->Completcode) { + case TRB_COMPLETION_STALL_ERROR: + Urb->Result |= EFI_USB_ERR_STALL; + Status = EFI_DEVICE_ERROR; + DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: STALL_ERROR! Completcode = %x\n",EvtTrb->Completcode)); + goto EXIT; + break; + + case TRB_COMPLETION_BABBLE_ERROR: + Urb->Result |= EFI_USB_ERR_BABBLE; + Status = EFI_DEVICE_ERROR; + DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: BABBLE_ERROR! Completcode = %x\n",EvtTrb->Completcode)); + goto EXIT; + break; + + case TRB_COMPLETION_DATA_BUFFER_ERROR: + Urb->Result |= EFI_USB_ERR_BUFFER; + Status = EFI_DEVICE_ERROR; + DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: ERR_BUFFER! Completcode = %x\n",EvtTrb->Completcode)); + goto EXIT; + break; + + case TRB_COMPLETION_USB_TRANSACTION_ERROR: + Urb->Result |= EFI_USB_ERR_TIMEOUT; + Status = EFI_DEVICE_ERROR; + DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: TRANSACTION_ERROR! Completcode = %x\n",EvtTrb->Completcode)); + goto EXIT; + break; + + case TRB_COMPLETION_SHORT_PACKET: + case TRB_COMPLETION_SUCCESS: + if (IsTransferRingTrb (Urb->Ring, TRBPtr)) { + if (EvtTrb->Completcode == TRB_COMPLETION_SHORT_PACKET) { + DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: short packet happens!\n")); + } + TRBType = (UINT8) (TRBPtr->Type); + if ((TRBType == TRB_TYPE_DATA_STAGE) || + (TRBType == TRB_TYPE_NORMAL) || + (TRBType == TRB_TYPE_ISOCH)) { + Urb->Completed += (Urb->DataLen - EvtTrb->Lenth); + } + } + Status = EFI_SUCCESS; + break; + + default: + DEBUG ((EFI_D_ERROR, "Transfer Default Error Occur! Completcode = 0x%x!\n",EvtTrb->Completcode)); + Urb->Result |= EFI_USB_ERR_TIMEOUT; + Status = EFI_DEVICE_ERROR; + goto EXIT; + break; + } + + // + // Only check first and end Trb event address + // + if (TRBPtr == Urb->TrbStart) { + StartDone = TRUE; + } + + if (TRBPtr == Urb->TrbEnd) { + EndDone = TRUE; + } + + if (StartDone && EndDone) { + break; + } + } + +EXIT: + return Status; +} + + +/** + Execute the transfer by polling the URB. This is a synchronous operation. + + @param Xhc The XHCI device. + @param CmdTransfer The executed URB is for cmd transfer or not. + @param Urb The URB to execute. + @param TimeOut The time to wait before abort, in millisecond. + + @return EFI_DEVICE_ERROR The transfer failed due to transfer error. + @return EFI_TIMEOUT The transfer failed due to time out. + @return EFI_SUCCESS The transfer finished OK. + +**/ +EFI_STATUS +XhcExecTransfer ( + IN USB_XHCI_DEV *Xhc, + IN BOOLEAN CmdTransfer, + IN URB *Urb, + IN UINTN TimeOut + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN Loop; + UINT8 SlotId; + UINT8 Dci; + + if (CmdTransfer) { + SlotId = 0; + Dci = 0; + } else { + SlotId = XhcDevAddrToSlotId(Urb->Ep.DevAddr); + Dci = XhcEndpointToDci(Urb->Ep.EpAddr, Urb->Ep.Direction); + } + + Status = EFI_SUCCESS; + Loop = (TimeOut * XHC_1_MILLISECOND / XHC_SYNC_POLL_INTERVAL) + 1; + if (TimeOut == 0) { + Loop = 0xFFFFFFFF; + } + + XhcRingDoorBell (Xhc, SlotId, Dci); + + for (Index = 0; Index < Loop; Index++) { + Status = XhcCheckUrbResult (Xhc, Urb); + if ((Status != EFI_NOT_READY)) { + break; + } + gBS->Stall (XHC_SYNC_POLL_INTERVAL); + } + + return Status; +} + +/** + Delete a single asynchronous interrupt transfer for + the device and endpoint. + + @param Xhc The XHCI device. + @param DevAddr The address of the target device. + @param EpNum The endpoint of the target. + + @retval EFI_SUCCESS An asynchronous transfer is removed. + @retval EFI_NOT_FOUND No transfer for the device is found. + +**/ +EFI_STATUS +XhciDelAsyncIntTransfer ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 DevAddr, + IN UINT8 EpNum + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + URB *Urb; + EFI_USB_DATA_DIRECTION Direction; + BOOLEAN Found; + + Direction = ((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut; + EpNum &= 0x0F; + + Found = FALSE; + Urb = NULL; + + EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) { + Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList); + if ((Urb->Ep.DevAddr == DevAddr) && + (Urb->Ep.EpAddr == EpNum) && + (Urb->Ep.Direction == Direction)) { + RemoveEntryList (&Urb->UrbList); + FreePool (Urb->Data); + FreePool (Urb); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Remove all the asynchronous interrutp transfers. + + @param Xhc The XHCI device. + +**/ +VOID +XhciDelAllAsyncIntTransfers ( + IN USB_XHCI_DEV *Xhc + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + URB *Urb; + + EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) { + Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList); + RemoveEntryList (&Urb->UrbList); + FreePool (Urb->Data); + FreePool (Urb); + } +} + +/** + Update the queue head for next round of asynchronous transfer + + @param Xhc The XHCI device. + @param Urb The URB to update + +**/ +VOID +XhcUpdateAsyncRequest ( + IN USB_XHCI_DEV* Xhc, + IN URB *Urb + ) +{ + EFI_STATUS Status; + + if (Urb->Result == EFI_USB_NOERROR) { + Status = XhcCreateTransferTrb (Xhc, Urb); + ASSERT_EFI_ERROR (Status); + Status = RingIntTransferDoorBell (Xhc, Urb); + ASSERT_EFI_ERROR (Status); + } +} + + +/** + Interrupt transfer periodic check handler. + + @param Event Interrupt event. + @param Context Pointer to USB_XHCI_DEV. + +**/ +VOID +EFIAPI +XhcMonitorAsyncRequests ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + USB_XHCI_DEV *Xhc; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + UINT8 *ProcBuf; + URB *Urb; + UINT8 SlotId; + EFI_STATUS Status; + EFI_TPL OldTpl; + + OldTpl = gBS->RaiseTPL (XHC_TPL); + + Xhc = (USB_XHCI_DEV*) Context; + + EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) { + Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList); + + // + // Make sure that the device is available before every check. + // + SlotId = XhcDevAddrToSlotId(Urb->Ep.DevAddr); + if (SlotId == 0) { + continue; + } + + // + // Check the result of URB execution. If it is still + // active, check the next one. + // + Status = XhcCheckUrbResult (Xhc, Urb); + + if (Status == EFI_NOT_READY) { + continue; + } + + // + // Allocate a buffer then copy the transferred data for user. + // If failed to allocate the buffer, update the URB for next + // round of transfer. Ignore the data of this round. + // + ProcBuf = NULL; + if (Urb->Result == EFI_USB_NOERROR) { + ASSERT (Urb->Completed <= Urb->DataLen); + + ProcBuf = AllocatePool (Urb->Completed); + + if (ProcBuf == NULL) { + XhcUpdateAsyncRequest (Xhc, Urb); + continue; + } + + CopyMem (ProcBuf, Urb->Data, Urb->Completed); + } + + XhcUpdateAsyncRequest (Xhc, Urb); + + // + // Leave error recovery to its related device driver. A + // common case of the error recovery is to re-submit the + // interrupt transfer which is linked to the head of the + // list. This function scans from head to tail. So the + // re-submitted interrupt transfer's callback function + // will not be called again in this round. Don't touch this + // URB after the callback, it may have been removed by the + // callback. + // + if (Urb->Callback != NULL) { + // + // Restore the old TPL, USB bus maybe connect device in + // his callback. Some drivers may has a lower TPL restriction. + // + gBS->RestoreTPL (OldTpl); + (Urb->Callback) (ProcBuf, Urb->Completed, Urb->Context, Urb->Result); + OldTpl = gBS->RaiseTPL (XHC_TPL); + } + + if (ProcBuf != NULL) { + gBS->FreePool (ProcBuf); + } + } + gBS->RestoreTPL (OldTpl); +} + +/** + Monitor the port status change. Enable/Disable device slot if there is a device attached/detached. + + @param Xhc The XHCI device. + @param ParentRouteChart The route string pointed to the parent device if it exists. + @param Port The port to be polled. + @param PortState The port state. + + @retval EFI_SUCCESS Successfully enable/disable device slot according to port state. + @retval Others Should not appear. + +**/ +EFI_STATUS +EFIAPI +XhcPollPortStatusChange ( + IN USB_XHCI_DEV* Xhc, + IN USB_DEV_ROUTE ParentRouteChart, + IN UINT8 Port, + IN EFI_USB_PORT_STATUS *PortState + ) +{ + EFI_STATUS Status; + UINT8 Speed; + UINT8 SlotId; + USB_DEV_ROUTE RouteChart; + + Status = EFI_SUCCESS; + + if (ParentRouteChart.Dword == 0) { + RouteChart.Field.RouteString = 0; + RouteChart.Field.RootPortNum = Port + 1; + RouteChart.Field.TierNum = 1; + } else { + if(Port < 14) { + RouteChart.Field.RouteString = ParentRouteChart.Field.RouteString | (Port << (4 * (ParentRouteChart.Field.TierNum - 1))); + } else { + RouteChart.Field.RouteString = ParentRouteChart.Field.RouteString | (15 << (4 * (ParentRouteChart.Field.TierNum - 1))); + } + RouteChart.Field.RootPortNum = ParentRouteChart.Field.RootPortNum; + RouteChart.Field.TierNum = ParentRouteChart.Field.TierNum + 1; + } + + if (((PortState->PortStatus & USB_PORT_STAT_ENABLE) != 0) && + ((PortState->PortStatus & USB_PORT_STAT_CONNECTION) != 0)) { + // + // Has a device attached, Identify device speed after port is enabled. + // + Speed = EFI_USB_SPEED_FULL; + if ((PortState->PortStatus & USB_PORT_STAT_LOW_SPEED) != 0) { + Speed = EFI_USB_SPEED_LOW; + } else if ((PortState->PortStatus & USB_PORT_STAT_HIGH_SPEED) != 0) { + Speed = EFI_USB_SPEED_HIGH; + } else if ((PortState->PortStatus & USB_PORT_STAT_SUPER_SPEED) != 0) { + Speed = EFI_USB_SPEED_SUPER; + } + // + // Execute Enable_Slot cmd for attached device, initialize device context and assign device address. + // + SlotId = XhcRouteStringToSlotId (RouteChart); + if (SlotId == 0) { + Status = XhcInitializeDeviceSlot (Xhc, ParentRouteChart, Port, RouteChart, Speed); + ASSERT_EFI_ERROR (Status); + } + } else if ((PortState->PortStatus & USB_PORT_STAT_CONNECTION) == 0) { + // + // Device is detached. Disable the allocated device slot and release resource. + // + SlotId = XhcRouteStringToSlotId (RouteChart); + if (SlotId != 0) { + Status = XhcDisableSlotCmd (Xhc, SlotId); + ASSERT_EFI_ERROR (Status); + } + } + return Status; +} + + +/** + Calculate the device context index by endpoint address and direction. + + @param EpAddr The target endpoint number. + @param Direction The direction of the target endpoint. + + @return The device context index of endpoint. + +**/ +UINT8 +XhcEndpointToDci ( + IN UINT8 EpAddr, + IN UINT8 Direction + ) +{ + UINT8 Index; + + if (EpAddr == 0) { + return 1; + } else { + Index = 2 * EpAddr; + if (Direction == EfiUsbDataIn) { + Index += 1; + } + return Index; + } +} + +/** + Find out the slot id according to device address assigned by XHCI's Address_Device cmd. + + @param DevAddr The device address of the target device. + + @return The slot id used by the device. + +**/ +UINT8 +EFIAPI +XhcDevAddrToSlotId ( + IN UINT8 DevAddr + ) +{ + UINT8 Index; + + for (Index = 0; Index < 255; Index++) { + if (UsbDevContext[Index + 1].Enabled && + (UsbDevContext[Index + 1].SlotId != 0) && + (UsbDevContext[Index + 1].XhciDevAddr == DevAddr)) { + break; + } + } + + if (Index == 255) { + return 0; + } + + return UsbDevContext[Index + 1].SlotId; +} + +/** + Find out the actual device address according to the requested device address from UsbBus. + + @param BusDevAddr The requested device address by UsbBus upper driver. + + @return The actual device address assigned to the device. + +**/ +UINT8 +EFIAPI +XhcBusDevAddrToSlotId ( + IN UINT8 BusDevAddr + ) +{ + UINT8 Index; + + for (Index = 0; Index < 255; Index++) { + if (UsbDevContext[Index + 1].Enabled && + (UsbDevContext[Index + 1].SlotId != 0) && + (UsbDevContext[Index + 1].BusDevAddr == BusDevAddr)) { + break; + } + } + + if (Index == 255) { + return 0; + } + + return UsbDevContext[Index + 1].SlotId; +} + +/** + Find out the slot id according to the device's route string. + + @param RouteString The route string described the device location. + + @return The slot id used by the device. + +**/ +UINT8 +EFIAPI +XhcRouteStringToSlotId ( + IN USB_DEV_ROUTE RouteString + ) +{ + UINT8 Index; + + for (Index = 0; Index < 255; Index++) { + if (UsbDevContext[Index + 1].Enabled && + (UsbDevContext[Index + 1].SlotId != 0) && + (UsbDevContext[Index + 1].RouteString.Dword == RouteString.Dword)) { + break; + } + } + + if (Index == 255) { + return 0; + } + + return UsbDevContext[Index + 1].SlotId; +} + +/** + Synchronize the specified event ring to update the enqueue and dequeue pointer. + + @param Xhc The XHCI device. + @param EvtRing The event ring to sync. + + @retval EFI_SUCCESS The event ring is synchronized successfully. + +**/ +EFI_STATUS +EFIAPI +XhcSyncEventRing ( + IN USB_XHCI_DEV *Xhc, + IN EVENT_RING *EvtRing + ) +{ + UINTN Index; + TRB *EvtTrb1; + TRB *EvtTrb2; + TRB *XhcDequeue; + + ASSERT (EvtRing != NULL); + + // + // Calculate the EventRingEnqueue and EventRingCCS. + // Note: only support single Segment + // + EvtTrb1 = EvtRing->EventRingSeg0; + EvtTrb2 = EvtRing->EventRingSeg0; + + for (Index = 0; Index < EvtRing->TrbNumber; Index++) { + if (EvtTrb1->CycleBit != EvtTrb2->CycleBit) { + break; + } + EvtTrb1++; + } + + if (Index < EvtRing->TrbNumber) { + EvtRing->EventRingEnqueue = EvtTrb1; + EvtRing->EventRingCCS = (EvtTrb2->CycleBit) ? 1 : 0; + } else { + EvtRing->EventRingEnqueue = EvtTrb2; + EvtRing->EventRingCCS = (EvtTrb2->CycleBit) ? 0 : 1; + } + + // + // Apply the EventRingDequeue to Xhc + // + XhcDequeue = (TRB *)(UINTN) XhcReadRuntimeReg64 ( + Xhc, + XHC_ERDP_OFFSET + (32 * EvtRing->EventInterrupter) + ); + + if (((UINT64) XhcDequeue & (~0x0F)) != ((UINT64) EvtRing->EventRingDequeue & (~0x0F))) { + XhcWriteRuntimeReg64 ( + Xhc, + XHC_ERDP_OFFSET + (32 * EvtRing->EventInterrupter), + (UINT64)EvtRing->EventRingDequeue | BIT3 + ); + } + + return EFI_SUCCESS; +} + +/** + Synchronize the specified transfer ring to update the enqueue and dequeue pointer. + + @param Xhc The XHCI device. + @param TrsRing The transfer ring to sync. + + @retval EFI_SUCCESS The transfer ring is synchronized successfully. + +**/ +EFI_STATUS +EFIAPI +XhcSyncTrsRing ( + IN USB_XHCI_DEV *Xhc, + IN TRANSFER_RING *TrsRing + ) +{ + UINTN Index; + TRB *TrsTrb; + + ASSERT (TrsRing != NULL); + // + // Calculate the latest RingEnqueue and RingPCS + // + TrsTrb = TrsRing->RingEnqueue; + ASSERT (TrsTrb != NULL); + + for (Index = 0; Index < TrsRing->TrbNumber; Index++) { + if (TrsTrb->CycleBit != (TrsRing->RingPCS & BIT0)) { + break; + } + TrsTrb++; + if ((UINT8) TrsTrb->Type == TRB_TYPE_LINK) { + ASSERT (((LNK_TRB*)TrsTrb)->TC != 0); + // + // set cycle bit in Link TRB as normal + // + ((LNK_TRB*)TrsTrb)->CycleBit = TrsRing->RingPCS & BIT0; + // + // Toggle PCS maintained by software + // + TrsRing->RingPCS = (TrsRing->RingPCS & BIT0) ? 0 : 1; + TrsTrb = (TRB*)(UINTN)((TrsTrb->Dword1 | ((UINT64)TrsTrb->Dword2 << 32)) & ~0x0F); + } + } + + ASSERT (Index != TrsRing->TrbNumber); + + if (TrsTrb != TrsRing->RingEnqueue) { + TrsRing->RingEnqueue = TrsTrb; + } + + // + // Clear the Trb context for enqueue, but reserve the PCS bit + // + TrsTrb->Dword1 = 0; + TrsTrb->Dword2 = 0; + TrsTrb->Dword3 = 0; + TrsTrb->RsvdZ1 = 0; + TrsTrb->Type = 0; + TrsTrb->RsvdZ2 = 0; + + return EFI_SUCCESS; +} + +/** + Check if there is a new generated event. + + @param Xhc The XHCI device. + @param EvtRing The event ring to check. + @param NewEvtTrb The new event TRB found. + + @retval EFI_SUCCESS Found a new event TRB at the event ring. + @retval EFI_NOT_READY The event ring has no new event. + +**/ +EFI_STATUS +EFIAPI +XhcCheckNewEvent ( + IN USB_XHCI_DEV *Xhc, + IN EVENT_RING *EvtRing, + OUT TRB **NewEvtTrb + ) +{ + EFI_STATUS Status; + TRB *EvtTrb; + + ASSERT (EvtRing != NULL); + + EvtTrb = EvtRing->EventRingDequeue; + *NewEvtTrb = EvtRing->EventRingDequeue; + + if (EvtRing->EventRingDequeue == EvtRing->EventRingEnqueue) { + return EFI_NOT_READY; + } + + Status = EFI_SUCCESS; + + if (((EvtTrb->Dword3 >> 24) & 0xFF) != TRB_COMPLETION_SUCCESS) { + Status = EFI_DEVICE_ERROR; + } + + EvtRing->EventRingDequeue++; + // + // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring. + // + if ((UINTN)EvtRing->EventRingDequeue >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB) * EvtRing->TrbNumber)) { + EvtRing->EventRingDequeue = EvtRing->EventRingSeg0; + } + + return Status; +} + +/** + Ring the door bell to notify XHCI there is a transaction to be executed. + + @param Xhc The XHCI device. + @param SlotId The slot id of the target device. + @param Dci The device context index of the target slot or endpoint. + + @retval EFI_SUCCESS Successfully ring the door bell. + +**/ +EFI_STATUS +EFIAPI +XhcRingDoorBell ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 Dci + ) +{ + if (SlotId == 0) { + XhcWriteDoorBellReg (Xhc, 0, 0); + } else { + XhcWriteDoorBellReg (Xhc, SlotId * sizeof (UINT32), Dci); + } + + return EFI_SUCCESS; +} + +/** + Ring the door bell to notify XHCI there is a transaction to be executed through URB. + + @param Xhc The XHCI device. + @param Urb The URB to be rung. + + @retval EFI_SUCCESS Successfully ring the door bell. + +**/ +EFI_STATUS +RingIntTransferDoorBell ( + IN USB_XHCI_DEV *Xhc, + IN URB *Urb + ) +{ + UINT8 SlotId; + UINT8 Dci; + + SlotId = XhcDevAddrToSlotId(Urb->Ep.DevAddr); + Dci = XhcEndpointToDci(Urb->Ep.EpAddr, Urb->Ep.Direction); + XhcRingDoorBell (Xhc, SlotId, Dci); + return EFI_SUCCESS; +} + +/** + Assign and initialize the device slot for a new device. + + @param Xhc The XHCI device. + @param ParentRouteChart The route string pointed to the parent device. + @param ParentPort The port at which the device is located. + @param RouteChart The route string pointed to the device. + @param DeviceSpeed The device speed. + + @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it. + +**/ +EFI_STATUS +EFIAPI +XhcInitializeDeviceSlot ( + IN USB_XHCI_DEV *Xhc, + IN USB_DEV_ROUTE ParentRouteChart, + IN UINT16 ParentPort, + IN USB_DEV_ROUTE RouteChart, + IN UINT8 DeviceSpeed + ) +{ + EFI_STATUS Status; + EVT_TRB_COMMAND *EvtTrb; + INPUT_CONTEXT *InputContext; + DEVICE_CONTEXT *OutputDevContxt; + TRANSFER_RING *EndpointTransferRing; + CMD_TRB_ADDR_DEV CmdTrbAddr; + UINT8 DeviceAddress; + CMD_TRB_EN_SLOT CmdTrb; + UINT8 SlotId; + UINT8 ParentSlotId; + DEVICE_CONTEXT *ParentDeviceContext; + + ZeroMem (&CmdTrb, sizeof (CMD_TRB_EN_SLOT)); + CmdTrb.CycleBit = 1; + CmdTrb.Type = TRB_TYPE_EN_SLOT; + + Status = XhcCmdTransfer ( + Xhc, + (TRB *) (UINTN) &CmdTrb, + XHC_GENERIC_TIMEOUT, + (TRB **) (UINTN) &EvtTrb + ); + ASSERT_EFI_ERROR (Status); + ASSERT (EvtTrb->SlotId <= Xhc->MaxSlotsEn); + DEBUG ((EFI_D_INFO, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb->SlotId)); + SlotId = (UINT8)EvtTrb->SlotId; + ASSERT (SlotId != 0); + + ZeroMem (&UsbDevContext[SlotId], sizeof (USB_DEV_CONTEXT)); + UsbDevContext[SlotId].Enabled = TRUE; + UsbDevContext[SlotId].SlotId = SlotId; + UsbDevContext[SlotId].RouteString.Dword = RouteChart.Dword; + UsbDevContext[SlotId].ParentRouteString.Dword = ParentRouteChart.Dword; + + // + // 4.3.3 Device Slot Initialization + // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'. + // + InputContext = AllocateAlignedZeroPool(sizeof (INPUT_CONTEXT), 64); + ASSERT (InputContext != NULL); + ASSERT (((UINTN) InputContext & 0x3F) == 0); + + UsbDevContext[SlotId].InputContext = (VOID *) InputContext; + + // + // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1 + // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input + // Context are affected by the command. + // + InputContext->InputControlContext.Dword2 |= (BIT0 | BIT1); + + // + // 3) Initialize the Input Slot Context data structure + // + InputContext->Slot.RouteStr = RouteChart.Field.RouteString; + InputContext->Slot.Speed = DeviceSpeed + 1; + InputContext->Slot.ContextEntries = 1; + InputContext->Slot.RootHubPortNum = RouteChart.Field.RootPortNum; + + if (RouteChart.Field.RouteString) { + // + // The device is behind of hub device. + // + ParentSlotId = XhcRouteStringToSlotId(ParentRouteChart); + ASSERT (ParentSlotId != 0); + // + //if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context + // + ParentDeviceContext = (DEVICE_CONTEXT *)UsbDevContext[ParentSlotId].OutputDevContxt; + if ((ParentDeviceContext->Slot.TTPortNum == 0) && + (ParentDeviceContext->Slot.TTHubSlotId == 0)) { + if ((ParentDeviceContext->Slot.Speed == (EFI_USB_SPEED_HIGH + 1)) && (DeviceSpeed < EFI_USB_SPEED_HIGH)) { + // + // Full/Low device attached to High speed hub port that isolates the high speed signaling + // environment from Full/Low speed signaling environment for a device + // + InputContext->Slot.TTPortNum = ParentPort; + InputContext->Slot.TTHubSlotId = ParentSlotId; + } + } else { + // + // Inherit the TT parameters from parent device. + // + InputContext->Slot.TTPortNum = ParentDeviceContext->Slot.TTPortNum; + InputContext->Slot.TTHubSlotId = ParentDeviceContext->Slot.TTHubSlotId; + // + // If the device is a High speed device then down the speed to be the same as its parent Hub + // + if (DeviceSpeed == EFI_USB_SPEED_HIGH) { + InputContext->Slot.Speed = ParentDeviceContext->Slot.Speed; + } + } + } + + // + // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint. + // + EndpointTransferRing = AllocateAlignedZeroPool(sizeof (TRANSFER_RING), 64); + UsbDevContext[SlotId].EndpointTransferRing[0] = EndpointTransferRing; + CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)UsbDevContext[SlotId].EndpointTransferRing[0]); + // + // 5) Initialize the Input default control Endpoint 0 Context (6.2.3). + // + InputContext->EP[0].EPType = ED_CONTROL_BIDIR; + + if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + InputContext->EP[0].MaxPacketSize = 512; + } else if (DeviceSpeed == EFI_USB_SPEED_HIGH) { + InputContext->EP[0].MaxPacketSize = 64; + } else { + InputContext->EP[0].MaxPacketSize = 8; + } + // + // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints + // 1KB, and Bulk and Isoch endpoints 3KB. + // + InputContext->EP[0].AverageTRBLength = 8; + InputContext->EP[0].MaxBurstSize = 0; + InputContext->EP[0].Interval = 0; + InputContext->EP[0].MaxPStreams = 0; + InputContext->EP[0].Mult = 0; + InputContext->EP[0].CErr = 3; + + // + // Init the DCS(dequeue cycle state) as the transfer ring's CCS + // + InputContext->EP[0].PtrLo = XHC_LOW_32BIT (((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[0])->RingSeg0) | BIT0; + InputContext->EP[0].PtrHi = XHC_HIGH_32BIT (((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[0])->RingSeg0); + + // + // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'. + // + OutputDevContxt = AllocateAlignedZeroPool(sizeof (DEVICE_CONTEXT), 64); + ASSERT (OutputDevContxt != NULL); + ASSERT (((UINTN) OutputDevContxt & 0x3F) == 0); + + UsbDevContext[SlotId].OutputDevContxt = OutputDevContxt; + // + // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with + // a pointer to the Output Device Context data structure (6.2.1). + // + Xhc->DCBAA[SlotId] = (UINT64) (UINTN) OutputDevContxt; + + // + // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input + // Context data structure described above. + // + ZeroMem (&CmdTrbAddr, sizeof (CmdTrbAddr)); + CmdTrbAddr.PtrLo = XHC_LOW_32BIT (UsbDevContext[SlotId].InputContext); + CmdTrbAddr.PtrHi = XHC_HIGH_32BIT (UsbDevContext[SlotId].InputContext); + CmdTrbAddr.CycleBit = 1; + CmdTrbAddr.Type = TRB_TYPE_ADDRESS_DEV; + CmdTrbAddr.SlotId = UsbDevContext[SlotId].SlotId; + Status = XhcCmdTransfer ( + Xhc, + (TRB *) (UINTN) &CmdTrbAddr, + XHC_GENERIC_TIMEOUT, + (TRB **) (UINTN) &EvtTrb + ); + ASSERT (!EFI_ERROR(Status)); + + DeviceAddress = (UINT8) ((DEVICE_CONTEXT *) OutputDevContxt)->Slot.DeviceAddress; + DEBUG ((EFI_D_INFO, " Address %d assigned succeefully\n", DeviceAddress)); + + UsbDevContext[SlotId].XhciDevAddr = DeviceAddress; + + return Status; +} + +/** + Disable the specified device slot. + + @param Xhc The XHCI device. + @param SlotId The slot id to be disabled. + + @retval EFI_SUCCESS Successfully disable the device slot. + +**/ +EFI_STATUS +EFIAPI +XhcDisableSlotCmd ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 SlotId + ) +{ + EFI_STATUS Status; + TRB *EvtTrb; + CMD_TRB_DIS_SLOT CmdTrbDisSlot; + UINT8 Index; + VOID *RingSeg; + + // + // Disable the device slots occupied by these devices on its downstream ports. + // Entry 0 is reserved. + // + for (Index = 0; Index < 255; Index++) { + if (!UsbDevContext[Index + 1].Enabled || + (UsbDevContext[Index + 1].SlotId == 0) || + (UsbDevContext[Index + 1].ParentRouteString.Dword != UsbDevContext[SlotId].RouteString.Dword)) { + continue; + } + + Status = XhcDisableSlotCmd (Xhc, UsbDevContext[Index + 1].SlotId); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcDisableSlotCmd: failed to disable child, ignore error\n")); + UsbDevContext[Index + 1].SlotId = 0; + } + } + + // + // Construct the disable slot command + // + DEBUG ((EFI_D_INFO, "Disable device slot %d!\n", SlotId)); + + ZeroMem (&CmdTrbDisSlot, sizeof (CmdTrbDisSlot)); + CmdTrbDisSlot.CycleBit = 1; + CmdTrbDisSlot.Type = TRB_TYPE_DIS_SLOT; + CmdTrbDisSlot.SlotId = SlotId; + Status = XhcCmdTransfer ( + Xhc, + (TRB *) (UINTN) &CmdTrbDisSlot, + XHC_GENERIC_TIMEOUT, + (TRB **) (UINTN) &EvtTrb + ); + ASSERT_EFI_ERROR(Status); + // + // Free the slot's device context entry + // + Xhc->DCBAA[SlotId] = 0; + + // + // Free the slot related data structure + // + for (Index = 0; Index < 31; Index++) { + if (UsbDevContext[SlotId].EndpointTransferRing[Index] != NULL) { + RingSeg = ((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[Index])->RingSeg0; + if (RingSeg != NULL) { + FreeAlignedPool(RingSeg); + } + FreeAlignedPool(UsbDevContext[SlotId].EndpointTransferRing[Index]); + } + } + + for (Index = 0; Index < UsbDevContext[SlotId].DevDesc.NumConfigurations; Index++) { + if (UsbDevContext[SlotId].ConfDesc[Index] != NULL) { + FreePool (UsbDevContext[SlotId].ConfDesc[Index]); + } + } + + if (UsbDevContext[SlotId].InputContext != NULL) { + FreeAlignedPool (UsbDevContext[SlotId].InputContext); + } + + if (UsbDevContext[SlotId].OutputDevContxt != NULL) { + FreeAlignedPool (UsbDevContext[SlotId].OutputDevContxt); + } + // + // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established + // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to + // remove urb from XHCI's asynchronous transfer list. + // + UsbDevContext[SlotId].Enabled = FALSE; + + return Status; +} + +/** + Configure all the device endpoints through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param ConfigDesc The pointer to the usb device configuration descriptor. + + @retval EFI_SUCCESS Successfully configure all the device endpoints. + +**/ +EFI_STATUS +EFIAPI +XhcSetConfigCmd ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN USB_CONFIG_DESCRIPTOR *ConfigDesc + ) +{ + EFI_STATUS Status; + + USB_INTERFACE_DESCRIPTOR *IfDesc; + USB_ENDPOINT_DESCRIPTOR *EpDesc; + UINT8 Index; + UINTN NumEp; + UINTN EpIndex; + UINT8 EpAddr; + UINT8 Direction; + UINT8 Dci; + UINT8 MaxDci; + UINT32 PhyAddr; + UINT8 Interval; + + TRANSFER_RING *EndpointTransferRing; + CMD_CFG_ED CmdTrbCfgEP; + INPUT_CONTEXT *InputContext; + DEVICE_CONTEXT *OutputDevContxt; + EVT_TRB_COMMAND *EvtTrb; + // + // 4.6.6 Configure Endpoint + // + InputContext = UsbDevContext[SlotId].InputContext; + OutputDevContxt = UsbDevContext[SlotId].OutputDevContxt; + ZeroMem (InputContext, sizeof (INPUT_CONTEXT)); + CopyMem (&InputContext->Slot, &OutputDevContxt->Slot, sizeof (SLOT_CONTEXT)); + + ASSERT (ConfigDesc != NULL); + + MaxDci = 0; + + IfDesc = (USB_INTERFACE_DESCRIPTOR *)(ConfigDesc + 1); + for (Index = 0; Index < ConfigDesc->NumInterfaces; Index++) { + while (IfDesc->DescriptorType != USB_DESC_TYPE_INTERFACE) { + IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); + } + + NumEp = IfDesc->NumEndpoints; + + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)(IfDesc + 1); + for (EpIndex = 0; EpIndex < NumEp; EpIndex++) { + while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) { + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + } + + EpAddr = EpDesc->EndpointAddress & 0x0F; + Direction = (UINT8)((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut); + + Dci = XhcEndpointToDci (EpAddr, Direction); + if (Dci > MaxDci) { + MaxDci = Dci; + } + + InputContext->InputControlContext.Dword2 |= (BIT0 << Dci); + InputContext->EP[Dci-1].MaxPacketSize = EpDesc->MaxPacketSize; + + if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + // + // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor. + // + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + } else { + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + } + + switch (EpDesc->Attributes & USB_ENDPOINT_TYPE_MASK) { + case USB_ENDPOINT_BULK: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_BULK_IN; + } else { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_BULK_OUT; + } + + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + if (UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { + EndpointTransferRing = AllocateAlignedZeroPool(sizeof (TRANSFER_RING), 64); + UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; + CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + } + + break; + case USB_ENDPOINT_ISO: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 0; + InputContext->EP[Dci-1].EPType = ED_ISOCH_IN; + } else { + InputContext->EP[Dci-1].CErr = 0; + InputContext->EP[Dci-1].EPType = ED_ISOCH_OUT; + } + break; + case USB_ENDPOINT_INTERRUPT: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_INTERRUPT_IN; + } else { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_INTERRUPT_OUT; + } + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + InputContext->EP[Dci-1].MaxESITPayload = EpDesc->MaxPacketSize; + // + // Get the bInterval from descriptor and init the the interval field of endpoint context + // + if ((DeviceSpeed == EFI_USB_SPEED_FULL) || (DeviceSpeed == EFI_USB_SPEED_LOW)) { + Interval = EpDesc->Interval; + // + // BUGBUG: Hard code the interval to MAX + // + InputContext->EP[Dci-1].Interval = 6; + } else if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + Interval = EpDesc->Interval; + InputContext->EP[Dci-1].Interval = 0x0F; + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + InputContext->EP[Dci-1].MaxESITPayload = 0x0002; + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + InputContext->EP[Dci-1].CErr = 3; + } + + if (UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { + EndpointTransferRing = AllocateAlignedZeroPool(sizeof (TRANSFER_RING), 64); + UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; + CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + } + break; + + case USB_ENDPOINT_CONTROL: + default: + ASSERT (0); + break; + } + + PhyAddr = XHC_LOW_32BIT (((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0); + PhyAddr &= ~(0x0F); + PhyAddr |= ((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingPCS; + InputContext->EP[Dci-1].PtrLo = PhyAddr; + InputContext->EP[Dci-1].PtrHi = XHC_HIGH_32BIT (((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0); + + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + } + IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); + } + + InputContext->InputControlContext.Dword2 |= BIT0; + InputContext->Slot.ContextEntries = MaxDci; + // + // configure endpoint + // + ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP)); + CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (InputContext); + CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (InputContext); + CmdTrbCfgEP.CycleBit = 1; + CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT; + CmdTrbCfgEP.SlotId = UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "Configure Endpoint\n")); + Status = XhcCmdTransfer ( + Xhc, + (TRB *) (UINTN) &CmdTrbCfgEP, + XHC_GENERIC_TIMEOUT, + (TRB **) (UINTN) &EvtTrb + ); + ASSERT_EFI_ERROR(Status); + + return Status; +} + +/** + Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be evaluated. + @param MaxPacketSize The max packet size supported by the device control transfer. + + @retval EFI_SUCCESS Successfully evaluate the device endpoint 0. + +**/ +EFI_STATUS +EFIAPI +XhcEvaluateContext ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 SlotId, + IN UINT32 MaxPacketSize + ) +{ + EFI_STATUS Status; + CMD_TRB_EVALU_CONTX CmdTrbEvalu; + EVT_TRB_COMMAND *EvtTrb; + INPUT_CONTEXT *InputContext; + + ASSERT (UsbDevContext[SlotId].SlotId != 0); + + // + // 4.6.7 Evaluate Context + // + InputContext = UsbDevContext[SlotId].InputContext; + ZeroMem (InputContext, sizeof (INPUT_CONTEXT)); + + InputContext->InputControlContext.Dword2 |= BIT1; + InputContext->EP[0].MaxPacketSize = MaxPacketSize; + + ZeroMem (&CmdTrbEvalu, sizeof (CmdTrbEvalu)); + CmdTrbEvalu.PtrLo = XHC_LOW_32BIT (InputContext); + CmdTrbEvalu.PtrHi = XHC_HIGH_32BIT (InputContext); + CmdTrbEvalu.CycleBit = 1; + CmdTrbEvalu.Type = TRB_TYPE_EVALU_CONTXT; + CmdTrbEvalu.SlotId = UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "Evaluate context\n")); + Status = XhcCmdTransfer ( + Xhc, + (TRB *) (UINTN) &CmdTrbEvalu, + XHC_GENERIC_TIMEOUT, + (TRB **) (UINTN) &EvtTrb + ); + ASSERT (!EFI_ERROR(Status)); + + return Status; +} + +/** + Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param PortNum The total number of downstream port supported by the hub. + @param TTT The TT think time of the hub device. + @param MTT The multi-TT of the hub device. + + @retval EFI_SUCCESS Successfully configure the hub device's slot context. + +**/ +EFI_STATUS +XhcConfigHubContext ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 PortNum, + IN UINT8 TTT, + IN UINT8 MTT + ) +{ + EFI_STATUS Status; + + EVT_TRB_COMMAND *EvtTrb; + INPUT_CONTEXT *InputContext; + DEVICE_CONTEXT *OutputDevContxt; + CMD_CFG_ED CmdTrbCfgEP; + + ASSERT (UsbDevContext[SlotId].SlotId != 0); + InputContext = UsbDevContext[SlotId].InputContext; + OutputDevContxt = UsbDevContext[SlotId].OutputDevContxt; + + // + // 4.6.7 Evaluate Context + // + ZeroMem (InputContext, sizeof (INPUT_CONTEXT)); + + InputContext->InputControlContext.Dword2 |= BIT0; + + // + // Copy the slot context from OutputContext to Input context + // + CopyMem(&(InputContext->Slot), &(OutputDevContxt->Slot), sizeof (SLOT_CONTEXT)); + InputContext->Slot.Hub = 1; + InputContext->Slot.PortNum = PortNum; + InputContext->Slot.TTT = TTT; + InputContext->Slot.MTT = MTT; + + ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP)); + CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (InputContext); + CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (InputContext); + CmdTrbCfgEP.CycleBit = 1; + CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT; + CmdTrbCfgEP.SlotId = UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "Configure Hub Slot Context\n")); + Status = XhcCmdTransfer ( + Xhc, + (TRB *) (UINTN) &CmdTrbCfgEP, + XHC_GENERIC_TIMEOUT, + (TRB **) (UINTN) &EvtTrb + ); + ASSERT (!EFI_ERROR(Status)); + + return Status; +} + diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.h b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.h new file mode 100644 index 0000000000..64f6f79cd8 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.h @@ -0,0 +1,1117 @@ +/** @file + + This file contains the definition for XHCI host controller schedule routines. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_XHCI_SCHED_H_ +#define _EFI_XHCI_SCHED_H_ + +#define XHC_URB_SIG SIGNATURE_32 ('U', 'S', 'B', 'R') + +// +// Transfer types, used in URB to identify the transfer type +// +#define XHC_CTRL_TRANSFER 0x01 +#define XHC_BULK_TRANSFER 0x02 +#define XHC_INT_TRANSFER_SYNC 0x04 +#define XHC_INT_TRANSFER_ASYNC 0x08 +#define XHC_INT_ONLY_TRANSFER_ASYNC 0x10 + +// +// 6.4.6 TRB Types +// +#define TRB_TYPE_NORMAL 1 +#define TRB_TYPE_SETUP_STAGE 2 +#define TRB_TYPE_DATA_STAGE 3 +#define TRB_TYPE_STATUS_STAGE 4 +#define TRB_TYPE_ISOCH 5 +#define TRB_TYPE_LINK 6 +#define TRB_TYPE_EVENT_DATA 7 +#define TRB_TYPE_NO_OP 8 +#define TRB_TYPE_EN_SLOT 9 +#define TRB_TYPE_DIS_SLOT 10 +#define TRB_TYPE_ADDRESS_DEV 11 +#define TRB_TYPE_CON_ENDPOINT 12 +#define TRB_TYPE_EVALU_CONTXT 13 +#define TRB_TYPE_RESET_ENDPOINT 14 +#define TRB_TYPE_STOP_ENDPOINT 15 +#define TRB_TYPE_SET_TR_DEQUE 16 +#define TRB_TYPE_RESET_DEV 17 +#define TRB_TYPE_GET_PORT_BANW 21 +#define TRB_TYPE_FORCE_HEADER 22 +#define TRB_TYPE_NO_OP_COMMAND 23 +#define TRB_TYPE_TRANS_EVENT 32 +#define TRB_TYPE_COMMAND_COMPLT_EVENT 33 +#define TRB_TYPE_PORT_STATUS_CHANGE_EVENT 34 +#define TRB_TYPE_HOST_CONTROLLER_EVENT 37 +#define TRB_TYPE_DEVICE_NOTIFI_EVENT 38 +#define TRB_TYPE_MFINDEX_WRAP_EVENT 39 + +// +// Endpoint Type (EP Type). +// +#define ED_NOT_VALID 0 +#define ED_ISOCH_OUT 1 +#define ED_BULK_OUT 2 +#define ED_INTERRUPT_OUT 3 +#define ED_CONTROL_BIDIR 4 +#define ED_ISOCH_IN 5 +#define ED_BULK_IN 6 +#define ED_INTERRUPT_IN 7 + +// +// 6.4.5 TRB Completion Codes +// +#define TRB_COMPLETION_INVALID 0 +#define TRB_COMPLETION_SUCCESS 1 +#define TRB_COMPLETION_DATA_BUFFER_ERROR 2 +#define TRB_COMPLETION_BABBLE_ERROR 3 +#define TRB_COMPLETION_USB_TRANSACTION_ERROR 4 +#define TRB_COMPLETION_TRB_ERROR 5 +#define TRB_COMPLETION_STALL_ERROR 6 +#define TRB_COMPLETION_SHORT_PACKET 13 + +// +// USB device RouteChart record +// +typedef union _USB_DEV_TOPOLOGY { + UINT32 Dword; + struct { + UINT32 RouteString:20; ///< The tier concatenation of down stream port + UINT32 RootPortNum:8; ///< The root port number of the chain + UINT32 TierNum:4; ///< The Tier the device reside + } Field; +} USB_DEV_ROUTE; + +// +// Endpoint address and its capabilities +// +typedef struct _USB_ENDPOINT { + UINT8 DevAddr; + UINT8 EpAddr; + EFI_USB_DATA_DIRECTION Direction; + UINT8 DevSpeed; + UINTN MaxPacket; + UINTN Type; +} USB_ENDPOINT; + +// +// Command TRB +// +typedef struct _TRB { + UINT32 Dword1; + UINT32 Dword2; + UINT32 Dword3; + UINT32 CycleBit:1; + UINT32 RsvdZ1:9; + UINT32 Type:6; + UINT32 RsvdZ2:16; +} TRB; + +typedef struct _TRANSFER_RING { + VOID *RingSeg0; + UINTN TrbNumber; + TRB *RingEnqueue; + TRB *RingDequeue; + UINT32 RingPCS; +} TRANSFER_RING; + +typedef struct _EVENT_RING { + UINT32 EventInterrupter; + VOID *ERSTBase; + VOID *EventRingSeg0; + UINTN TrbNumber; + TRB *EventRingEnqueue; + TRB *EventRingDequeue; + UINT32 EventRingCCS; +} EVENT_RING; + +// +// URB (Usb Request Block) contains information for all kinds of +// usb requests. +// +typedef struct _URB { + UINT32 Signature; + LIST_ENTRY UrbList; + // + // Usb Device URB related information + // + USB_ENDPOINT Ep; + EFI_USB_DEVICE_REQUEST *Request; + VOID *Data; + UINTN DataLen; + EFI_ASYNC_USB_TRANSFER_CALLBACK Callback; + VOID *Context; + // + // Execute result + // + UINT32 Result; + // + // completed data length + // + UINTN Completed; + // + // Command/Tranfer Ring info + // + TRANSFER_RING *Ring; + TRB *TrbStart; + TRB *TrbEnd; + UINTN TrbNum; + EVENT_RING *EvtRing; + TRB *EvtTrbStart; +} URB; + +// +// 5.5.2 Interrupter Register Set +// +typedef struct _INTERRUPTER_REGISTER_SET { + UINT32 InterrupterManagement; + UINT32 InterrupterModeration; + UINT32 RingSegTableSize:16; + UINT32 RsvdZ1:16; + UINT32 RsvdZ2; + UINT32 BasePtrLo; + UINT32 BasePtrHi; + UINT32 DequeLo; + UINT32 DequeHi; +} INTERRUPTER_REGISTER_SET; + +// +// Host Controller Runtime Registers +// +typedef struct _HC_RUNTIME_REGS { + UINT32 MicroframeIndex; + UINT32 RsvdZ1; + UINT64 RsvdZ2; + UINT64 RsvdZ3; + UINT64 RsvdZ4; + INTERRUPTER_REGISTER_SET IR[1]; +} HC_RUNTIME_REGS; + +// +// 6.5 Event Ring Segment Table +// The Event Ring Segment Table is used to define multi-segment Event Rings and to enable runtime +// expansion and shrinking of the Event Ring. The location of the Event Ring Segment Table is defined by the +// Event Ring Segment Table Base Address Register (5.5.2.3.2). The size of the Event Ring Segment Table +// is defined by the Event Ring Segment Table Base Size Register (5.5.2.3.1). +// +typedef struct _EVENT_RING_SEG_TABLE_ENTRY { + UINT32 PtrLo; + UINT32 PtrHi; + UINT32 RingTrbSize:16; + UINT32 RsvdZ1:16; + UINT32 RsvdZ2; +} EVENT_RING_SEG_TABLE_ENTRY; + +// +// 6.4.1.1 Normal TRB +// A Normal TRB is used in several ways; exclusively on Bulk and Interrupt Transfer Rings for normal and +// Scatter/Gather operations, to define additional data buffers for Scatter/Gather operations on Isoch Transfer +// Rings, and to define the Data stage information for Control Transfer Rings. +// +typedef struct _TRANSFER_TRB_NORMAL { + UINT32 TRBPtrLo; + UINT32 TRBPtrHi; + UINT32 Lenth:17; + UINT32 TDSize:5; + UINT32 IntTarget:10; + UINT32 CycleBit:1; + UINT32 ENT:1; + UINT32 ISP:1; + UINT32 NS:1; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 IDT:1; + UINT32 RsvdZ1:2; + UINT32 BEI:1; + UINT32 Type:6; + UINT32 RsvdZ2:16; +} TRANSFER_TRB_NORMAL; + +// +// 6.4.1.2.1 Setup Stage TRB +// A Setup Stage TRB is created by system software to initiate a USB Setup packet on a control endpoint. +// +typedef struct _TRANSFER_TRB_CONTROL_SETUP{ + UINT32 bmRequestType:8; + UINT32 bRequest:8; + UINT32 wValue:16; + + UINT32 wIndex:16; + UINT32 wLength:16; + + UINT32 Lenth:17; + UINT32 RsvdZ1:5; + UINT32 IntTarget:10; + + UINT32 CycleBit:1; + UINT32 RsvdZ2:4; + UINT32 IOC:1; + UINT32 IDT:1; + UINT32 RsvdZ3:3; + UINT32 Type:6; + UINT32 TRT:2; + UINT32 RsvdZ4:14; +} TRANSFER_TRB_CONTROL_SETUP; + +// +// 6.4.1.2.2 Data Stage TRB +// A Data Stage TRB is used generate the Data stage transaction of a USB Control transfer. +// +typedef struct _TRANSFER_TRB_CONTROL_DATA { + UINT32 TRBPtrLo; + UINT32 TRBPtrHi; + UINT32 Lenth:17; + UINT32 TDSize:5; + UINT32 IntTarget:10; + UINT32 CycleBit:1; + UINT32 ENT:1; + UINT32 ISP:1; + UINT32 NS:1; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 IDT:1; + UINT32 RsvdZ1:3; + UINT32 Type:6; + UINT32 DIR:1; + UINT32 RsvdZ2:15; +} TRANSFER_TRB_CONTROL_DATA; + +// +// 6.4.1.2.2 Data Stage TRB +// A Data Stage TRB is used generate the Data stage transaction of a USB Control transfer. +// +typedef struct _TRANSFER_TRB_CONTROL_STATUS { + UINT32 RsvdZ1; + UINT32 RsvdZ2; + UINT32 RsvdZ3:22; + UINT32 IntTarget:10; + UINT32 CycleBit:1; + UINT32 ENT:1; + UINT32 RsvdZ4:2; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 RsvdZ5:4; + UINT32 Type:6; + UINT32 DIR:1; + UINT32 RsvdZ6:15; +} TRANSFER_TRB_CONTROL_STATUS; + +// +// 6.4.2.1 Transfer Event TRB +// A Transfer Event provides the completion status associated with a Transfer TRB. Refer to section 4.11.3.1 +// for more information on the use and operation of Transfer Events. +// +typedef struct _EVT_TRB_TRANSFER { + UINT32 TRBPtrLo; + UINT32 TRBPtrHi; + UINT32 Lenth:24; + UINT32 Completcode:8; + UINT32 CycleBit:1; + UINT32 RsvdZ1:1; + UINT32 ED:1; + UINT32 RsvdZ2:7; + UINT32 Type:6; + UINT32 EndpointID:5; + UINT32 RsvdZ3:3; + UINT32 SlotId:8; +} EVT_TRB_TRANSFER; + +// +// 6.4.2.2 Command Completion Event TRB +// A Command Completion Event TRB shall be generated by the xHC when a command completes on the +// Command Ring. Refer to section 4.11.4 for more information on the use of Command Completion Events. +// +typedef struct _EVT_TRB_COMMAND { + UINT32 TRBPtrLo; + UINT32 TRBPtrHi; + UINT32 RsvdZ2:24; + UINT32 Completcode:8; + UINT32 CycleBit:1; + UINT32 RsvdZ3:9; + UINT32 Type:6; + UINT32 VFID:8; + UINT32 SlotId:8; +} EVT_TRB_COMMAND; + +// +// 6.4.2.3 Port Status Change Event TRB +// +typedef struct _EVT_TRB_PORT { + UINT32 RsvdZ1:24; + UINT32 PortID:8; + UINT32 RsvdZ2; + UINT32 RsvdZ3:24; + UINT32 Completcode:8; + UINT32 CycleBit:1; + UINT32 RsvdZ4:9; + UINT32 Type:6; + UINT32 RsvdZ5:16; +} EVT_TRB_PORT; + +// +// 6.4.3.1 No Op Command TRB +// The No Op Command TRB provides a simple means for verifying the operation of the Command Ring +// mechanisms offered by the xHCI. +// +typedef struct _CMD_TRB_NO_OP { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + UINT32 CycleBit:1; + UINT32 RsvdZ3:9; + UINT32 Type:6; + UINT32 RsvdZ4:16; +} CMD_TRB_NO_OP; + +// +// 6.4.3.2 Enable Slot Command TRB +// The Enable Slot Command TRB causes the xHC to select an available Device Slot and return the ID of the +// selected slot to the host in a Command Completion Event. +// +typedef struct _CMD_TRB_EN_SLOT { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + UINT32 CycleBit:1; + UINT32 RsvdZ3:9; + UINT32 Type:6; + UINT32 RsvdZ4:16; +} CMD_TRB_EN_SLOT; + +// +// 6.4.3.3 Disable Slot Command TRB +// The Disable Slot Command TRB releases any bandwidth assigned to the disabled slot and frees any +// internal xHC resources assigned to the slot. +// +typedef struct _CMD_TRB_DIS_SLOT { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + UINT32 CycleBit:1; + UINT32 RsvdZ3:9; + UINT32 Type:6; + UINT32 RsvdZ4:8; + UINT32 SlotId:8; +} CMD_TRB_DIS_SLOT; + +typedef struct _CMD_TRB_RESET_PORT { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + UINT32 CycleBit:1; + UINT32 RsvdZ3:8; + UINT32 Tsp:1; + UINT32 Type:6; + UINT32 Endpoint:5; + UINT32 RsvdZ4:3; + UINT32 SlotId:8; +} CMD_TRB_RESET_PORT; + +// +// 6.4.3.4 Address Device Command TRB +// The Address Device Command TRB transitions the selected Device Context from the Default to the +// Addressed state and causes the xHC to select an address for the USB device in the Default State and +// issue a SET_ADDRESS request to the USB device. +// +typedef struct _CMD_TRB_ADDR_DEV { + UINT32 PtrLo; + UINT32 PtrHi; + UINT32 RsvdZ1; + UINT32 CycleBit:1; + UINT32 RsvdZ2:8; + UINT32 BSR:1; + UINT32 Type:6; + UINT32 RsvdZ3:8; + UINT32 SlotId:8; +} CMD_TRB_ADDR_DEV; + +// +// 6.4.3.5 Configure Endpoint Command TRB +// The Configure Endpoint Command TRB evaluates the bandwidth and resource requirements of the +// endpoints selected by the command. +// +typedef struct _CMD_CFG_ED { + UINT32 PtrLo; + UINT32 PtrHi; + UINT32 RsvdZ1; + UINT32 CycleBit:1; + UINT32 RsvdZ2:8; + UINT32 DC:1; + UINT32 Type:6; + UINT32 RsvdZ3:8; + UINT32 SlotId:8; +} CMD_CFG_ED; + +// +// 6.4.3.6 Evaluate Context Command TRB +// The Evaluate Context Command TRB is used by system software to inform the xHC that the selected +// Context data structures in the Device Context have been modified by system software and that the xHC +// shall evaluate any changes +// +typedef struct _CMD_TRB_EVALU_CONTX { + UINT32 PtrLo; + UINT32 PtrHi; + UINT32 RsvdZ1; + UINT32 CycleBit:1; + UINT32 RsvdZ2:9; + UINT32 Type:6; + UINT32 RsvdZ3:8; + UINT32 SlotId:8; +} CMD_TRB_EVALU_CONTX; + +// +// 6.4.3.7 Reset Endpoint Command TRB +// The Reset Endpoint Command TRB is used by system software to reset a specified Transfer Ring +// +typedef struct _CMD_TRB_RESET_ED { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + UINT32 CycleBit:1; + UINT32 RsvdZ3:8; + UINT32 TSP:1; + UINT32 Type:6; + UINT32 EDID:5; + UINT32 RsvdZ4:3; + UINT32 SlotId:8; +} CMD_TRB_RESET_ED; + +// +// 6.4.3.8 Stop Endpoint Command TRB +// The Stop Endpoint Command TRB command allows software to stop the xHC execution of the TDs on a +// Transfer Ring and temporarily take ownership of TDs that had previously been passed to the xHC. +// +typedef struct _CMD_TRB_STOP_ED { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + UINT32 CycleBit:1; + UINT32 RsvdZ3:9; + UINT32 Type:6; + UINT32 EDID:5; + UINT32 RsvdZ4:2; + UINT32 SP:1; + UINT32 SlotId:8; +} CMD_TRB_STOP_ED; + +// +// 6.4.3.9 Set TR Dequeue Pointer Command TRB +// The Set TR Dequeue Pointer Command TRB is used by system software to modify the TR Dequeue +// Pointer and DCS fields of an Endpoint or Stream Context. +// +typedef struct _CMD_SET_TR_DEQ { + UINT32 PtrLo; + UINT32 PtrHi; + UINT32 RsvdZ1:16; + UINT32 StreamID:16; + UINT32 CycleBit:1; + UINT32 RsvdZ2:9; + UINT32 Type:6; + UINT32 Endpoint:5; + UINT32 RsvdZ3:3; + UINT32 SlotId:8; +} CMD_SET_TR_DEQ; + +// +// A Link TRB provides support for non-contiguous TRB Rings. +// +typedef struct _LNK_TRB { + UINT32 PtrLo; + UINT32 PtrHi; + UINT32 RsvdZ1:22; + UINT32 InterTarget:10; + UINT32 CycleBit:1; + UINT32 TC:1; + UINT32 RsvdZ2:2; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 RsvdZ3:4; + UINT32 Type:6; + UINT32 RsvdZ4:16; +} LNK_TRB; + +// +// A Link TRB provides support for non-contiguous TRB Rings. +// +typedef struct _NO_OP_TRB { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + UINT32 CycleBit:1; + UINT32 RsvdZ3:9; + UINT32 Type:6; + UINT32 RsvdZ4:16; +} CMD_NO_OP_TRB; + +// +// 6.2.2 Slot Context +// +typedef struct _SLOT_CONTEXT { + UINT32 RouteStr:20; + UINT32 Speed:4; + UINT32 RsvdZ1:1; + UINT32 MTT:1; + UINT32 Hub:1; + UINT32 ContextEntries:5; + + UINT32 MaxExitLatency:16; + UINT32 RootHubPortNum:8; + UINT32 PortNum:8; + + UINT32 TTHubSlotId:8; + UINT32 TTPortNum:8; + UINT32 TTT:2; + UINT32 RsvdZ2:4; + UINT32 InterTarget:10; + + UINT32 DeviceAddress:8; + UINT32 RsvdZ3:19; + UINT32 SlotState:5; + + UINT32 RsvdZ4; + UINT32 RsvdZ5; + UINT32 RsvdZ6; + UINT32 RsvdZ7; +} SLOT_CONTEXT; + +// +// 6.2.3 Endpoint Context +// +typedef struct _ENDPOINT_CONTEXT { + UINT32 EPState:3; + UINT32 RsvdZ1:5; + UINT32 Mult:2; + UINT32 MaxPStreams:5; + UINT32 LSA:1; + UINT32 Interval:8; + UINT32 RsvdZ2:8; + + UINT32 RsvdZ3:1; + UINT32 CErr:2; + UINT32 EPType:3; + UINT32 RsvdZ4:1; + UINT32 HID:1; + UINT32 MaxBurstSize:8; + UINT32 MaxPacketSize:16; + + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 AverageTRBLength:16; + UINT32 MaxESITPayload:16; + + UINT32 RsvdZ5; + UINT32 RsvdZ6; + UINT32 RsvdZ7; +} ENDPOINT_CONTEXT; + +// +// 6.2.5.1 Input Control Context +// +typedef struct _INPUT_CONTRL_CONTEXT { + UINT32 Dword1; + UINT32 Dword2; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + UINT32 RsvdZ3; + UINT32 RsvdZ4; + UINT32 RsvdZ5; + UINT32 RsvdZ6; +} INPUT_CONTRL_CONTEXT; + +// +// 6.2.1 Device Context +// +typedef struct _DEVICE_CONTEXT { + SLOT_CONTEXT Slot; + ENDPOINT_CONTEXT EP[31]; +} DEVICE_CONTEXT; + +// +// 6.2.5 Input Context +// +typedef struct _INPUT_CONTEXT { + INPUT_CONTRL_CONTEXT InputControlContext; + SLOT_CONTEXT Slot; + ENDPOINT_CONTEXT EP[31]; +} INPUT_CONTEXT; + +/** + Initialize the XHCI host controller for schedule. + + @param Xhc The XHCI device to be initialized. + +**/ +VOID +XhcInitSched ( + IN USB_XHCI_DEV *Xhc + ); + +/** + Free the resouce allocated at initializing schedule. + + @param Xhc The XHCI device. + +**/ +VOID +XhcFreeSched ( + IN USB_XHCI_DEV *Xhc + ); + +/** + Ring the door bell to notify XHCI there is a transaction to be executed through URB. + + @param Xhc The XHCI device. + @param Urb The URB to be rung. + + @retval EFI_SUCCESS Successfully ring the door bell. + +**/ +EFI_STATUS +RingIntTransferDoorBell ( + IN USB_XHCI_DEV *Xhc, + IN URB *Urb + ); + +/** + Execute the transfer by polling the URB. This is a synchronous operation. + + @param Xhc The XHCI device. + @param CmdTransfer The executed URB is for cmd transfer or not. + @param Urb The URB to execute. + @param TimeOut The time to wait before abort, in millisecond. + + @return EFI_DEVICE_ERROR The transfer failed due to transfer error. + @return EFI_TIMEOUT The transfer failed due to time out. + @return EFI_SUCCESS The transfer finished OK. + +**/ +EFI_STATUS +XhcExecTransfer ( + IN USB_XHCI_DEV *Xhc, + IN BOOLEAN CmdTransfer, + IN URB *Urb, + IN UINTN TimeOut + ); + +/** + Delete a single asynchronous interrupt transfer for + the device and endpoint. + + @param Xhc The XHCI device. + @param DevAddr The address of the target device. + @param EpNum The endpoint of the target. + + @retval EFI_SUCCESS An asynchronous transfer is removed. + @retval EFI_NOT_FOUND No transfer for the device is found. + +**/ +EFI_STATUS +XhciDelAsyncIntTransfer ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 DevAddr, + IN UINT8 EpNum + ); + +/** + Remove all the asynchronous interrupt transfers. + + @param Xhc The XHCI device. + +**/ +VOID +XhciDelAllAsyncIntTransfers ( + IN USB_XHCI_DEV *Xhc + ); + +/** + Set Bios Ownership + + @param Xhc The XHCI device. + +**/ +VOID +XhcSetBiosOwnership ( + IN USB_XHCI_DEV *Xhc + ); + +/** + Clear Bios Ownership + + @param Xhc The XHCI device. + +**/ +VOID +XhcClearBiosOwnership ( + IN USB_XHCI_DEV *Xhc + ); + +/** + Find out the slot id according to device address assigned by XHCI's Address_Device cmd. + + @param DevAddr The device address of the target device. + + @return The slot id used by the device. + +**/ +UINT8 +XhcDevAddrToSlotId ( + IN UINT8 DevAddr + ); + +/** + Find out the slot id according to the device's route string. + + @param RouteString The route string described the device location. + + @return The slot id used by the device. + +**/ +UINT8 +EFIAPI +XhcRouteStringToSlotId ( + IN USB_DEV_ROUTE RouteString + ); + +/** + Calculate the device context index by endpoint address and direction. + + @param EpAddr The target endpoint number. + @param Direction The direction of the target endpoint. + + @return The device context index of endpoint. + +**/ +UINT8 +XhcEndpointToDci ( + IN UINT8 EpAddr, + IN UINT8 Direction + ); + +/** + Ring the door bell to notify XHCI there is a transaction to be executed. + + @param Xhc The XHCI device. + @param SlotId The slot id of the target device. + @param Dci The device context index of the target slot or endpoint. + + @retval EFI_SUCCESS Successfully ring the door bell. + +**/ +EFI_STATUS +EFIAPI +XhcRingDoorBell ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 Dci + ); + +/** + Interrupt transfer periodic check handler. + + @param Event Interrupt event. + @param Context Pointer to USB_XHCI_DEV. + +**/ +VOID +EFIAPI +XhcMonitorAsyncRequests ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Monitor the port status change. Enable/Disable device slot if there is a device attached/detached. + + @param Xhc The XHCI device. + @param ParentRouteChart The route string pointed to the parent device if it exists. + @param Port The port to be polled. + @param PortState The port state. + + @retval EFI_SUCCESS Successfully enable/disable device slot according to port state. + @retval Others Should not appear. + +**/ +EFI_STATUS +EFIAPI +XhcPollPortStatusChange ( + IN USB_XHCI_DEV* Xhc, + IN USB_DEV_ROUTE ParentRouteChart, + IN UINT8 Port, + IN EFI_USB_PORT_STATUS *PortState + ); + +/** + Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param PortNum The total number of downstream port supported by the hub. + @param TTT The TT think time of the hub device. + @param MTT The multi-TT of the hub device. + + @retval EFI_SUCCESS Successfully configure the hub device's slot context. + +**/ +EFI_STATUS +XhcConfigHubContext ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 PortNum, + IN UINT8 TTT, + IN UINT8 MTT + ); + +/** + Configure all the device endpoints through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param ConfigDesc The pointer to the usb device configuration descriptor. + + @retval EFI_SUCCESS Successfully configure all the device endpoints. + +**/ +EFI_STATUS +EFIAPI +XhcSetConfigCmd ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN USB_CONFIG_DESCRIPTOR *ConfigDesc + ); + +/** + Find out the actual device address according to the requested device address from UsbBus. + + @param BusDevAddr The requested device address by UsbBus upper driver. + + @return The actual device address assigned to the device. + +**/ +UINT8 +EFIAPI +XhcBusDevAddrToSlotId ( + IN UINT8 BusDevAddr + ); + +/** + Assign and initialize the device slot for a new device. + + @param Xhc The XHCI device. + @param ParentRouteChart The route string pointed to the parent device. + @param ParentPort The port at which the device is located. + @param RouteChart The route string pointed to the device. + @param DeviceSpeed The device speed. + + @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it. + +**/ +EFI_STATUS +EFIAPI +XhcInitializeDeviceSlot ( + IN USB_XHCI_DEV *Xhc, + IN USB_DEV_ROUTE ParentRouteChart, + IN UINT16 ParentPort, + IN USB_DEV_ROUTE RouteChart, + IN UINT8 DeviceSpeed + ); + +/** + Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be evaluated. + @param MaxPacketSize The max packet size supported by the device control transfer. + + @retval EFI_SUCCESS Successfully evaluate the device endpoint 0. + +**/ +EFI_STATUS +EFIAPI +XhcEvaluateContext ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 SlotId, + IN UINT32 MaxPacketSize + ); + +/** + Disable the specified device slot. + + @param Xhc The XHCI device. + @param SlotId The slot id to be disabled. + + @retval EFI_SUCCESS Successfully disable the device slot. + +**/ +EFI_STATUS +EFIAPI +XhcDisableSlotCmd ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 SlotId + ); + +/** + Synchronize the specified transfer ring to update the enqueue and dequeue pointer. + + @param Xhc The XHCI device. + @param TrsRing The transfer ring to sync. + + @retval EFI_SUCCESS The transfer ring is synchronized successfully. + +**/ +EFI_STATUS +EFIAPI +XhcSyncTrsRing ( + IN USB_XHCI_DEV *Xhc, + TRANSFER_RING *TrsRing + ); + +/** + Synchronize the specified event ring to update the enqueue and dequeue pointer. + + @param Xhc The XHCI device. + @param EvtRing The event ring to sync. + + @retval EFI_SUCCESS The event ring is synchronized successfully. + +**/ +EFI_STATUS +EFIAPI +XhcSyncEventRing ( + IN USB_XHCI_DEV *Xhc, + EVENT_RING *EvtRing + ); + +/** + Check if there is a new generated event. + + @param Xhc The XHCI device. + @param EvtRing The event ring to check. + @param NewEvtTrb The new event TRB found. + + @retval EFI_SUCCESS Found a new event TRB at the event ring. + @retval EFI_NOT_READY The event ring has no new event. + +**/ +EFI_STATUS +EFIAPI +XhcCheckNewEvent ( + IN USB_XHCI_DEV *Xhc, + IN EVENT_RING *EvtRing, + OUT TRB **NewEvtTrb + ); + +/** + Create XHCI transfer ring. + + @param Xhc The XHCI device. + @param TrbNum The number of TRB in the ring. + @param TransferRing The created transfer ring. + +**/ +VOID +CreateTransferRing ( + IN USB_XHCI_DEV *Xhc, + IN UINTN TrbNum, + OUT TRANSFER_RING *TransferRing + ); + +/** + Create XHCI event ring. + + @param Xhc The XHCI device. + @param EventInterrupter The interrupter of event. + @param EventRing The created event ring. + +**/ +VOID +CreateEventRing ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 EventInterrupter, + OUT EVENT_RING *EventRing + ); + +/** + System software shall use a Reset Endpoint Command (section 4.11.4.7) to remove the Halted + condition in the xHC. After the successful completion of the Reset Endpoint Command, the Endpoint + Context is transitioned from the Halted to the Stopped state and the Transfer Ring of the endpoint is + reenabled. The next write to the Doorbell of the Endpoint will transition the Endpoint Context from the + Stopped to the Running state. + + @param Xhc The XHCI device. + @param Urb The urb which makes the endpoint halted. + + @retval EFI_SUCCESS The recovery is successful. + @retval Others Failed to recovery halted endpoint. + +**/ +EFI_STATUS +EFIAPI +XhcRecoverHaltedEndpoint ( + IN USB_XHCI_DEV *Xhc, + IN URB *Urb + ); + +/** + Create a new URB for a new transaction. + + @param Xhc The XHCI device + @param DevAddr The device address + @param EpAddr Endpoint addrress + @param DevSpeed The device speed + @param MaxPacket The max packet length of the endpoint + @param Type The transaction type + @param Request The standard USB request for control transfer + @param Data The user data to transfer + @param DataLen The length of data buffer + @param Callback The function to call when data is transferred + @param Context The context to the callback + + @return Created URB or NULL + +**/ +URB* +XhcCreateUrb ( + IN USB_XHCI_DEV *Xhc, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN UINTN Type, + IN EFI_USB_DEVICE_REQUEST *Request, + IN VOID *Data, + IN UINTN DataLen, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context + ); + +/** + Create a transfer TRB. + + @param Xhc The XHCI device + @param Urb The urb used to construct the transfer TRB. + + @return Created TRB or NULL + +**/ +EFI_STATUS +XhcCreateTransferTrb ( + IN USB_XHCI_DEV *Xhc, + IN URB *Urb + ); + +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.c index 64aa9d9545..b592913bd3 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.c +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.c @@ -45,6 +45,7 @@ EFI_DRIVER_BINDING_PROTOCOL mUsbBusDriverBinding = { NULL }; +UINT16 mMaxUsbDeviceNum = USB_MAX_DEVICES; /** USB_IO function to execute a control transfer. This @@ -111,7 +112,7 @@ UsbIoControlTransfer ( // Clear TT buffer when CTRL/BULK split transaction failes // Clear the TRANSLATOR TT buffer, not parent's buffer // - ASSERT (Dev->Translator.TranslatorHubAddress < USB_MAX_DEVICES); + ASSERT (Dev->Translator.TranslatorHubAddress < mMaxUsbDeviceNum); if (Dev->Translator.TranslatorHubAddress != 0) { UsbHubCtrlClearTTBuffer ( Dev->Bus->Devices[Dev->Translator.TranslatorHubAddress], @@ -284,7 +285,7 @@ UsbIoBulkTransfer ( // Clear TT buffer when CTRL/BULK split transaction failes. // Clear the TRANSLATOR TT buffer, not parent's buffer // - ASSERT (Dev->Translator.TranslatorHubAddress < USB_MAX_DEVICES); + ASSERT (Dev->Translator.TranslatorHubAddress < mMaxUsbDeviceNum); if (Dev->Translator.TranslatorHubAddress != 0) { UsbHubCtrlClearTTBuffer ( Dev->Bus->Devices[Dev->Translator.TranslatorHubAddress], @@ -966,6 +967,16 @@ UsbBusBuildProtocol ( goto CLOSE_HC; } + if (!EFI_ERROR (Status)) { + if (UsbBus->Usb2Hc->MajorRevision == 0x3) { + // + // The EFI_USB2_HC_PROTOCOL is produced for XHCI support. + // Then its max supported devices are 256. + // + mMaxUsbDeviceNum = 256; + } + } + UsbHcReset (UsbBus, EFI_USB_HC_RESET_GLOBAL); UsbHcSetState (UsbBus, EfiUsbHcStateOperational); @@ -1011,6 +1022,7 @@ UsbBusBuildProtocol ( 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; @@ -1434,7 +1446,7 @@ UsbBusControllerDriverStop ( mUsbRootHubApi.Release (RootIf); - for (Index = 1; Index < USB_MAX_DEVICES; Index++) { + for (Index = 1; Index < mMaxUsbDeviceNum; Index++) { if (Bus->Devices[Index] != NULL) { UsbRemoveDevice (Bus->Devices[Index]); } diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h index c41c1272fc..ce041b9f79 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h @@ -167,7 +167,7 @@ struct _USB_DEVICE { // UINT8 Speed; UINT8 Address; - UINT8 MaxPacket0; + UINT32 MaxPacket0; // // The device's descriptors and its configuration @@ -189,6 +189,7 @@ struct _USB_DEVICE { UINT8 ParentAddr; USB_INTERFACE *ParentIf; UINT8 ParentPort; // Start at 0 + UINT8 Tier; }; // @@ -249,7 +250,7 @@ struct _USB_BUS { // An array of device that is on the bus. Devices[0] is // for root hub. Device with address i is at Devices[i]. // - USB_DEVICE *Devices[USB_MAX_DEVICES]; + USB_DEVICE *Devices[256]; // // USB Bus driver need to control the recursive connect policy of the bus, only those wanted @@ -746,6 +747,7 @@ UsbBusControllerDriverStop ( IN EFI_HANDLE *ChildHandleBuffer ); +extern UINT16 mMaxUsbDeviceNum; extern EFI_USB_IO_PROTOCOL mUsbIoProtocol; extern EFI_DRIVER_BINDING_PROTOCOL mUsbBusDriverBinding; extern EFI_COMPONENT_NAME_PROTOCOL mUsbBusComponentName; diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c index 55fe5c873c..529b136188 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c @@ -527,12 +527,14 @@ UsbGetMaxPacketSize0 ( // Get the first 8 bytes of the device descriptor which contains // max packet size for endpoint 0, which is at least 8. // - UsbDev->MaxPacket0 = 8; - for (Index = 0; Index < 3; Index++) { Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_DEVICE, 0, 0, &DevDesc, 8); if (!EFI_ERROR (Status)) { + if ((DevDesc.BcdUSB == 0x0300) && (DevDesc.MaxPacketSize0 == 9)) { + UsbDev->MaxPacket0 = 1 << 9; + return EFI_SUCCESS; + } UsbDev->MaxPacket0 = DevDesc.MaxPacketSize0; return EFI_SUCCESS; } diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c index a6dc1c0c29..b004212fa2 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c @@ -2,7 +2,7 @@ Usb bus enumeration support. -Copyright (c) 2007 - 2008, Intel Corporation. All rights reserved.
+Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -15,7 +15,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "UsbBus.h" - /** Return the endpoint descriptor in this interface. @@ -234,6 +233,7 @@ UsbCreateDevice ( Device->ParentAddr = ParentIf->Device->Address; Device->ParentIf = ParentIf; Device->ParentPort = ParentPort; + Device->Tier = ParentIf->Device->Tier + 1; return Device; } @@ -540,7 +540,7 @@ UsbRemoveDevice ( USB_BUS *Bus; USB_DEVICE *Child; EFI_STATUS Status; - UINT8 Index; + UINTN Index; Bus = Device->Bus; @@ -548,7 +548,7 @@ UsbRemoveDevice ( // Remove all the devices on its downstream ports. Search from devices[1]. // Devices[0] is the root hub. // - for (Index = 1; Index < USB_MAX_DEVICES; Index++) { + for (Index = 1; Index < mMaxUsbDeviceNum; Index++) { Child = Bus->Devices[Index]; if ((Child == NULL) || (Child->ParentAddr != Device->Address)) { @@ -567,7 +567,7 @@ UsbRemoveDevice ( DEBUG (( EFI_D_INFO, "UsbRemoveDevice: device %d removed\n", Device->Address)); - ASSERT (Device->Address < USB_MAX_DEVICES); + ASSERT (Device->Address < mMaxUsbDeviceNum); Bus->Devices[Device->Address] = NULL; UsbFreeDevice (Device); @@ -599,7 +599,7 @@ UsbFindChild ( // // Start checking from device 1, device 0 is the root hub // - for (Index = 1; Index < USB_MAX_DEVICES; Index++) { + for (Index = 1; Index < mMaxUsbDeviceNum; Index++) { Device = Bus->Devices[Index]; if ((Device != NULL) && (Device->ParentAddr == HubIf->Device->Address) && @@ -635,11 +635,11 @@ UsbEnumerateNewDev ( USB_DEVICE *Child; USB_DEVICE *Parent; EFI_USB_PORT_STATUS PortState; - UINT8 Address; + UINTN Address; UINT8 Config; EFI_STATUS Status; - Address = USB_MAX_DEVICES; + Address = mMaxUsbDeviceNum; Parent = HubIf->Device; Bus = Parent->Bus; HubApi = HubIf->HubApi; @@ -679,14 +679,21 @@ UsbEnumerateNewDev ( goto ON_ERROR; } - if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) { - Child->Speed = EFI_USB_SPEED_LOW; - + if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) { + DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: No device presented at port %d\n", Port)); + goto ON_ERROR; + } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_SUPER_SPEED)){ + Child->Speed = EFI_USB_SPEED_SUPER; + Child->MaxPacket0 = 512; } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { - Child->Speed = EFI_USB_SPEED_HIGH; - + Child->Speed = EFI_USB_SPEED_HIGH; + Child->MaxPacket0 = 64; + } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) { + Child->Speed = EFI_USB_SPEED_LOW; + Child->MaxPacket0 = 8; } else { - Child->Speed = EFI_USB_SPEED_FULL; + Child->Speed = EFI_USB_SPEED_FULL; + Child->MaxPacket0 = 8; } DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device is of %d speed\n", Child->Speed)); @@ -719,50 +726,50 @@ UsbEnumerateNewDev ( // a time) , and ready to respond to control transfer at EP 0. // - // - // Host sends a Get_Descriptor request to learn the max packet - // size of default pipe (only part of the device's descriptor). - // - Status = UsbGetMaxPacketSize0 (Child); - - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status)); - goto ON_ERROR; - } - - DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0)); - // // Host assigns an address to the device. Device completes the // status stage with default address, then switches to new address. // ADDRESS state. Address zero is reserved for root hub. // - for (Address = 1; Address < USB_MAX_DEVICES; Address++) { + for (Address = 1; Address < mMaxUsbDeviceNum; Address++) { if (Bus->Devices[Address] == NULL) { break; } } - if (Address == USB_MAX_DEVICES) { + if (Address == mMaxUsbDeviceNum) { DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: address pool is full for port %d\n", Port)); Status = EFI_ACCESS_DENIED; goto ON_ERROR; } + Status = UsbSetAddress (Child, (UINT8)Address); + Child->Address = (UINT8)Address; Bus->Devices[Address] = Child; - Status = UsbSetAddress (Child, Address); - Child->Address = Address; if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set device address - %r\n", Status)); goto ON_ERROR; } - + gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL); DEBUG ((EFI_D_INFO, "UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address)); + // + // Host sends a Get_Descriptor request to learn the max packet + // size of default pipe (only part of the device's descriptor). + // + Status = UsbGetMaxPacketSize0 (Child); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status)); + goto ON_ERROR; + } + + DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0)); + // // Host learns about the device's abilities by requesting device's // entire descriptions. @@ -801,7 +808,7 @@ UsbEnumerateNewDev ( return EFI_SUCCESS; ON_ERROR: - if (Address != USB_MAX_DEVICES) { + if (Address != mMaxUsbDeviceNum) { Bus->Devices[Address] = NULL; } @@ -848,12 +855,16 @@ UsbEnumeratePort ( return Status; } - if (PortState.PortChangeStatus == 0) { + // + // Only handle connection/enable/overcurrent/reset change. + // Usb super speed hub may report other changes, such as warm reset change. Ignore them. + // + if ((PortState.PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { return EFI_SUCCESS; } - DEBUG (( EFI_D_INFO, "UsbEnumeratePort: port %d state - %x, change - %x on %p\n", - Port, PortState.PortStatus, PortState.PortChangeStatus, HubIf)); + DEBUG (( EFI_D_INFO, "UsbEnumeratePort: port %d state - %02x, change - %02x on %p\n", + Port, PortState.PortChangeStatus, PortState.PortStatus, HubIf)); // // This driver only process two kinds of events now: over current and diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c index 21270b8080..2dfc5751d5 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c @@ -22,19 +22,15 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // bits determine whether hub will report the port in changed // bit maps. // -#define USB_HUB_MAP_SIZE 5 - -USB_CHANGE_FEATURE_MAP mHubFeatureMap[USB_HUB_MAP_SIZE] = { +USB_CHANGE_FEATURE_MAP mHubFeatureMap[] = { {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_RESET, EfiUsbPortResetChange} }; -#define USB_ROOT_HUB_MAP_SIZE 5 - -USB_CHANGE_FEATURE_MAP mRootHubFeatureMap[USB_ROOT_HUB_MAP_SIZE] = { +USB_CHANGE_FEATURE_MAP mRootHubFeatureMap[] = { {USB_PORT_STAT_C_CONNECTION, EfiUsbPortConnectChange}, {USB_PORT_STAT_C_ENABLE, EfiUsbPortEnableChange}, {USB_PORT_STAT_C_SUSPEND, EfiUsbPortSuspendChange}, @@ -47,7 +43,38 @@ USB_CHANGE_FEATURE_MAP mRootHubFeatureMap[USB_ROOT_HUB_MAP_SIZE] = { // is related to an interface, these requests are sent // to the control endpoint of the device. // +/** + USB hub control transfer to set the hub depth. + + @param HubDev The device of the hub. + @param Depth The depth to set. + + @retval EFI_SUCCESS Depth of the hub is set. + @retval Others Failed to set the depth. + +**/ +EFI_STATUS +UsbHubCtrlSetHubDepth ( + IN USB_DEVICE *HubDev, + IN UINT16 Depth + ) +{ + EFI_STATUS Status; + Status = UsbCtrlRequest ( + HubDev, + EfiUsbNoData, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_HUB, + USB_HUB_REQ_SET_DEPTH, + Depth, + 0, + NULL, + 0 + ); + + return Status; +} /** USB hub control transfer to clear the hub feature. @@ -173,6 +200,41 @@ UsbHubCtrlClearTTBuffer ( return Status; } +/** + Usb hub control transfer to get the super speed hub descriptor. + + @param HubDev The hub device. + @param Buf The buffer to hold the descriptor. + @param Len The length to retrieve. + + @retval EFI_SUCCESS The hub descriptor is retrieved. + @retval Others Failed to retrieve the hub descriptor. + +**/ +EFI_STATUS +UsbHubCtrlGetSuperSpeedHubDesc ( + IN USB_DEVICE *HubDev, + OUT VOID *Buf + ) +{ + EFI_STATUS Status; + + Status = EFI_INVALID_PARAMETER; + + Status = UsbCtrlRequest ( + HubDev, + EfiUsbDataIn, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_HUB, + USB_HUB_REQ_GET_DESC, + (UINT16) (USB_DESC_TYPE_HUB_SUPER_SPEED << 8), + 0, + Buf, + 32 + ); + + return Status; +} /** Usb hub control transfer to get the hub descriptor. @@ -414,19 +476,27 @@ UsbHubReadDesc ( { EFI_STATUS Status; - // - // First get the hub descriptor length - // - Status = UsbHubCtrlGetHubDesc (HubDev, HubDesc, 2); + if (HubDev->Speed == EFI_USB_SPEED_SUPER) { + // + // Get the super speed hub descriptor + // + Status = UsbHubCtrlGetSuperSpeedHubDesc (HubDev, HubDesc); + } else { - if (EFI_ERROR (Status)) { - return Status; - } + // + // First get the hub descriptor length + // + Status = UsbHubCtrlGetHubDesc (HubDev, HubDesc, 2); - // - // Get the whole hub descriptor - // - Status = UsbHubCtrlGetHubDesc (HubDev, HubDesc, HubDesc->Length); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the whole hub descriptor + // + Status = UsbHubCtrlGetHubDesc (HubDev, HubDesc, HubDesc->Length); + } return Status; } @@ -629,6 +699,7 @@ UsbHubInit ( EFI_STATUS Status; UINT8 Index; UINT8 NumEndpoints; + UINT16 Depth; // // Locate the interrupt endpoint for port change map @@ -666,6 +737,37 @@ UsbHubInit ( DEBUG (( EFI_D_INFO, "UsbHubInit: hub %d has %d ports\n", HubDev->Address,HubIf->NumOfPort)); + // + // OK, set IsHub to TRUE. Now usb bus can handle this device + // as a working HUB. If failed eariler, bus driver will not + // recognize it as a hub. Other parts of the bus should be able + // to work. + // + HubIf->IsHub = TRUE; + HubIf->HubApi = &mUsbHubApi; + HubIf->HubEp = EpDesc; + + if (HubIf->Device->Speed == EFI_USB_SPEED_SUPER) { + Depth = (UINT16)(HubIf->Device->Tier - 1); + DEBUG ((EFI_D_INFO, "UsbHubInit: Set Hub Depth as 0x%x\n", Depth)); + UsbHubCtrlSetHubDepth (HubIf->Device, Depth); + + for (Index = 0; Index < HubDesc.NumPorts; Index++) { + UsbHubCtrlSetPortFeature (HubIf->Device, Index, USB_HUB_PORT_REMOTE_WAKE_MASK); + } + } else { + // + // Feed power to all the hub ports. It should be ok + // for both gang/individual powered hubs. + // + for (Index = 0; Index < HubDesc.NumPorts; Index++) { + UsbHubCtrlSetPortFeature (HubIf->Device, Index, (EFI_USB_PORT_FEATURE) USB_HUB_PORT_POWER); + } + + gBS->Stall (HubDesc.PwrOn2PwrGood * USB_SET_PORT_POWER_STALL); + UsbHubAckHubStatus (HubIf->Device); + } + // // Create an event to enumerate the hub's port. On // @@ -712,27 +814,6 @@ UsbHubInit ( return Status; } - // - // OK, set IsHub to TRUE. Now usb bus can handle this device - // as a working HUB. If failed eariler, bus driver will not - // recognize it as a hub. Other parts of the bus should be able - // to work. - // - HubIf->IsHub = TRUE; - HubIf->HubApi = &mUsbHubApi; - HubIf->HubEp = EpDesc; - - // - // Feed power to all the hub ports. It should be ok - // for both gang/individual powered hubs. - // - for (Index = 0; Index < HubDesc.NumPorts; Index++) { - UsbHubCtrlSetPortFeature (HubIf->Device, Index, (EFI_USB_PORT_FEATURE) USB_HUB_PORT_POWER); - } - - gBS->Stall (HubDesc.PwrOn2PwrGood * USB_SET_PORT_POWER_STALL); - UsbHubAckHubStatus (HubIf->Device); - DEBUG (( EFI_D_INFO, "UsbHubInit: hub %d initialized\n", HubDev->Address)); return Status; } @@ -764,6 +845,12 @@ UsbHubGetPortStatus ( Status = UsbHubCtrlGetPortStatus (HubIf->Device, Port, PortState); + // + // Mark the USB_PORT_STAT_SUPER_SPEED bit if SuperSpeed + // + if (HubIf->Device->Speed == EFI_USB_SPEED_SUPER) { + PortState->PortStatus |= USB_PORT_STAT_SUPER_SPEED; + } return Status; } @@ -799,7 +886,7 @@ UsbHubClearPortChange ( // It may lead to extra port state report. USB bus should // be able to handle this. // - for (Index = 0; Index < USB_HUB_MAP_SIZE; Index++) { + for (Index = 0; Index < sizeof (mHubFeatureMap) / sizeof (mHubFeatureMap[0]); Index++) { Map = &mHubFeatureMap[Index]; if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) { @@ -1091,7 +1178,7 @@ UsbRootHubClearPortChange ( // It may lead to extra port state report. USB bus should // be able to handle this. // - for (Index = 0; Index < USB_ROOT_HUB_MAP_SIZE; Index++) { + for (Index = 0; Index < sizeof (mRootHubFeatureMap) / sizeof (mRootHubFeatureMap[0]); Index++) { Map = &mRootHubFeatureMap[Index]; if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) { diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h index 1a6ebdd6ca..c7ee16db46 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h @@ -23,6 +23,9 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define USB_DESC_TYPE_HUB 0x29 + +#define USB_DESC_TYPE_HUB_SUPER_SPEED 0x2a + // // Hub class control transfer target // @@ -40,6 +43,9 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define USB_HUB_REQ_RESET_TT 9 #define USB_HUB_REQ_GET_TT_STATE 10 #define USB_HUB_REQ_STOP_TT 11 + +#define USB_HUB_REQ_SET_DEPTH 12 + // // USB hub class feature selector // @@ -50,6 +56,9 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define USB_HUB_PORT_SUSPEND 2 #define USB_HUB_PORT_OVER_CURRENT 3 #define USB_HUB_PORT_RESET 4 + +#define USB_HUB_PORT_LINK_STATE 5 + #define USB_HUB_PORT_POWER 8 #define USB_HUB_PORT_LOW_SPEED 9 #define USB_HUB_C_PORT_CONNECT 16 @@ -59,6 +68,17 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define USB_HUB_C_PORT_RESET 20 #define USB_HUB_PORT_TEST 21 #define USB_HUB_PORT_INDICATOR 22 + +#define USB_HUB_C_PORT_LINK_STATE 25 +#define USB_HUB_PORT_REMOTE_WAKE_MASK 27 +#define USB_HUB_BH_PORT_RESET 28 +#define USB_HUB_C_BH_PORT_RESET 29 + +// +// Constant value for Port Status & Port Change Status of SuperSpeed port +// +#define USB_SS_PORT_STAT_C_BH_RESET 0x0020 +#define USB_SS_PORT_STAT_C_PORT_LINK_STATE 0x0040 // // USB hub power control method. In gang power control // @@ -94,6 +114,19 @@ typedef struct { UINT8 HubContrCurrent; UINT8 Filler[16]; } EFI_USB_HUB_DESCRIPTOR; + +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 NumPorts; + UINT16 HubCharacter; + UINT8 PwrOn2PwrGood; + UINT8 HubContrCurrent; + UINT8 HubHdrDecLat; + UINT8 HubDelay; + UINT8 DeviceRemovable; +} EFI_USB_SUPER_SPEED_HUB_DESCRIPTOR; + #pragma pack() diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 67c13444d1..89fa0b405d 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -190,6 +190,7 @@ MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf + MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf MdeModulePkg/Bus/Pci/UhciPei/UhciPei.inf -- 2.39.2