From 92870c983c6d99d31f449d8dcd729090255dda49 Mon Sep 17 00:00:00 2001 From: erictian Date: Tue, 23 Aug 2011 14:36:33 +0000 Subject: 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 ++++++++++++ 9 files changed, 7810 insertions(+) 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 (limited to 'MdeModulePkg/Bus/Pci') 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 -- cgit v1.2.3