diff options
author | Guo Mang <mang.guo@intel.com> | 2016-12-22 15:55:38 +0800 |
---|---|---|
committer | Guo Mang <mang.guo@intel.com> | 2016-12-26 19:14:37 +0800 |
commit | 7f05fa00f73038b425002566d3afe6c3ade2ccdb (patch) | |
tree | 297e208d4ade33a8bb3d5d20f72c53e0d134e003 /Core/MdeModulePkg/Bus/Pci/XhciDxe | |
parent | ed2ecce34b3830562c4239093a41ba92d76d5f31 (diff) | |
download | edk2-platforms-7f05fa00f73038b425002566d3afe6c3ade2ccdb.tar.xz |
MdeModulePkg: Move to new location
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Guo Mang <mang.guo@intel.com>
Diffstat (limited to 'Core/MdeModulePkg/Bus/Pci/XhciDxe')
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.c | 224 | ||||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.h | 146 | ||||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/UsbHcMem.c | 758 | ||||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/UsbHcMem.h | 213 | ||||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c | 2250 | ||||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h | 727 | ||||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf | 76 | ||||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.uni | bin | 0 -> 2358 bytes | |||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxeExtra.uni | bin | 0 -> 1326 bytes | |||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c | 743 | ||||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h | 583 | ||||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c | 3838 | ||||
-rw-r--r-- | Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.h | 1461 |
13 files changed, 11019 insertions, 0 deletions
diff --git a/Core/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.c b/Core/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.c new file mode 100644 index 0000000000..706aa292c8 --- /dev/null +++ b/Core/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.<BR>
+
+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_INSTANCE *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/Core/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.h b/Core/MdeModulePkg/Bus/Pci/XhciDxe/ComponentName.h new file mode 100644 index 0000000000..c3c44ccdfb --- /dev/null +++ b/Core/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.<BR>
+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/Core/MdeModulePkg/Bus/Pci/XhciDxe/UsbHcMem.c b/Core/MdeModulePkg/Bus/Pci/XhciDxe/UsbHcMem.c new file mode 100644 index 0000000000..54c69d78ed --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/XhciDxe/UsbHcMem.c @@ -0,0 +1,758 @@ +/** @file
+
+ Routine procedures for memory allocate/free.
+
+Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.<BR>
+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"
+
+
+/**
+ Allocate a block of memory to be used by the buffer pool.
+
+ @param Pool The buffer pool to allocate memory for.
+ @param Pages How many pages to allocate.
+
+ @return The allocated memory block or NULL if failed.
+
+**/
+USBHC_MEM_BLOCK *
+UsbHcAllocMemBlock (
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Pages
+ )
+{
+ USBHC_MEM_BLOCK *Block;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ VOID *BufHost;
+ VOID *Mapping;
+ EFI_PHYSICAL_ADDRESS MappedAddr;
+ UINTN Bytes;
+ EFI_STATUS Status;
+
+ PciIo = Pool->PciIo;
+
+ Block = AllocateZeroPool (sizeof (USBHC_MEM_BLOCK));
+ if (Block == NULL) {
+ return NULL;
+ }
+
+ //
+ // each bit in the bit array represents USBHC_MEM_UNIT
+ // bytes of memory in the memory block.
+ //
+ ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE);
+
+ Block->BufLen = EFI_PAGES_TO_SIZE (Pages);
+ Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8);
+ Block->Bits = AllocateZeroPool (Block->BitsLen);
+
+ if (Block->Bits == NULL) {
+ gBS->FreePool (Block);
+ return NULL;
+ }
+
+ //
+ // Allocate the number of Pages of memory, then map it for
+ // bus master read and write.
+ //
+ Status = PciIo->AllocateBuffer (
+ PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ Pages,
+ &BufHost,
+ 0
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_BITARRAY;
+ }
+
+ Bytes = EFI_PAGES_TO_SIZE (Pages);
+ Status = PciIo->Map (
+ PciIo,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ BufHost,
+ &Bytes,
+ &MappedAddr,
+ &Mapping
+ );
+
+ if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (Pages))) {
+ goto FREE_BUFFER;
+ }
+
+ Block->BufHost = BufHost;
+ Block->Buf = (UINT8 *) ((UINTN) MappedAddr);
+ Block->Mapping = Mapping;
+
+ return Block;
+
+FREE_BUFFER:
+ PciIo->FreeBuffer (PciIo, Pages, BufHost);
+
+FREE_BITARRAY:
+ gBS->FreePool (Block->Bits);
+ gBS->FreePool (Block);
+ return NULL;
+}
+
+
+/**
+ Free the memory block from the memory pool.
+
+ @param Pool The memory pool to free the block from.
+ @param Block The memory block to free.
+
+**/
+VOID
+UsbHcFreeMemBlock (
+ IN USBHC_MEM_POOL *Pool,
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ ASSERT ((Pool != NULL) && (Block != NULL));
+
+ PciIo = Pool->PciIo;
+
+ //
+ // Unmap the common buffer then free the structures
+ //
+ PciIo->Unmap (PciIo, Block->Mapping);
+ PciIo->FreeBuffer (PciIo, EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost);
+
+ gBS->FreePool (Block->Bits);
+ gBS->FreePool (Block);
+}
+
+
+/**
+ Alloc some memory from the block.
+
+ @param Block The memory block to allocate memory from.
+ @param Units Number of memory units to allocate.
+
+ @return The pointer to the allocated memory. If couldn't allocate the needed memory,
+ the return value is NULL.
+
+**/
+VOID *
+UsbHcAllocMemFromBlock (
+ IN USBHC_MEM_BLOCK *Block,
+ IN UINTN Units
+ )
+{
+ UINTN Byte;
+ UINT8 Bit;
+ UINTN StartByte;
+ UINT8 StartBit;
+ UINTN Available;
+ UINTN Count;
+
+ ASSERT ((Block != 0) && (Units != 0));
+
+ StartByte = 0;
+ StartBit = 0;
+ Available = 0;
+
+ for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {
+ //
+ // If current bit is zero, the corresponding memory unit is
+ // available, otherwise we need to restart our searching.
+ // Available counts the consective number of zero bit.
+ //
+ if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) {
+ Available++;
+
+ if (Available >= Units) {
+ break;
+ }
+
+ NEXT_BIT (Byte, Bit);
+
+ } else {
+ NEXT_BIT (Byte, Bit);
+
+ Available = 0;
+ StartByte = Byte;
+ StartBit = Bit;
+ }
+ }
+
+ if (Available < Units) {
+ return NULL;
+ }
+
+ //
+ // Mark the memory as allocated
+ //
+ Byte = StartByte;
+ Bit = StartBit;
+
+ for (Count = 0; Count < Units; Count++) {
+ ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
+
+ Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | USB_HC_BIT (Bit));
+ NEXT_BIT (Byte, Bit);
+ }
+
+ return Block->BufHost + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT;
+}
+
+/**
+ Calculate the corresponding pci bus address according to the Mem parameter.
+
+ @param Pool The memory pool of the host controller.
+ @param Mem The pointer to host memory.
+ @param Size The size of the memory region.
+
+ @return The pci memory address
+
+**/
+EFI_PHYSICAL_ADDRESS
+UsbHcGetPciAddrForHostAddr (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ )
+{
+ USBHC_MEM_BLOCK *Head;
+ USBHC_MEM_BLOCK *Block;
+ UINTN AllocSize;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ UINTN Offset;
+
+ Head = Pool->Head;
+ AllocSize = USBHC_MEM_ROUND (Size);
+
+ if (Mem == NULL) {
+ return 0;
+ }
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ //
+ // scan the memory block list for the memory block that
+ // completely contains the allocated memory.
+ //
+ if ((Block->BufHost <= (UINT8 *) Mem) && (((UINT8 *) Mem + AllocSize) <= (Block->BufHost + Block->BufLen))) {
+ break;
+ }
+ }
+
+ ASSERT ((Block != NULL));
+ //
+ // calculate the pci memory address for host memory address.
+ //
+ Offset = (UINT8 *)Mem - Block->BufHost;
+ PhyAddr = (EFI_PHYSICAL_ADDRESS)(UINTN) (Block->Buf + Offset);
+ return PhyAddr;
+}
+
+/**
+ Calculate the corresponding host address according to the pci address.
+
+ @param Pool The memory pool of the host controller.
+ @param Mem The pointer to pci memory.
+ @param Size The size of the memory region.
+
+ @return The host memory address
+
+**/
+EFI_PHYSICAL_ADDRESS
+UsbHcGetHostAddrForPciAddr (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ )
+{
+ USBHC_MEM_BLOCK *Head;
+ USBHC_MEM_BLOCK *Block;
+ UINTN AllocSize;
+ EFI_PHYSICAL_ADDRESS HostAddr;
+ UINTN Offset;
+
+ Head = Pool->Head;
+ AllocSize = USBHC_MEM_ROUND (Size);
+
+ if (Mem == NULL) {
+ return 0;
+ }
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ //
+ // scan the memory block list for the memory block that
+ // completely contains the allocated memory.
+ //
+ if ((Block->Buf <= (UINT8 *) Mem) && (((UINT8 *) Mem + AllocSize) <= (Block->Buf + Block->BufLen))) {
+ break;
+ }
+ }
+
+ ASSERT ((Block != NULL));
+ //
+ // calculate the pci memory address for host memory address.
+ //
+ Offset = (UINT8 *)Mem - Block->Buf;
+ HostAddr = (EFI_PHYSICAL_ADDRESS)(UINTN) (Block->BufHost + Offset);
+ return HostAddr;
+}
+
+/**
+ Insert the memory block to the pool's list of the blocks.
+
+ @param Head The head of the memory pool's block list.
+ @param Block The memory block to insert.
+
+**/
+VOID
+UsbHcInsertMemBlockToPool (
+ IN USBHC_MEM_BLOCK *Head,
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ ASSERT ((Head != NULL) && (Block != NULL));
+ Block->Next = Head->Next;
+ Head->Next = Block;
+}
+
+
+/**
+ Is the memory block empty?
+
+ @param Block The memory block to check.
+
+ @retval TRUE The memory block is empty.
+ @retval FALSE The memory block isn't empty.
+
+**/
+BOOLEAN
+UsbHcIsMemBlockEmpty (
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < Block->BitsLen; Index++) {
+ if (Block->Bits[Index] != 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Unlink the memory block from the pool's list.
+
+ @param Head The block list head of the memory's pool.
+ @param BlockToUnlink The memory block to unlink.
+
+**/
+VOID
+UsbHcUnlinkMemBlock (
+ IN USBHC_MEM_BLOCK *Head,
+ IN USBHC_MEM_BLOCK *BlockToUnlink
+ )
+{
+ USBHC_MEM_BLOCK *Block;
+
+ ASSERT ((Head != NULL) && (BlockToUnlink != NULL));
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ if (Block->Next == BlockToUnlink) {
+ Block->Next = BlockToUnlink->Next;
+ BlockToUnlink->Next = NULL;
+ break;
+ }
+ }
+}
+
+
+/**
+ Initialize the memory management pool for the host controller.
+
+ @param PciIo The PciIo that can be used to access the host controller.
+
+ @retval EFI_SUCCESS The memory pool is initialized.
+ @retval EFI_OUT_OF_RESOURCE Fail to init the memory pool.
+
+**/
+USBHC_MEM_POOL *
+UsbHcInitMemPool (
+ IN EFI_PCI_IO_PROTOCOL *PciIo
+ )
+{
+ USBHC_MEM_POOL *Pool;
+
+ Pool = AllocatePool (sizeof (USBHC_MEM_POOL));
+
+ if (Pool == NULL) {
+ return Pool;
+ }
+
+ Pool->PciIo = PciIo;
+ Pool->Head = UsbHcAllocMemBlock (Pool, USBHC_MEM_DEFAULT_PAGES);
+
+ if (Pool->Head == NULL) {
+ gBS->FreePool (Pool);
+ Pool = NULL;
+ }
+
+ return Pool;
+}
+
+
+/**
+ Release the memory management pool.
+
+ @param Pool The USB memory pool to free.
+
+ @retval EFI_SUCCESS The memory pool is freed.
+ @retval EFI_DEVICE_ERROR Failed to free the memory pool.
+
+**/
+EFI_STATUS
+UsbHcFreeMemPool (
+ IN USBHC_MEM_POOL *Pool
+ )
+{
+ USBHC_MEM_BLOCK *Block;
+
+ ASSERT (Pool->Head != NULL);
+
+ //
+ // Unlink all the memory blocks from the pool, then free them.
+ // UsbHcUnlinkMemBlock can't be used to unlink and free the
+ // first block.
+ //
+ for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) {
+ UsbHcUnlinkMemBlock (Pool->Head, Block);
+ UsbHcFreeMemBlock (Pool, Block);
+ }
+
+ UsbHcFreeMemBlock (Pool, Pool->Head);
+ gBS->FreePool (Pool);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Allocate some memory from the host controller's memory pool
+ which can be used to communicate with host controller.
+
+ @param Pool The host controller's memory pool.
+ @param Size Size of the memory to allocate.
+
+ @return The allocated memory or NULL.
+
+**/
+VOID *
+UsbHcAllocateMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Size
+ )
+{
+ USBHC_MEM_BLOCK *Head;
+ USBHC_MEM_BLOCK *Block;
+ USBHC_MEM_BLOCK *NewBlock;
+ VOID *Mem;
+ UINTN AllocSize;
+ UINTN Pages;
+
+ Mem = NULL;
+ AllocSize = USBHC_MEM_ROUND (Size);
+ Head = Pool->Head;
+ ASSERT (Head != NULL);
+
+ //
+ // First check whether current memory blocks can satisfy the allocation.
+ //
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT);
+
+ if (Mem != NULL) {
+ ZeroMem (Mem, Size);
+ break;
+ }
+ }
+
+ if (Mem != NULL) {
+ return Mem;
+ }
+
+ //
+ // Create a new memory block if there is not enough memory
+ // in the pool. If the allocation size is larger than the
+ // default page number, just allocate a large enough memory
+ // block. Otherwise allocate default pages.
+ //
+ if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) {
+ Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;
+ } else {
+ Pages = USBHC_MEM_DEFAULT_PAGES;
+ }
+
+ NewBlock = UsbHcAllocMemBlock (Pool, Pages);
+
+ if (NewBlock == NULL) {
+ DEBUG ((EFI_D_ERROR, "UsbHcAllocateMem: failed to allocate block\n"));
+ return NULL;
+ }
+
+ //
+ // Add the new memory block to the pool, then allocate memory from it
+ //
+ UsbHcInsertMemBlockToPool (Head, NewBlock);
+ Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT);
+
+ if (Mem != NULL) {
+ ZeroMem (Mem, Size);
+ }
+
+ return Mem;
+}
+
+
+/**
+ Free the allocated memory back to the memory pool.
+
+ @param Pool The memory pool of the host controller.
+ @param Mem The memory to free.
+ @param Size The size of the memory to free.
+
+**/
+VOID
+UsbHcFreeMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ )
+{
+ USBHC_MEM_BLOCK *Head;
+ USBHC_MEM_BLOCK *Block;
+ UINT8 *ToFree;
+ UINTN AllocSize;
+ UINTN Byte;
+ UINTN Bit;
+ UINTN Count;
+
+ Head = Pool->Head;
+ AllocSize = USBHC_MEM_ROUND (Size);
+ ToFree = (UINT8 *) Mem;
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ //
+ // scan the memory block list for the memory block that
+ // completely contains the memory to free.
+ //
+ if ((Block->BufHost <= ToFree) && ((ToFree + AllocSize) <= (Block->BufHost + Block->BufLen))) {
+ //
+ // compute the start byte and bit in the bit array
+ //
+ Byte = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) / 8;
+ Bit = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) % 8;
+
+ //
+ // reset associated bits in bit arry
+ //
+ for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) {
+ ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
+
+ Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ USB_HC_BIT (Bit));
+ NEXT_BIT (Byte, Bit);
+ }
+
+ break;
+ }
+ }
+
+ //
+ // If Block == NULL, it means that the current memory isn't
+ // in the host controller's pool. This is critical because
+ // the caller has passed in a wrong memory point
+ //
+ ASSERT (Block != NULL);
+
+ //
+ // Release the current memory block if it is empty and not the head
+ //
+ if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) {
+ UsbHcUnlinkMemBlock (Head, Block);
+ UsbHcFreeMemBlock (Pool, Block);
+ }
+
+ return ;
+}
+
+/**
+ Allocates pages at a specified alignment that are suitable for an EfiPciIoOperationBusMasterCommonBuffer mapping.
+
+ If Alignment is not a power of two and Alignment is not zero, then ASSERT().
+
+ @param PciIo The PciIo that can be used to access the host controller.
+ @param Pages The number of pages to allocate.
+ @param Alignment The requested alignment of the allocation. Must be a power of two.
+ @param HostAddress The system memory address to map to the PCI controller.
+ @param DeviceAddress The resulting map address for the bus master PCI controller to
+ use to access the hosts HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS Success to allocate aligned pages.
+ @retval EFI_INVALID_PARAMETER Pages or Alignment is not valid.
+ @retval EFI_OUT_OF_RESOURCES Do not have enough resources to allocate memory.
+
+
+**/
+EFI_STATUS
+UsbHcAllocateAlignedPages (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINTN Pages,
+ IN UINTN Alignment,
+ OUT VOID **HostAddress,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ VOID *Memory;
+ UINTN AlignedMemory;
+ UINTN AlignmentMask;
+ UINTN UnalignedPages;
+ UINTN RealPages;
+ UINTN Bytes;
+
+ //
+ // Alignment must be a power of two or zero.
+ //
+ ASSERT ((Alignment & (Alignment - 1)) == 0);
+
+ if ((Alignment & (Alignment - 1)) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Pages == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if (Alignment > EFI_PAGE_SIZE) {
+ //
+ // Calculate the total number of pages since alignment is larger than page size.
+ //
+ AlignmentMask = Alignment - 1;
+ RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment);
+ //
+ // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow.
+ //
+ ASSERT (RealPages > Pages);
+
+ Status = PciIo->AllocateBuffer (
+ PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ Pages,
+ &Memory,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask;
+ UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN) Memory);
+ if (UnalignedPages > 0) {
+ //
+ // Free first unaligned page(s).
+ //
+ Status = PciIo->FreeBuffer (PciIo, UnalignedPages, Memory);
+ ASSERT_EFI_ERROR (Status);
+ }
+ Memory = (VOID *)(UINTN)(AlignedMemory + EFI_PAGES_TO_SIZE (Pages));
+ UnalignedPages = RealPages - Pages - UnalignedPages;
+ if (UnalignedPages > 0) {
+ //
+ // Free last unaligned page(s).
+ //
+ Status = PciIo->FreeBuffer (PciIo, UnalignedPages, Memory);
+ ASSERT_EFI_ERROR (Status);
+ }
+ } else {
+ //
+ // Do not over-allocate pages in this case.
+ //
+ Status = PciIo->AllocateBuffer (
+ PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ Pages,
+ &Memory,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ AlignedMemory = (UINTN) Memory;
+ }
+
+ Bytes = EFI_PAGES_TO_SIZE (Pages);
+ Status = PciIo->Map (
+ PciIo,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ (VOID *) AlignedMemory,
+ &Bytes,
+ DeviceAddress,
+ Mapping
+ );
+
+ if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (Pages))) {
+ Status = PciIo->FreeBuffer (PciIo, Pages, (VOID *) AlignedMemory);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *HostAddress = (VOID *) AlignedMemory;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Frees memory that was allocated with UsbHcAllocateAlignedPages().
+
+ @param PciIo The PciIo that can be used to access the host controller.
+ @param HostAddress The system memory address to map to the PCI controller.
+ @param Pages The number of 4 KB pages to free.
+ @param Mapping The mapping value returned from Map().
+
+**/
+VOID
+UsbHcFreeAlignedPages (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN VOID *HostAddress,
+ IN UINTN Pages,
+ VOID *Mapping
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (Pages != 0);
+
+ Status = PciIo->Unmap (PciIo, Mapping);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = PciIo->FreeBuffer (
+ PciIo,
+ Pages,
+ HostAddress
+ );
+ ASSERT_EFI_ERROR (Status);
+}
diff --git a/Core/MdeModulePkg/Bus/Pci/XhciDxe/UsbHcMem.h b/Core/MdeModulePkg/Bus/Pci/XhciDxe/UsbHcMem.h new file mode 100644 index 0000000000..1907685ddd --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/XhciDxe/UsbHcMem.h @@ -0,0 +1,213 @@ +/** @file
+
+ This file contains the definination for host controller memory management routines.
+
+Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+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_MEM_H_
+#define _EFI_XHCI_MEM_H_
+
+#define USB_HC_BIT(a) ((UINTN)(1 << (a)))
+
+#define USB_HC_BIT_IS_SET(Data, Bit) \
+ ((BOOLEAN)(((Data) & USB_HC_BIT(Bit)) == USB_HC_BIT(Bit)))
+
+typedef struct _USBHC_MEM_BLOCK USBHC_MEM_BLOCK;
+struct _USBHC_MEM_BLOCK {
+ UINT8 *Bits; // Bit array to record which unit is allocated
+ UINTN BitsLen;
+ UINT8 *Buf;
+ UINT8 *BufHost;
+ UINTN BufLen; // Memory size in bytes
+ VOID *Mapping;
+ USBHC_MEM_BLOCK *Next;
+};
+
+//
+// USBHC_MEM_POOL is used to manage the memory used by USB
+// host controller. XHCI requires the control memory and transfer
+// data to be on the same 4G memory.
+//
+typedef struct _USBHC_MEM_POOL {
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ BOOLEAN Check4G;
+ UINT32 Which4G;
+ USBHC_MEM_BLOCK *Head;
+} USBHC_MEM_POOL;
+
+//
+// Memory allocation unit, must be 2^n, n>4
+//
+#define USBHC_MEM_UNIT 64
+
+#define USBHC_MEM_UNIT_MASK (USBHC_MEM_UNIT - 1)
+#define USBHC_MEM_DEFAULT_PAGES 16
+
+#define USBHC_MEM_ROUND(Len) (((Len) + USBHC_MEM_UNIT_MASK) & (~USBHC_MEM_UNIT_MASK))
+
+//
+// Advance the byte and bit to the next bit, adjust byte accordingly.
+//
+#define NEXT_BIT(Byte, Bit) \
+ do { \
+ (Bit)++; \
+ if ((Bit) > 7) { \
+ (Byte)++; \
+ (Bit) = 0; \
+ } \
+ } while (0)
+
+
+
+/**
+ Initialize the memory management pool for the host controller.
+
+ @param PciIo The PciIo that can be used to access the host controller.
+
+ @retval EFI_SUCCESS The memory pool is initialized.
+ @retval EFI_OUT_OF_RESOURCE Fail to init the memory pool.
+
+**/
+USBHC_MEM_POOL *
+UsbHcInitMemPool (
+ IN EFI_PCI_IO_PROTOCOL *PciIo
+ );
+
+
+/**
+ Release the memory management pool.
+
+ @param Pool The USB memory pool to free.
+
+ @retval EFI_SUCCESS The memory pool is freed.
+ @retval EFI_DEVICE_ERROR Failed to free the memory pool.
+
+**/
+EFI_STATUS
+UsbHcFreeMemPool (
+ IN USBHC_MEM_POOL *Pool
+ );
+
+
+/**
+ Allocate some memory from the host controller's memory pool
+ which can be used to communicate with host controller.
+
+ @param Pool The host controller's memory pool.
+ @param Size Size of the memory to allocate.
+
+ @return The allocated memory or NULL.
+
+**/
+VOID *
+UsbHcAllocateMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Size
+ );
+
+
+/**
+ Free the allocated memory back to the memory pool.
+
+ @param Pool The memory pool of the host controller.
+ @param Mem The memory to free.
+ @param Size The size of the memory to free.
+
+**/
+VOID
+UsbHcFreeMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ );
+
+/**
+ Calculate the corresponding pci bus address according to the Mem parameter.
+
+ @param Pool The memory pool of the host controller.
+ @param Mem The pointer to host memory.
+ @param Size The size of the memory region.
+
+ @return The pci memory address
+
+**/
+EFI_PHYSICAL_ADDRESS
+UsbHcGetPciAddrForHostAddr (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ );
+
+/**
+ Calculate the corresponding host address according to the pci address.
+
+ @param Pool The memory pool of the host controller.
+ @param Mem The pointer to pci memory.
+ @param Size The size of the memory region.
+
+ @return The host memory address
+
+**/
+EFI_PHYSICAL_ADDRESS
+UsbHcGetHostAddrForPciAddr (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ );
+
+/**
+ Allocates pages at a specified alignment that are suitable for an EfiPciIoOperationBusMasterCommonBuffer mapping.
+
+ If Alignment is not a power of two and Alignment is not zero, then ASSERT().
+
+ @param PciIo The PciIo that can be used to access the host controller.
+ @param Pages The number of pages to allocate.
+ @param Alignment The requested alignment of the allocation. Must be a power of two.
+ @param HostAddress The system memory address to map to the PCI controller.
+ @param DeviceAddress The resulting map address for the bus master PCI controller to
+ use to access the hosts HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS Success to allocate aligned pages.
+ @retval EFI_INVALID_PARAMETER Pages or Alignment is not valid.
+ @retval EFI_OUT_OF_RESOURCES Do not have enough resources to allocate memory.
+
+
+**/
+EFI_STATUS
+UsbHcAllocateAlignedPages (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINTN Pages,
+ IN UINTN Alignment,
+ OUT VOID **HostAddress,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ );
+
+/**
+ Frees memory that was allocated with UsbHcAllocateAlignedPages().
+
+ @param PciIo The PciIo that can be used to access the host controller.
+ @param HostAddress The system memory address to map to the PCI controller.
+ @param Pages The number of pages to free.
+ @param Mapping The mapping value returned from Map().
+
+**/
+VOID
+UsbHcFreeAlignedPages (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN VOID *HostAddress,
+ IN UINTN Pages,
+ VOID *Mapping
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c b/Core/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c new file mode 100644 index 0000000000..39c28ab7d8 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c @@ -0,0 +1,2250 @@ +/** @file
+ The XHCI controller driver.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+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"
+
+//
+// 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}
+};
+
+USB_CLEAR_PORT_MAP mUsbClearPortChangeMap[] = {
+ {XHC_PORTSC_CSC, EfiUsbPortConnectChange},
+ {XHC_PORTSC_PEC, EfiUsbPortEnableChange},
+ {XHC_PORTSC_OCC, EfiUsbPortOverCurrentChange},
+ {XHC_PORTSC_PRC, EfiUsbPortResetChange}
+};
+
+USB_PORT_STATE_MAP mUsbHubPortStateMap[] = {
+ {XHC_HUB_PORTSC_CCS, USB_PORT_STAT_CONNECTION},
+ {XHC_HUB_PORTSC_PED, USB_PORT_STAT_ENABLE},
+ {XHC_HUB_PORTSC_OCA, USB_PORT_STAT_OVERCURRENT},
+ {XHC_HUB_PORTSC_RESET, USB_PORT_STAT_RESET}
+};
+
+USB_PORT_STATE_MAP mUsbHubPortChangeMap[] = {
+ {XHC_HUB_PORTSC_CSC, USB_PORT_STAT_C_CONNECTION},
+ {XHC_HUB_PORTSC_PEC, USB_PORT_STAT_C_ENABLE},
+ {XHC_HUB_PORTSC_OCC, USB_PORT_STAT_C_OVERCURRENT},
+ {XHC_HUB_PORTSC_PRC, USB_PORT_STAT_C_RESET}
+};
+
+USB_CLEAR_PORT_MAP mUsbHubClearPortChangeMap[] = {
+ {XHC_HUB_PORTSC_CSC, EfiUsbPortConnectChange},
+ {XHC_HUB_PORTSC_PEC, EfiUsbPortEnableChange},
+ {XHC_HUB_PORTSC_OCC, EfiUsbPortOverCurrentChange},
+ {XHC_HUB_PORTSC_PRC, EfiUsbPortResetChange},
+ {XHC_HUB_PORTSC_BHRC, Usb3PortBHPortResetChange}
+};
+
+EFI_DRIVER_BINDING_PROTOCOL gXhciDriverBinding = {
+ XhcDriverBindingSupported,
+ XhcDriverBindingStart,
+ XhcDriverBindingStop,
+ 0x30,
+ NULL,
+ NULL
+};
+
+//
+// Template for Xhci's Usb2 Host Controller Protocol Instance.
+//
+EFI_USB2_HC_PROTOCOL gXhciUsb2HcTemplate = {
+ XhcGetCapability,
+ XhcReset,
+ XhcGetState,
+ XhcSetState,
+ XhcControlTransfer,
+ XhcBulkTransfer,
+ XhcAsyncInterruptTransfer,
+ XhcSyncInterruptTransfer,
+ XhcIsochronousTransfer,
+ XhcAsyncIsochronousTransfer,
+ XhcGetRootHubPortStatus,
+ XhcSetRootHubPortFeature,
+ XhcClearRootHubPortFeature,
+ 0x3,
+ 0x0
+};
+
+/**
+ 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_INSTANCE *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_INSTANCE *Xhc;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ Xhc = XHC_FROM_THIS (This);
+
+ if (Xhc->DevicePath != NULL) {
+ //
+ // Report Status Code to indicate reset happens
+ //
+ REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+ EFI_PROGRESS_CODE,
+ (EFI_IO_BUS_USB | EFI_IOB_PC_RESET),
+ Xhc->DevicePath
+ );
+ }
+
+ OldTpl = gBS->RaiseTPL (XHC_TPL);
+
+ switch (Attributes) {
+ case EFI_USB_HC_RESET_GLOBAL:
+ //
+ // Flow through, same behavior as Host Controller Reset
+ //
+ case EFI_USB_HC_RESET_HOST_CONTROLLER:
+ if ((Xhc->DebugCapSupOffset != 0xFFFFFFFF) && ((XhcReadExtCapReg (Xhc, Xhc->DebugCapSupOffset) & 0xFF) == XHC_CAP_USB_DEBUG) &&
+ ((XhcReadExtCapReg (Xhc, Xhc->DebugCapSupOffset + XHC_DC_DCCTRL) & BIT0) != 0)) {
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+ //
+ // 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_INSTANCE *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_INSTANCE *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_INSTANCE *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);
+ }
+ }
+
+ MapSize = sizeof (mUsbClearPortChangeMap) / sizeof (USB_CLEAR_PORT_MAP);
+
+ for (Index = 0; Index < MapSize; Index++) {
+ if (XHC_BIT_IS_SET (State, mUsbClearPortChangeMap[Index].HwState)) {
+ XhcClearRootHubPortFeature (This, PortNumber, (EFI_USB_PORT_FEATURE)mUsbClearPortChangeMap[Index].Selector);
+ }
+ }
+
+ //
+ // 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_INSTANCE *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 = (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;
+ }
+ }
+
+ //
+ // 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_INSTANCE *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_INSTANCE *Xhc;
+ URB *Urb;
+ UINT8 Endpoint;
+ UINT8 Index;
+ 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;
+ EFI_USB_DEVICE_REQUEST ClearPortRequest;
+ UINTN Len;
+
+ //
+ // 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;
+ Len = 0;
+
+ 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 (Xhc, DeviceAddress);
+ if (SlotId == 0) {
+ goto ON_EXIT;
+ }
+
+ //
+ // 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) &&
+ (Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD, USB_TARGET_DEVICE))) {
+ //
+ // 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 (!Xhc->UsbDevContext[Index + 1].Enabled &&
+ (Xhc->UsbDevContext[Index + 1].SlotId == 0) &&
+ (Xhc->UsbDevContext[Index + 1].BusDevAddr == (UINT8)Request->Value)) {
+ Xhc->UsbDevContext[Index + 1].BusDevAddr = 0;
+ }
+ }
+
+ if (Xhc->UsbDevContext[SlotId].XhciDevAddr == 0) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ //
+ // 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.
+ //
+ Xhc->UsbDevContext[SlotId].BusDevAddr = (UINT8)Request->Value;
+ 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 = (UINT8) (0 | ((TransferDirection == EfiUsbDataIn) ? 0x80 : 0));
+ Urb = XhcCreateUrb (
+ Xhc,
+ DeviceAddress,
+ 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;
+ }
+
+ 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 (Status == EFI_TIMEOUT) {
+ //
+ // The transfer timed out. Abort the transfer by dequeueing of the TD.
+ //
+ RecoveryStatus = XhcDequeueTrbFromEndpoint(Xhc, Urb);
+ if (EFI_ERROR(RecoveryStatus)) {
+ DEBUG((EFI_D_ERROR, "XhcControlTransfer: XhcDequeueTrbFromEndpoint failed\n"));
+ }
+ goto FREE_URB;
+ } else {
+ if (*TransferResult == EFI_USB_NOERROR) {
+ Status = EFI_SUCCESS;
+ } else if (*TransferResult == EFI_USB_ERR_STALL) {
+ RecoveryStatus = XhcRecoverHaltedEndpoint(Xhc, Urb);
+ if (EFI_ERROR (RecoveryStatus)) {
+ DEBUG ((EFI_D_ERROR, "XhcControlTransfer: XhcRecoverHaltedEndpoint failed\n"));
+ }
+ Status = EFI_DEVICE_ERROR;
+ goto FREE_URB;
+ } else {
+ goto FREE_URB;
+ }
+ }
+
+ Xhc->PciIo->Flush (Xhc->PciIo);
+
+ if (Urb->DataMap != NULL) {
+ Status = Xhc->PciIo->Unmap (Xhc->PciIo, Urb->DataMap);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ 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) &&
+ ((Request->RequestType == USB_REQUEST_TYPE (EfiUsbDataIn, USB_REQ_TYPE_STANDARD, USB_TARGET_DEVICE)) ||
+ ((Request->RequestType == USB_REQUEST_TYPE (EfiUsbDataIn, USB_REQ_TYPE_CLASS, USB_TARGET_DEVICE))))) {
+ DescriptorType = (UINT8)(Request->Value >> 8);
+ if ((DescriptorType == USB_DESC_TYPE_DEVICE) && ((*DataLength == sizeof (EFI_USB_DEVICE_DESCRIPTOR)) || ((DeviceSpeed == EFI_USB_SPEED_FULL) && (*DataLength == 8)))) {
+ ASSERT (Data != NULL);
+ //
+ // Store a copy of device scriptor as hub device need this info to configure endpoint.
+ //
+ CopyMem (&Xhc->UsbDevContext[SlotId].DevDesc, Data, *DataLength);
+ if (Xhc->UsbDevContext[SlotId].DevDesc.BcdUSB == 0x0300) {
+ //
+ // If it's a usb3.0 device, then its max packet size is a 2^n.
+ //
+ MaxPacket0 = 1 << Xhc->UsbDevContext[SlotId].DevDesc.MaxPacketSize0;
+ } else {
+ MaxPacket0 = Xhc->UsbDevContext[SlotId].DevDesc.MaxPacketSize0;
+ }
+ Xhc->UsbDevContext[SlotId].ConfDesc = AllocateZeroPool (Xhc->UsbDevContext[SlotId].DevDesc.NumConfigurations * sizeof (EFI_USB_CONFIG_DESCRIPTOR *));
+ if (Xhc->HcCParams.Data.Csz == 0) {
+ Status = XhcEvaluateContext (Xhc, SlotId, MaxPacket0);
+ } else {
+ Status = XhcEvaluateContext64 (Xhc, SlotId, MaxPacket0);
+ }
+ } else if (DescriptorType == USB_DESC_TYPE_CONFIG) {
+ ASSERT (Data != NULL);
+ if (*DataLength == ((UINT16 *)Data)[1]) {
+ //
+ // Get configuration value from request, Store the configuration descriptor for Configure_Endpoint cmd.
+ //
+ Index = (UINT8)Request->Value;
+ ASSERT (Index < Xhc->UsbDevContext[SlotId].DevDesc.NumConfigurations);
+ Xhc->UsbDevContext[SlotId].ConfDesc[Index] = AllocateZeroPool(*DataLength);
+ CopyMem (Xhc->UsbDevContext[SlotId].ConfDesc[Index], Data, *DataLength);
+ //
+ // Default to use AlternateSetting 0 for all interfaces.
+ //
+ Xhc->UsbDevContext[SlotId].ActiveAlternateSetting = AllocateZeroPool (Xhc->UsbDevContext[SlotId].ConfDesc[Index]->NumInterfaces * sizeof (UINT8));
+ }
+ } else if (((DescriptorType == USB_DESC_TYPE_HUB) ||
+ (DescriptorType == USB_DESC_TYPE_HUB_SUPER_SPEED)) && (*DataLength > 2)) {
+ ASSERT (Data != NULL);
+ HubDesc = (EFI_USB_HUB_DESCRIPTOR *)Data;
+ ASSERT (HubDesc->NumPorts <= 15);
+ //
+ // The bit 5,6 of HubCharacter field of Hub Descriptor is TTT.
+ //
+ TTT = (UINT8)((HubDesc->HubCharacter & (BIT5 | BIT6)) >> 5);
+ if (Xhc->UsbDevContext[SlotId].DevDesc.DeviceProtocol == 2) {
+ //
+ // Don't support multi-TT feature for super speed hub now.
+ //
+ MTT = 0;
+ DEBUG ((EFI_D_ERROR, "XHCI: Don't support multi-TT feature for Hub now. (force to disable MTT)\n"));
+ } else {
+ MTT = 0;
+ }
+
+ if (Xhc->HcCParams.Data.Csz == 0) {
+ Status = XhcConfigHubContext (Xhc, SlotId, HubDesc->NumPorts, TTT, MTT);
+ } else {
+ Status = XhcConfigHubContext64 (Xhc, SlotId, HubDesc->NumPorts, TTT, MTT);
+ }
+ }
+ } else if ((Request->Request == USB_REQ_SET_CONFIG) &&
+ (Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD, USB_TARGET_DEVICE))) {
+ //
+ // Hook Set_Config request from UsbBus as we need configure device endpoint.
+ //
+ for (Index = 0; Index < Xhc->UsbDevContext[SlotId].DevDesc.NumConfigurations; Index++) {
+ if (Xhc->UsbDevContext[SlotId].ConfDesc[Index]->ConfigurationValue == (UINT8)Request->Value) {
+ if (Xhc->HcCParams.Data.Csz == 0) {
+ Status = XhcSetConfigCmd (Xhc, SlotId, DeviceSpeed, Xhc->UsbDevContext[SlotId].ConfDesc[Index]);
+ } else {
+ Status = XhcSetConfigCmd64 (Xhc, SlotId, DeviceSpeed, Xhc->UsbDevContext[SlotId].ConfDesc[Index]);
+ }
+ break;
+ }
+ }
+ } else if ((Request->Request == USB_REQ_SET_INTERFACE) &&
+ (Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD, USB_TARGET_INTERFACE))) {
+ //
+ // Hook Set_Interface request from UsbBus as we need configure interface setting.
+ // Request->Value indicates AlterlateSetting to set
+ // Request->Index indicates Interface to set
+ //
+ if (Xhc->UsbDevContext[SlotId].ActiveAlternateSetting[(UINT8) Request->Index] != (UINT8) Request->Value) {
+ if (Xhc->HcCParams.Data.Csz == 0) {
+ Status = XhcSetInterface (Xhc, SlotId, DeviceSpeed, Xhc->UsbDevContext[SlotId].ConfDesc[Xhc->UsbDevContext[SlotId].ActiveConfiguration - 1], Request);
+ } else {
+ Status = XhcSetInterface64 (Xhc, SlotId, DeviceSpeed, Xhc->UsbDevContext[SlotId].ConfDesc[Xhc->UsbDevContext[SlotId].ActiveConfiguration - 1], Request);
+ }
+ }
+ } else if ((Request->Request == USB_REQ_GET_STATUS) &&
+ (Request->RequestType == USB_REQUEST_TYPE (EfiUsbDataIn, USB_REQ_TYPE_CLASS, USB_TARGET_OTHER))) {
+ ASSERT (Data != NULL);
+ //
+ // 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 ((State & XHC_PORTSC_PS) >> 10 == 0) {
+ PortStatus.PortStatus |= USB_PORT_STAT_SUPER_SPEED;
+ }
+ } else {
+ //
+ // For high or full/low speed hub, its bit9~10 presents the attached device speed.
+ //
+ if (XHC_BIT_IS_SET (State, BIT9)) {
+ PortStatus.PortStatus |= USB_PORT_STAT_LOW_SPEED;
+ } else if (XHC_BIT_IS_SET (State, BIT10)) {
+ PortStatus.PortStatus |= USB_PORT_STAT_HIGH_SPEED;
+ }
+ }
+
+ //
+ // Convert the XHCI port/port change state to UEFI status
+ //
+ MapSize = sizeof (mUsbHubPortStateMap) / sizeof (USB_PORT_STATE_MAP);
+ for (Index = 0; Index < MapSize; Index++) {
+ if (XHC_BIT_IS_SET (State, mUsbHubPortStateMap[Index].HwState)) {
+ PortStatus.PortStatus = (UINT16) (PortStatus.PortStatus | mUsbHubPortStateMap[Index].UefiState);
+ }
+ }
+
+ MapSize = sizeof (mUsbHubPortChangeMap) / sizeof (USB_PORT_STATE_MAP);
+ for (Index = 0; Index < MapSize; Index++) {
+ if (XHC_BIT_IS_SET (State, mUsbHubPortChangeMap[Index].HwState)) {
+ PortStatus.PortChangeStatus = (UINT16) (PortStatus.PortChangeStatus | mUsbHubPortChangeMap[Index].UefiState);
+ }
+ }
+
+ MapSize = sizeof (mUsbHubClearPortChangeMap) / sizeof (USB_CLEAR_PORT_MAP);
+
+ for (Index = 0; Index < MapSize; Index++) {
+ if (XHC_BIT_IS_SET (State, mUsbHubClearPortChangeMap[Index].HwState)) {
+ ZeroMem (&ClearPortRequest, sizeof (EFI_USB_DEVICE_REQUEST));
+ ClearPortRequest.RequestType = USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_CLASS, USB_TARGET_OTHER);
+ ClearPortRequest.Request = (UINT8) USB_REQ_CLEAR_FEATURE;
+ ClearPortRequest.Value = mUsbHubClearPortChangeMap[Index].Selector;
+ ClearPortRequest.Index = Request->Index;
+ ClearPortRequest.Length = 0;
+
+ XhcControlTransfer (
+ This,
+ DeviceAddress,
+ DeviceSpeed,
+ MaximumPacketLength,
+ &ClearPortRequest,
+ EfiUsbNoData,
+ NULL,
+ &Len,
+ Timeout,
+ Translator,
+ TransferResult
+ );
+ }
+ }
+
+ XhcPollPortStatusChange (Xhc, Xhc->UsbDevContext[SlotId].RouteString, (UINT8)Request->Index, &PortStatus);
+
+ *(UINT32 *)Data = *(UINT32*)&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_INSTANCE *Xhc;
+ URB *Urb;
+ 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 (Xhc, DeviceAddress);
+ if (SlotId == 0) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Create a new URB, insert it into the asynchronous
+ // schedule list, then poll the execution status.
+ //
+ Urb = XhcCreateUrb (
+ Xhc,
+ DeviceAddress,
+ 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;
+ }
+
+ Status = XhcExecTransfer (Xhc, FALSE, Urb, Timeout);
+
+ *TransferResult = Urb->Result;
+ *DataLength = Urb->Completed;
+
+ if (Status == EFI_TIMEOUT) {
+ //
+ // The transfer timed out. Abort the transfer by dequeueing of the TD.
+ //
+ RecoveryStatus = XhcDequeueTrbFromEndpoint(Xhc, Urb);
+ if (EFI_ERROR(RecoveryStatus)) {
+ DEBUG((EFI_D_ERROR, "XhcBulkTransfer: XhcDequeueTrbFromEndpoint failed\n"));
+ }
+ } else {
+ if (*TransferResult == EFI_USB_NOERROR) {
+ Status = EFI_SUCCESS;
+ } else if (*TransferResult == EFI_USB_ERR_STALL) {
+ RecoveryStatus = XhcRecoverHaltedEndpoint(Xhc, Urb);
+ if (EFI_ERROR (RecoveryStatus)) {
+ DEBUG ((EFI_D_ERROR, "XhcBulkTransfer: XhcRecoverHaltedEndpoint failed\n"));
+ }
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+
+ Xhc->PciIo->Flush (Xhc->PciIo);
+ XhcFreeUrb (Xhc, 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_INSTANCE *Xhc;
+ URB *Urb;
+ EFI_STATUS Status;
+ 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 (Xhc->UsbDevContext[Index + 1].BusDevAddr == DeviceAddress) {
+ break;
+ }
+ }
+
+ if (Index == 255) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Status = XhciDelAsyncIntTransfer (Xhc, DeviceAddress, EndPointAddress);
+ DEBUG ((EFI_D_INFO, "XhcAsyncInterruptTransfer: remove old transfer for addr %d, Status = %r\n", DeviceAddress, 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 (Xhc, DeviceAddress);
+ if (SlotId == 0) {
+ goto ON_EXIT;
+ }
+
+ Data = AllocateZeroPool (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,
+ DeviceAddress,
+ 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;
+ }
+
+ InsertHeadList (&Xhc->AsyncIntTransfers, &Urb->UrbList);
+ //
+ // Ring the doorbell
+ //
+ Status = RingIntTransferDoorBell (Xhc, Urb);
+
+ON_EXIT:
+ Xhc->PciIo->Flush (Xhc->PciIo);
+ 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_INSTANCE *Xhc;
+ URB *Urb;
+ 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 ((*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 (Xhc, DeviceAddress);
+ if (SlotId == 0) {
+ goto ON_EXIT;
+ }
+
+ Urb = XhcCreateUrb (
+ Xhc,
+ DeviceAddress,
+ 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 (Status == EFI_TIMEOUT) {
+ //
+ // The transfer timed out. Abort the transfer by dequeueing of the TD.
+ //
+ RecoveryStatus = XhcDequeueTrbFromEndpoint(Xhc, Urb);
+ if (EFI_ERROR(RecoveryStatus)) {
+ DEBUG((EFI_D_ERROR, "XhcSyncInterruptTransfer: XhcDequeueTrbFromEndpoint failed\n"));
+ }
+ } else {
+ if (*TransferResult == EFI_USB_NOERROR) {
+ Status = EFI_SUCCESS;
+ } else if (*TransferResult == EFI_USB_ERR_STALL) {
+ RecoveryStatus = XhcRecoverHaltedEndpoint(Xhc, Urb);
+ if (EFI_ERROR (RecoveryStatus)) {
+ DEBUG ((EFI_D_ERROR, "XhcSyncInterruptTransfer: XhcRecoverHaltedEndpoint failed\n"));
+ }
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+
+ Xhc->PciIo->Flush (Xhc->PciIo);
+ XhcFreeUrb (Xhc, 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_INSTANCE structure.
+
+ @param PciIo The PciIo on this device.
+ @param DevicePath The device path of host controller.
+ @param OriginalPciAttributes Original PCI attributes.
+
+ @return The allocated and initialized USB_XHCI_INSTANCE structure if created,
+ otherwise NULL.
+
+**/
+USB_XHCI_INSTANCE*
+XhcCreateUsbHc (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN UINT64 OriginalPciAttributes
+ )
+{
+ USB_XHCI_INSTANCE *Xhc;
+ EFI_STATUS Status;
+ UINT32 PageSize;
+ UINT16 ExtCapReg;
+
+ Xhc = AllocateZeroPool (sizeof (USB_XHCI_INSTANCE));
+
+ if (Xhc == NULL) {
+ return NULL;
+ }
+
+ //
+ // Initialize private data structure
+ //
+ Xhc->Signature = XHCI_INSTANCE_SIG;
+ Xhc->PciIo = PciIo;
+ Xhc->DevicePath = DevicePath;
+ Xhc->OriginalPciAttributes = OriginalPciAttributes;
+ CopyMem (&Xhc->Usb2Hc, &gXhciUsb2HcTemplate, sizeof (EFI_USB2_HC_PROTOCOL));
+
+ 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);
+
+ ExtCapReg = (UINT16) (Xhc->HcCParams.Data.ExtCapReg);
+ Xhc->ExtCapRegBase = ExtCapReg << 2;
+ Xhc->UsbLegSupOffset = XhcGetCapabilityAddr (Xhc, XHC_CAP_USB_LEGACY);
+ Xhc->DebugCapSupOffset = XhcGetCapabilityAddr (Xhc, XHC_CAP_USB_DEBUG);
+
+ 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: UsbLegSupOffset 0x%x\n", Xhc->UsbLegSupOffset));
+ DEBUG ((EFI_D_INFO, "XhcCreateUsb3Hc: DebugCapSupOffset 0x%x\n", Xhc->DebugCapSupOffset));
+
+ //
+ // Create AsyncRequest Polling Timer
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ 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 handler private data
+
+**/
+VOID
+EFIAPI
+XhcExitBootService (
+ EFI_EVENT Event,
+ VOID *Context
+ )
+
+{
+ USB_XHCI_INSTANCE *Xhc;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ Xhc = (USB_XHCI_INSTANCE*) Context;
+ PciIo = Xhc->PciIo;
+
+ //
+ // Stop AsyncRequest Polling timer then stop the XHCI driver
+ // and uninstall the XHCI protocl.
+ //
+ gBS->SetTimer (Xhc->PollTimer, TimerCancel, 0);
+ XhcHaltHC (Xhc, XHC_GENERIC_TIMEOUT);
+
+ if (Xhc->PollTimer != NULL) {
+ gBS->CloseEvent (Xhc->PollTimer);
+ }
+
+ XhcClearBiosOwnership (Xhc);
+
+ //
+ // Restore original PCI attributes
+ //
+ PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationSet,
+ Xhc->OriginalPciAttributes,
+ NULL
+ );
+}
+
+/**
+ 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_INSTANCE *Xhc;
+ EFI_DEVICE_PATH_PROTOCOL *HcDevicePath;
+
+ //
+ // 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;
+ }
+
+ //
+ // Open Device Path Protocol for on USB host controller
+ //
+ HcDevicePath = NULL;
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &HcDevicePath,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ 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 &= (UINT64)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, HcDevicePath, 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_TIMER_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_INSTANCE *Xhc;
+ UINT8 Index;
+
+ //
+ // 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;
+ }
+
+ Status = gBS->UninstallProtocolInterface (
+ Controller,
+ &gEfiUsb2HcProtocolGuid,
+ Usb2Hc
+ );
+
+ 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, 0);
+
+ //
+ // Disable the device slots occupied by these devices on its downstream ports.
+ // Entry 0 is reserved.
+ //
+ for (Index = 0; Index < 255; Index++) {
+ if (!Xhc->UsbDevContext[Index + 1].Enabled ||
+ (Xhc->UsbDevContext[Index + 1].SlotId == 0)) {
+ continue;
+ }
+ if (Xhc->HcCParams.Data.Csz == 0) {
+ XhcDisableSlotCmd (Xhc, Xhc->UsbDevContext[Index + 1].SlotId);
+ } else {
+ XhcDisableSlotCmd64 (Xhc, Xhc->UsbDevContext[Index + 1].SlotId);
+ }
+ }
+
+ if (Xhc->PollTimer != NULL) {
+ gBS->CloseEvent (Xhc->PollTimer);
+ }
+
+ if (Xhc->ExitBootServiceEvent != NULL) {
+ gBS->CloseEvent (Xhc->ExitBootServiceEvent);
+ }
+
+ XhcHaltHC (Xhc, XHC_GENERIC_TIMEOUT);
+ XhcClearBiosOwnership (Xhc);
+ 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/Core/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h b/Core/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h new file mode 100644 index 0000000000..7999151b3f --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h @@ -0,0 +1,727 @@ +/** @file
+
+ Provides some data structure definitions used by the XHCI host controller driver.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+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 <Uefi.h>
+
+#include <Protocol/Usb2HostController.h>
+#include <Protocol/PciIo.h>
+
+#include <Guid/EventGroup.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/ReportStatusCodeLib.h>
+
+#include <IndustryStandard/Pci.h>
+
+typedef struct _USB_XHCI_INSTANCE USB_XHCI_INSTANCE;
+typedef struct _USB_DEV_CONTEXT USB_DEV_CONTEXT;
+
+#include "XhciReg.h"
+#include "XhciSched.h"
+#include "ComponentName.h"
+#include "UsbHcMem.h"
+
+//
+// The unit is microsecond, setting it as 1us.
+//
+#define XHC_1_MICROSECOND (1)
+//
+// The unit is microsecond, setting it as 1ms.
+//
+#define XHC_1_MILLISECOND (1000)
+//
+// XHC generic timeout experience values.
+// The unit is millisecond, setting it as 10s.
+//
+#define XHC_GENERIC_TIMEOUT (10 * 1000)
+//
+// XHC reset timeout experience values.
+// The unit is millisecond, setting it as 1s.
+//
+#define XHC_RESET_TIMEOUT (1000)
+//
+// XHC async transfer timer interval, set by experience.
+// The unit is 100us, takes 1ms as interval.
+//
+#define XHC_ASYNC_TIMER_INTERVAL EFI_TIMER_PERIOD_MILLISECONDS(1)
+
+//
+// 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 0x100
+#define TR_RING_TRB_NUMBER 0x100
+#define ERST_NUMBER 0x01
+#define EVENT_RING_TRB_NUMBER 0x200
+
+#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((UINT64)(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 XHCI_INSTANCE_SIG SIGNATURE_32 ('x', 'h', 'c', 'i')
+#define XHC_FROM_THIS(a) CR(a, USB_XHCI_INSTANCE, Usb2Hc, XHCI_INSTANCE_SIG)
+
+#define USB_DESC_TYPE_HUB 0x29
+#define USB_DESC_TYPE_HUB_SUPER_SPEED 0x2a
+
+//
+// The RequestType in EFI_USB_DEVICE_REQUEST is composed of
+// three fields: One bit direction, 2 bit type, and 5 bit
+// target.
+//
+#define USB_REQUEST_TYPE(Dir, Type, Target) \
+ ((UINT8)((((Dir) == EfiUsbDataIn ? 0x01 : 0) << 7) | (Type) | (Target)))
+
+//
+// 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_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 *OutputContext;
+ //
+ // 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;
+ //
+ // A device has an active Configuration.
+ //
+ UINT8 ActiveConfiguration;
+ //
+ // Every interface has an active AlternateSetting.
+ //
+ UINT8 *ActiveAlternateSetting;
+};
+
+struct _USB_XHCI_INSTANCE {
+ UINT32 Signature;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINT64 OriginalPciAttributes;
+ USBHC_MEM_POOL *MemPool;
+
+ 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;
+ VOID *ScratchMap;
+ UINT32 MaxScratchpadBufs;
+ UINT64 *ScratchEntry;
+ UINTN *ScratchEntryMap;
+ UINT32 ExtCapRegBase;
+ UINT32 UsbLegSupOffset;
+ UINT32 DebugCapSupOffset;
+ UINT64 *DCBAA;
+ VOID *DCBAAMap;
+ UINT32 MaxSlotsEn;
+ //
+ // Cmd Transfer Ring
+ //
+ TRANSFER_RING CmdRing;
+ //
+ // EventRing
+ //
+ EVENT_RING EventRing;
+ //
+ // Misc
+ //
+ EFI_UNICODE_STRING_TABLE *ControllerNameTable;
+
+ //
+ // Store device contexts managed by XHCI instance
+ // The array supports up to 255 devices, entry 0 is reserved and should not be used.
+ //
+ USB_DEV_CONTEXT UsbDevContext[256];
+};
+
+
+extern EFI_DRIVER_BINDING_PROTOCOL gXhciDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gXhciComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf new file mode 100644 index 0000000000..614938ac89 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf @@ -0,0 +1,76 @@ +## @file
+# The 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 - 2014, Intel Corporation. All rights reserved.<BR>
+#
+# 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
+ MODULE_UNI_FILE = XhciDxe.uni
+ 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 ARM AARCH64
+#
+# DRIVER_BINDING = gXhciDriverBinding
+# COMPONENT_NAME = gXhciComponentName
+# COMPONENT_NAME2 = gXhciComponentName2
+#
+
+[Sources]
+ Xhci.c
+ XhciReg.c
+ XhciSched.c
+ UsbHcMem.c
+ UsbHcMem.h
+ ComponentName.c
+ ComponentName.h
+ Xhci.h
+ XhciReg.h
+ XhciSched.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ MemoryAllocationLib
+ BaseLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ DebugLib
+ ReportStatusCodeLib
+
+[Guids]
+ gEfiEventExitBootServicesGuid ## SOMETIMES_CONSUMES ## Event
+
+[Protocols]
+ gEfiPciIoProtocolGuid ## TO_START
+ gEfiUsb2HcProtocolGuid ## BY_START
+
+# [Event]
+# EVENT_TYPE_PERIODIC_TIMER ## CONSUMES
+#
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ XhciDxeExtra.uni
diff --git a/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.uni b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.uni Binary files differnew file mode 100644 index 0000000000..eea95dfa2b --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.uni diff --git a/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxeExtra.uni b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxeExtra.uni Binary files differnew file mode 100644 index 0000000000..9c598b1868 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxeExtra.uni diff --git a/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c new file mode 100644 index 0000000000..d0f22050ad --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c @@ -0,0 +1,743 @@ +/** @file
+
+ The XHCI register operation routines.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+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 Instance.
+ @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_INSTANCE *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 Instance.
+ @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_INSTANCE *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 Instance.
+ @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_INSTANCE *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 Instance.
+ @param Offset The offset of the 4-bytes width operational register.
+ @param Data The data to write.
+
+**/
+VOID
+XhcWriteOpReg (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @param Offset The offset of the 2-bytes width operational register.
+ @param Data The data to write.
+
+**/
+VOID
+XhcWriteOpReg16 (
+ IN USB_XHCI_INSTANCE *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));
+ }
+}
+
+/**
+ Read XHCI door bell register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the door bell register.
+
+ @return The register content read
+
+**/
+UINT32
+XhcReadDoorBellReg (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @param Offset The offset of the door bell register.
+ @param Data The data to write.
+
+**/
+VOID
+XhcWriteDoorBellReg (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @param Offset The offset of the runtime register.
+
+ @return The register content read
+
+**/
+UINT32
+XhcReadRuntimeReg (
+ IN USB_XHCI_INSTANCE *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;
+}
+
+/**
+ Write the data to the XHCI runtime register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the runtime register.
+ @param Data The data to write.
+
+**/
+VOID
+XhcWriteRuntimeReg (
+ IN USB_XHCI_INSTANCE *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));
+ }
+}
+
+/**
+ Read XHCI extended capability register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the extended capability register.
+
+ @return The register content read
+
+**/
+UINT32
+XhcReadExtCapReg (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @param Offset The offset of the extended capability register.
+ @param Data The data to write.
+
+**/
+VOID
+XhcWriteExtCapReg (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @param Offset The offset of the runtime register.
+ @param Bit The bit mask of the register to set.
+
+**/
+VOID
+XhcSetRuntimeRegBit (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @param Offset The offset of the runtime register.
+ @param Bit The bit mask of the register to set.
+
+**/
+VOID
+XhcClearRuntimeRegBit (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @param Offset The offset of the operational register.
+ @param Bit The bit mask of the register to set.
+
+**/
+VOID
+XhcSetOpRegBit (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @param Offset The offset of the operational register.
+ @param Bit The bit mask of the register to clear.
+
+**/
+VOID
+XhcClearOpRegBit (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT32 Offset,
+ IN UINT32 Bit,
+ IN BOOLEAN WaitToSet,
+ IN UINT32 Timeout
+ )
+{
+ UINT32 Index;
+ UINT64 Loop;
+
+ Loop = Timeout * XHC_1_MILLISECOND;
+
+ for (Index = 0; Index < Loop; Index++) {
+ if (XHC_REG_BIT_IS_SET (Xhc, Offset, Bit) == WaitToSet) {
+ return EFI_SUCCESS;
+ }
+
+ gBS->Stall (XHC_1_MICROSECOND);
+ }
+
+ return EFI_TIMEOUT;
+}
+
+/**
+ Set Bios Ownership
+
+ @param Xhc The XHCI Instance.
+
+**/
+VOID
+XhcSetBiosOwnership (
+ IN USB_XHCI_INSTANCE *Xhc
+ )
+{
+ UINT32 Buffer;
+
+ if (Xhc->UsbLegSupOffset == 0xFFFFFFFF) {
+ return;
+ }
+
+ 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 Instance.
+
+**/
+VOID
+XhcClearBiosOwnership (
+ IN USB_XHCI_INSTANCE *Xhc
+ )
+{
+ UINT32 Buffer;
+
+ if (Xhc->UsbLegSupOffset == 0xFFFFFFFF) {
+ return;
+ }
+
+ 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 offset of the XHCI capability.
+
+ @param Xhc The XHCI Instance.
+ @param CapId The XHCI Capability ID.
+
+ @return The offset of XHCI legacy support capability register.
+
+**/
+UINT32
+XhcGetCapabilityAddr (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 CapId
+ )
+{
+ 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) == CapId) {
+ return ExtCapOffset;
+ }
+ //
+ // If not, then traverse all of the ext capability registers till finding out it.
+ //
+ NextExtCapReg = (UINT8)((Data >> 8) & 0xFF);
+ ExtCapOffset += (NextExtCapReg << 2);
+ } while (NextExtCapReg != 0);
+
+ return 0xFFFFFFFF;
+}
+
+/**
+ Whether the XHCI host controller is halted.
+
+ @param Xhc The XHCI Instance.
+
+ @retval TRUE The controller is halted.
+ @retval FALSE It isn't halted.
+
+**/
+BOOLEAN
+XhcIsHalt (
+ IN USB_XHCI_INSTANCE *Xhc
+ )
+{
+ return XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT);
+}
+
+
+/**
+ Whether system error occurred.
+
+ @param Xhc The XHCI Instance.
+
+ @retval TRUE System error happened.
+ @retval FALSE No system error.
+
+**/
+BOOLEAN
+XhcIsSysError (
+ IN USB_XHCI_INSTANCE *Xhc
+ )
+{
+ return XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HSE);
+}
+
+/**
+ Reset the XHCI host controller.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ 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;
+ }
+ }
+
+ if ((Xhc->DebugCapSupOffset == 0xFFFFFFFF) || ((XhcReadExtCapReg (Xhc, Xhc->DebugCapSupOffset) & 0xFF) != XHC_CAP_USB_DEBUG) ||
+ ((XhcReadExtCapReg (Xhc, Xhc->DebugCapSupOffset + XHC_DC_DCCTRL) & BIT0) == 0)) {
+ 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 Instance.
+ @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_INSTANCE *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 Instance.
+ @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_INSTANCE *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/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h new file mode 100644 index 0000000000..b748c8d397 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h @@ -0,0 +1,583 @@ +/** @file
+
+ This file contains the register definition of XHCI host controller.
+
+Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
+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
+
+#define XHC_CAP_USB_LEGACY 0x01
+#define XHC_CAP_USB_DEBUG 0x0A
+
+//============================================//
+// 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
+
+//
+// Debug registers offset
+//
+#define XHC_DC_DCCTRL 0x20
+
+#define USBLEGSP_BIOS_SEMAPHORE BIT16 // HC BIOS Owned Semaphore
+#define USBLEGSP_OS_SEMAPHORE BIT24 // HC OS Owned Semaphore
+
+#pragma pack (1)
+typedef struct {
+ UINT8 MaxSlots; // Number of Device Slots
+ UINT16 MaxIntrs:11; // Number of Interrupters
+ UINT16 Rsvd:5;
+ UINT8 MaxPorts; // Number of Ports
+} HCSPARAMS1;
+
+//
+// Structural Parameters 1 Register Bitmap Definition
+//
+typedef union {
+ UINT32 Dword;
+ HCSPARAMS1 Data;
+} XHC_HCSPARAMS1;
+
+typedef 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
+} HCSPARAMS2;
+
+//
+// Structural Parameters 2 Register Bitmap Definition
+//
+typedef union {
+ UINT32 Dword;
+ HCSPARAMS2 Data;
+} XHC_HCSPARAMS2;
+
+typedef 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
+} HCCPARAMS;
+
+//
+// Capability Parameters Register Bitmap Definition
+//
+typedef union {
+ UINT32 Dword;
+ HCCPARAMS 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) // 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_HUB_PORTSC_CCS BIT0 // Hub's Current Connect Status
+#define XHC_HUB_PORTSC_PED BIT1 // Hub's Port Enabled/Disabled
+#define XHC_HUB_PORTSC_OCA BIT3 // Hub's Over-current Active
+#define XHC_HUB_PORTSC_RESET BIT4 // Hub's Port Reset
+#define XHC_HUB_PORTSC_PP BIT9 // Hub's Port Power
+#define XHC_HUB_PORTSC_CSC BIT16 // Hub's Connect Status Change
+#define XHC_HUB_PORTSC_PEC BIT17 // Hub's Port Enabled/Disabled Change
+#define XHC_HUB_PORTSC_OCC BIT19 // Hub's Over-Current Change
+#define XHC_HUB_PORTSC_PRC BIT20 // Hub's Port Reset Change
+#define XHC_HUB_PORTSC_BHRC BIT21 // Hub's Port Warm Reset Change
+#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
+
+//
+// Hub Class Feature Selector for Clear Port Feature Request
+// It's the extension of hub class feature selector of USB 2.0 in USB 3.0 Spec.
+// For more details, Please refer to USB 3.0 Spec Table 10-7.
+//
+typedef enum {
+ Usb3PortBHPortReset = 28,
+ Usb3PortBHPortResetChange = 29
+} XHC_PORT_FEATURE;
+
+//
+// Structure to map the hardware port states to the
+// UEFI's port states.
+//
+typedef struct {
+ UINT32 HwState;
+ UINT16 UefiState;
+} USB_PORT_STATE_MAP;
+
+//
+// Structure to map the hardware port states to feature selector for clear port feature request.
+//
+typedef struct {
+ UINT32 HwState;
+ UINT16 Selector;
+} USB_CLEAR_PORT_MAP;
+
+/**
+ Read 1-byte width XHCI capability register.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT32 Offset
+ );
+
+/**
+ Read 4-bytes width XHCI capability register.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT32 Offset
+ );
+
+/**
+ Read 4-bytes width XHCI Operational register.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT32 Offset
+ );
+
+/**
+ Write the data to the 4-bytes width XHCI operational register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the 4-bytes width operational register.
+ @param Data The data to write.
+
+**/
+VOID
+XhcWriteOpReg (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset,
+ IN UINT32 Data
+ );
+
+/**
+ Write the data to the 2-bytes width XHCI operational register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the 2-bytes width operational register.
+ @param Data The data to write.
+
+**/
+VOID
+XhcWriteOpReg16 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset,
+ IN UINT16 Data
+ );
+
+/**
+ Read XHCI runtime register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the runtime register.
+
+ @return The register content read
+
+**/
+UINT32
+XhcReadRuntimeReg (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset
+ );
+
+/**
+ Write the data to the XHCI runtime register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the runtime register.
+ @param Data The data to write.
+
+**/
+VOID
+XhcWriteRuntimeReg (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset,
+ IN UINT32 Data
+ );
+
+/**
+ Read XHCI door bell register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the door bell register.
+
+ @return The register content read
+
+**/
+UINT32
+XhcReadDoorBellReg (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset
+ );
+
+/**
+ Write the data to the XHCI door bell register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the door bell register.
+ @param Data The data to write.
+
+**/
+VOID
+XhcWriteDoorBellReg (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset,
+ IN UINT32 Data
+ );
+
+/**
+ Set one bit of the operational register while keeping other bits.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the operational register.
+ @param Bit The bit mask of the register to set.
+
+**/
+VOID
+XhcSetOpRegBit (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset,
+ IN UINT32 Bit
+ );
+
+/**
+ Clear one bit of the operational register while keeping other bits.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the operational register.
+ @param Bit The bit mask of the register to clear.
+
+**/
+VOID
+XhcClearOpRegBit (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @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 microsecond, us).
+
+ @retval EFI_SUCCESS The bit successfully changed by host controller.
+ @retval EFI_TIMEOUT The time out occurred.
+
+**/
+EFI_STATUS
+XhcWaitOpRegBit (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset,
+ IN UINT32 Bit,
+ IN BOOLEAN WaitToSet,
+ IN UINT32 Timeout
+ );
+
+/**
+ Read XHCI runtime register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the runtime register.
+
+ @return The register content read
+
+**/
+UINT32
+XhcReadRuntimeReg (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset
+ );
+
+/**
+ Write the data to the XHCI runtime register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the runtime register.
+ @param Data The data to write.
+
+**/
+VOID
+XhcWriteRuntimeReg (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset,
+ IN UINT32 Data
+ );
+
+/**
+ Set one bit of the runtime register while keeping other bits.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the runtime register.
+ @param Bit The bit mask of the register to set.
+
+**/
+VOID
+XhcSetRuntimeRegBit (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset,
+ IN UINT32 Bit
+ );
+
+/**
+ Clear one bit of the runtime register while keeping other bits.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the runtime register.
+ @param Bit The bit mask of the register to set.
+
+**/
+VOID
+XhcClearRuntimeRegBit (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset,
+ IN UINT32 Bit
+ );
+
+/**
+ Read XHCI extended capability register.
+
+ @param Xhc The XHCI Instance.
+ @param Offset The offset of the extended capability register.
+
+ @return The register content read
+
+**/
+UINT32
+XhcReadExtCapReg (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Offset
+ );
+
+/**
+ Whether the XHCI host controller is halted.
+
+ @param Xhc The XHCI Instance.
+
+ @retval TRUE The controller is halted.
+ @retval FALSE It isn't halted.
+
+**/
+BOOLEAN
+XhcIsHalt (
+ IN USB_XHCI_INSTANCE *Xhc
+ );
+
+/**
+ Whether system error occurred.
+
+ @param Xhc The XHCI Instance.
+
+ @retval TRUE System error happened.
+ @retval FALSE No system error.
+
+**/
+BOOLEAN
+XhcIsSysError (
+ IN USB_XHCI_INSTANCE *Xhc
+ );
+
+/**
+ Reset the XHCI host controller.
+
+ @param Xhc The XHCI Instance.
+ @param Timeout Time to wait before abort (in microsecond, us).
+
+ @retval EFI_SUCCESS The XHCI host controller is reset.
+ @return Others Failed to reset the XHCI before Timeout.
+
+**/
+EFI_STATUS
+XhcResetHC (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT32 Timeout
+ );
+
+/**
+ Halt the XHCI host controller.
+
+ @param Xhc The XHCI Instance.
+ @param Timeout Time to wait before abort (in microsecond, us).
+
+ @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_INSTANCE *Xhc,
+ IN UINT32 Timeout
+ );
+
+/**
+ Set the XHCI host controller to run.
+
+ @param Xhc The XHCI Instance.
+ @param Timeout Time to wait before abort (in microsecond, us).
+
+ @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_INSTANCE *Xhc,
+ IN UINT32 Timeout
+ );
+
+/**
+ Calculate the offset of the XHCI capability.
+
+ @param Xhc The XHCI Instance.
+ @param CapId The XHCI Capability ID.
+
+ @return The offset of XHCI legacy support capability register.
+
+**/
+UINT32
+XhcGetCapabilityAddr (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 CapId
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c new file mode 100644 index 0000000000..c25342dc1f --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c @@ -0,0 +1,3838 @@ +/** @file
+
+ XHCI transfer scheduling routines.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+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"
+
+/**
+ Create a command transfer TRB to support XHCI command interfaces.
+
+ @param Xhc The XHCI Instance.
+ @param CmdTrb The cmd TRB to be executed.
+
+ @return Created URB or NULL.
+
+**/
+URB*
+XhcCreateCmdTrb (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN TRB_TEMPLATE *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_TEMPLATE));
+ Urb->TrbStart->CycleBit = Urb->Ring->RingPCS & BIT0;
+ Urb->TrbEnd = Urb->TrbStart;
+
+ return Urb;
+}
+
+/**
+ Execute a XHCI cmd TRB pointed by CmdTrb.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN TRB_TEMPLATE *CmdTrb,
+ IN UINTN Timeout,
+ OUT TRB_TEMPLATE **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;
+ }
+
+ Status = XhcExecTransfer (Xhc, TRUE, Urb, Timeout);
+ *EvtTrb = Urb->EvtTrb;
+
+ if (Urb->Result == EFI_USB_NOERROR) {
+ Status = EFI_SUCCESS;
+ }
+
+ XhcFreeUrb (Xhc, Urb);
+
+ON_EXIT:
+ return Status;
+}
+
+/**
+ Create a new URB for a new transaction.
+
+ @param Xhc The XHCI Instance
+ @param BusAddr The logical device address assigned by UsbBus driver
+ @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_INSTANCE *Xhc,
+ IN UINT8 BusAddr,
+ 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->BusAddr = BusAddr;
+ Ep->EpAddr = (UINT8)(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);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcCreateUrb: XhcCreateTransferTrb Failed, Status = %r\n", Status));
+ FreePool (Urb);
+ Urb = NULL;
+ }
+
+ return Urb;
+}
+
+/**
+ Free an allocated URB.
+
+ @param Xhc The XHCI device.
+ @param Urb The URB to free.
+
+**/
+VOID
+XhcFreeUrb (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN URB *Urb
+ )
+{
+ if ((Xhc == NULL) || (Urb == NULL)) {
+ return;
+ }
+
+ if (Urb->DataMap != NULL) {
+ Xhc->PciIo->Unmap (Xhc->PciIo, Urb->DataMap);
+ }
+
+ FreePool (Urb);
+}
+
+/**
+ Create a transfer TRB.
+
+ @param Xhc The XHCI Instance
+ @param Urb The urb used to construct the transfer TRB.
+
+ @return Created TRB or NULL
+
+**/
+EFI_STATUS
+XhcCreateTransferTrb (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN URB *Urb
+ )
+{
+ VOID *OutputContext;
+ TRANSFER_RING *EPRing;
+ UINT8 EPType;
+ UINT8 SlotId;
+ UINT8 Dci;
+ TRB *TrbStart;
+ UINTN TotalLen;
+ UINTN Len;
+ UINTN TrbNum;
+ EFI_PCI_IO_PROTOCOL_OPERATION MapOp;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ VOID *Map;
+ EFI_STATUS Status;
+
+ SlotId = XhcBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr);
+ if (SlotId == 0) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Urb->Finished = FALSE;
+ Urb->StartDone = FALSE;
+ Urb->EndDone = FALSE;
+ Urb->Completed = 0;
+ Urb->Result = EFI_USB_NOERROR;
+
+ Dci = XhcEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));
+ ASSERT (Dci < 32);
+ EPRing = (TRANSFER_RING *)(UINTN) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1];
+ Urb->Ring = EPRing;
+ OutputContext = Xhc->UsbDevContext[SlotId].OutputContext;
+ if (Xhc->HcCParams.Data.Csz == 0) {
+ EPType = (UINT8) ((DEVICE_CONTEXT *)OutputContext)->EP[Dci-1].EPType;
+ } else {
+ EPType = (UINT8) ((DEVICE_CONTEXT_64 *)OutputContext)->EP[Dci-1].EPType;
+ }
+
+ if (Urb->Data != NULL) {
+ if (((UINT8) (Urb->Ep.Direction)) == EfiUsbDataIn) {
+ MapOp = EfiPciIoOperationBusMasterWrite;
+ } else {
+ MapOp = EfiPciIoOperationBusMasterRead;
+ }
+
+ Len = Urb->DataLen;
+ Status = Xhc->PciIo->Map (Xhc->PciIo, MapOp, Urb->Data, &Len, &PhyAddr, &Map);
+
+ if (EFI_ERROR (Status) || (Len != Urb->DataLen)) {
+ DEBUG ((EFI_D_ERROR, "XhcCreateTransferTrb: Fail to map Urb->Data.\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);
+ Urb->DataMap = Map;
+ }
+
+ //
+ // Construct the TRB
+ //
+ XhcSyncTrsRing (Xhc, EPRing);
+ Urb->TrbStart = EPRing->RingEnqueue;
+ switch (EPType) {
+ case ED_CONTROL_BIDIR:
+ //
+ // For control transfer, create SETUP_STAGE_TRB first.
+ //
+ TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue;
+ TrbStart->TrbCtrSetup.bmRequestType = Urb->Request->RequestType;
+ TrbStart->TrbCtrSetup.bRequest = Urb->Request->Request;
+ TrbStart->TrbCtrSetup.wValue = Urb->Request->Value;
+ TrbStart->TrbCtrSetup.wIndex = Urb->Request->Index;
+ TrbStart->TrbCtrSetup.wLength = Urb->Request->Length;
+ TrbStart->TrbCtrSetup.Length = 8;
+ TrbStart->TrbCtrSetup.IntTarget = 0;
+ TrbStart->TrbCtrSetup.IOC = 1;
+ TrbStart->TrbCtrSetup.IDT = 1;
+ TrbStart->TrbCtrSetup.Type = TRB_TYPE_SETUP_STAGE;
+ if (Urb->Ep.Direction == EfiUsbDataIn) {
+ TrbStart->TrbCtrSetup.TRT = 3;
+ } else if (Urb->Ep.Direction == EfiUsbDataOut) {
+ TrbStart->TrbCtrSetup.TRT = 2;
+ } else {
+ TrbStart->TrbCtrSetup.TRT = 0;
+ }
+ //
+ // Update the cycle bit
+ //
+ TrbStart->TrbCtrSetup.CycleBit = EPRing->RingPCS & BIT0;
+ Urb->TrbNum++;
+
+ //
+ // For control transfer, create DATA_STAGE_TRB.
+ //
+ if (Urb->DataLen > 0) {
+ XhcSyncTrsRing (Xhc, EPRing);
+ TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue;
+ TrbStart->TrbCtrData.TRBPtrLo = XHC_LOW_32BIT(Urb->DataPhy);
+ TrbStart->TrbCtrData.TRBPtrHi = XHC_HIGH_32BIT(Urb->DataPhy);
+ TrbStart->TrbCtrData.Length = (UINT32) Urb->DataLen;
+ TrbStart->TrbCtrData.TDSize = 0;
+ TrbStart->TrbCtrData.IntTarget = 0;
+ TrbStart->TrbCtrData.ISP = 1;
+ TrbStart->TrbCtrData.IOC = 1;
+ TrbStart->TrbCtrData.IDT = 0;
+ TrbStart->TrbCtrData.CH = 0;
+ TrbStart->TrbCtrData.Type = TRB_TYPE_DATA_STAGE;
+ if (Urb->Ep.Direction == EfiUsbDataIn) {
+ TrbStart->TrbCtrData.DIR = 1;
+ } else if (Urb->Ep.Direction == EfiUsbDataOut) {
+ TrbStart->TrbCtrData.DIR = 0;
+ } else {
+ TrbStart->TrbCtrData.DIR = 0;
+ }
+ //
+ // Update the cycle bit
+ //
+ TrbStart->TrbCtrData.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 = (TRB *)(UINTN)EPRing->RingEnqueue;
+ TrbStart->TrbCtrStatus.IntTarget = 0;
+ TrbStart->TrbCtrStatus.IOC = 1;
+ TrbStart->TrbCtrStatus.CH = 0;
+ TrbStart->TrbCtrStatus.Type = TRB_TYPE_STATUS_STAGE;
+ if (Urb->Ep.Direction == EfiUsbDataIn) {
+ TrbStart->TrbCtrStatus.DIR = 0;
+ } else if (Urb->Ep.Direction == EfiUsbDataOut) {
+ TrbStart->TrbCtrStatus.DIR = 1;
+ } else {
+ TrbStart->TrbCtrStatus.DIR = 0;
+ }
+ //
+ // Update the cycle bit
+ //
+ TrbStart->TrbCtrStatus.CycleBit = EPRing->RingPCS & BIT0;
+ //
+ // Update the enqueue pointer
+ //
+ XhcSyncTrsRing (Xhc, EPRing);
+ Urb->TrbNum++;
+ Urb->TrbEnd = (TRB_TEMPLATE *)(UINTN)TrbStart;
+
+ break;
+
+ case ED_BULK_OUT:
+ case ED_BULK_IN:
+ TotalLen = 0;
+ Len = 0;
+ TrbNum = 0;
+ TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue;
+ while (TotalLen < Urb->DataLen) {
+ if ((TotalLen + 0x10000) >= Urb->DataLen) {
+ Len = Urb->DataLen - TotalLen;
+ } else {
+ Len = 0x10000;
+ }
+ TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue;
+ TrbStart->TrbNormal.TRBPtrLo = XHC_LOW_32BIT((UINT8 *) Urb->DataPhy + TotalLen);
+ TrbStart->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT((UINT8 *) Urb->DataPhy + TotalLen);
+ TrbStart->TrbNormal.Length = (UINT32) Len;
+ TrbStart->TrbNormal.TDSize = 0;
+ TrbStart->TrbNormal.IntTarget = 0;
+ TrbStart->TrbNormal.ISP = 1;
+ TrbStart->TrbNormal.IOC = 1;
+ TrbStart->TrbNormal.Type = TRB_TYPE_NORMAL;
+ //
+ // Update the cycle bit
+ //
+ TrbStart->TrbNormal.CycleBit = EPRing->RingPCS & BIT0;
+
+ XhcSyncTrsRing (Xhc, EPRing);
+ TrbNum++;
+ TotalLen += Len;
+ }
+
+ Urb->TrbNum = TrbNum;
+ Urb->TrbEnd = (TRB_TEMPLATE *)(UINTN)TrbStart;
+ break;
+
+ case ED_INTERRUPT_OUT:
+ case ED_INTERRUPT_IN:
+ TotalLen = 0;
+ Len = 0;
+ TrbNum = 0;
+ TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue;
+ while (TotalLen < Urb->DataLen) {
+ if ((TotalLen + 0x10000) >= Urb->DataLen) {
+ Len = Urb->DataLen - TotalLen;
+ } else {
+ Len = 0x10000;
+ }
+ TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue;
+ TrbStart->TrbNormal.TRBPtrLo = XHC_LOW_32BIT((UINT8 *) Urb->DataPhy + TotalLen);
+ TrbStart->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT((UINT8 *) Urb->DataPhy + TotalLen);
+ TrbStart->TrbNormal.Length = (UINT32) Len;
+ TrbStart->TrbNormal.TDSize = 0;
+ TrbStart->TrbNormal.IntTarget = 0;
+ TrbStart->TrbNormal.ISP = 1;
+ TrbStart->TrbNormal.IOC = 1;
+ TrbStart->TrbNormal.Type = TRB_TYPE_NORMAL;
+ //
+ // Update the cycle bit
+ //
+ TrbStart->TrbNormal.CycleBit = EPRing->RingPCS & BIT0;
+
+ XhcSyncTrsRing (Xhc, EPRing);
+ TrbNum++;
+ TotalLen += Len;
+ }
+
+ Urb->TrbNum = TrbNum;
+ Urb->TrbEnd = (TRB_TEMPLATE *)(UINTN)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 Instance to be initialized.
+
+**/
+VOID
+XhcInitSched (
+ IN USB_XHCI_INSTANCE *Xhc
+ )
+{
+ VOID *Dcbaa;
+ EFI_PHYSICAL_ADDRESS DcbaaPhy;
+ UINT64 CmdRing;
+ EFI_PHYSICAL_ADDRESS CmdRingPhy;
+ UINTN Entries;
+ UINT32 MaxScratchpadBufs;
+ UINT64 *ScratchBuf;
+ EFI_PHYSICAL_ADDRESS ScratchPhy;
+ UINT64 *ScratchEntry;
+ EFI_PHYSICAL_ADDRESS ScratchEntryPhy;
+ UINT32 Index;
+ UINTN *ScratchEntryMap;
+ EFI_STATUS Status;
+
+ //
+ // Initialize memory management.
+ //
+ Xhc->MemPool = UsbHcInitMemPool (Xhc->PciIo);
+ ASSERT (Xhc->MemPool != NULL);
+
+ //
+ // 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 = UsbHcAllocateMem (Xhc->MemPool, Entries);
+ ASSERT (Dcbaa != NULL);
+ ZeroMem (Dcbaa, Entries);
+
+ //
+ // 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 <= 1023);
+ if (MaxScratchpadBufs != 0) {
+ //
+ // Allocate the buffer to record the Mapping for each scratch buffer in order to Unmap them
+ //
+ ScratchEntryMap = AllocateZeroPool (sizeof (UINTN) * MaxScratchpadBufs);
+ ASSERT (ScratchEntryMap != NULL);
+ Xhc->ScratchEntryMap = ScratchEntryMap;
+
+ //
+ // Allocate the buffer to record the host address for each entry
+ //
+ ScratchEntry = AllocateZeroPool (sizeof (UINT64) * MaxScratchpadBufs);
+ ASSERT (ScratchEntry != NULL);
+ Xhc->ScratchEntry = ScratchEntry;
+
+ ScratchPhy = 0;
+ Status = UsbHcAllocateAlignedPages (
+ Xhc->PciIo,
+ EFI_SIZE_TO_PAGES (MaxScratchpadBufs * sizeof (UINT64)),
+ Xhc->PageSize,
+ (VOID **) &ScratchBuf,
+ &ScratchPhy,
+ &Xhc->ScratchMap
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ ZeroMem (ScratchBuf, MaxScratchpadBufs * sizeof (UINT64));
+ Xhc->ScratchBuf = ScratchBuf;
+
+ //
+ // Allocate each scratch buffer
+ //
+ for (Index = 0; Index < MaxScratchpadBufs; Index++) {
+ ScratchEntryPhy = 0;
+ Status = UsbHcAllocateAlignedPages (
+ Xhc->PciIo,
+ EFI_SIZE_TO_PAGES (Xhc->PageSize),
+ Xhc->PageSize,
+ (VOID **) &ScratchEntry[Index],
+ &ScratchEntryPhy,
+ (VOID **) &ScratchEntryMap[Index]
+ );
+ ASSERT_EFI_ERROR (Status);
+ ZeroMem ((VOID *)(UINTN)ScratchEntry[Index], Xhc->PageSize);
+ //
+ // Fill with the PCI device address
+ //
+ *ScratchBuf++ = ScratchEntryPhy;
+ }
+ //
+ // 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) ScratchPhy;
+ }
+
+ //
+ // 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;
+ //
+ // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
+ // So divide it to two 32-bytes width register access.
+ //
+ DcbaaPhy = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Dcbaa, Entries);
+ XhcWriteOpReg (Xhc, XHC_DCBAAP_OFFSET, XHC_LOW_32BIT(DcbaaPhy));
+ XhcWriteOpReg (Xhc, XHC_DCBAAP_OFFSET + 4, XHC_HIGH_32BIT (DcbaaPhy));
+
+ DEBUG ((EFI_D_INFO, "XhcInitSched:DCBAA=0x%x\n", (UINT64)(UINTN)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;
+ CmdRingPhy = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, (VOID *)(UINTN) CmdRing, sizeof (TRB_TEMPLATE) * CMD_RING_TRB_NUMBER);
+ ASSERT ((CmdRingPhy & 0x3F) == 0);
+ CmdRingPhy |= XHC_CRCR_RCS;
+ //
+ // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
+ // So divide it to two 32-bytes width register access.
+ //
+ XhcWriteOpReg (Xhc, XHC_CRCR_OFFSET, XHC_LOW_32BIT(CmdRingPhy));
+ XhcWriteOpReg (Xhc, XHC_CRCR_OFFSET + 4, XHC_HIGH_32BIT (CmdRingPhy));
+
+ 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, &Xhc->EventRing);
+ DEBUG ((EFI_D_INFO, "XhcInitSched:XHC_EVENTRING=0x%x\n", Xhc->EventRing.EventRingSeg0));
+}
+
+/**
+ 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 Instance.
+ @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_INSTANCE *Xhc,
+ IN URB *Urb
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Dci;
+ UINT8 SlotId;
+
+ Status = EFI_SUCCESS;
+ SlotId = XhcBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr);
+ if (SlotId == 0) {
+ return EFI_DEVICE_ERROR;
+ }
+ Dci = XhcEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));
+ ASSERT (Dci < 32);
+
+ 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
+ //
+ Status = XhcResetEndpoint(Xhc, SlotId, Dci);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcRecoverHaltedEndpoint: Reset Endpoint Failed, Status = %r\n", Status));
+ goto Done;
+ }
+
+ //
+ // 2)Set dequeue pointer
+ //
+ Status = XhcSetTrDequeuePointer(Xhc, SlotId, Dci, Urb);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcRecoverHaltedEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status));
+ goto Done;
+ }
+
+ //
+ // 3)Ring the doorbell to transit from stop to active
+ //
+ XhcRingDoorBell (Xhc, SlotId, Dci);
+
+Done:
+ return Status;
+}
+
+/**
+ System software shall use a Stop Endpoint Command (section 4.6.9) and the Set TR Dequeue Pointer
+ Command (section 4.6.10) to remove the timed-out TDs from the xHC transfer ring. 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 Instance.
+ @param Urb The urb which doesn't get completed in a specified timeout range.
+
+ @retval EFI_SUCCESS The dequeuing of the TDs is successful.
+ @retval Others Failed to stop the endpoint and dequeue the TDs.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcDequeueTrbFromEndpoint (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN URB *Urb
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Dci;
+ UINT8 SlotId;
+
+ Status = EFI_SUCCESS;
+ SlotId = XhcBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr);
+ if (SlotId == 0) {
+ return EFI_DEVICE_ERROR;
+ }
+ Dci = XhcEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));
+ ASSERT (Dci < 32);
+
+ DEBUG ((EFI_D_INFO, "Stop Slot = %x,Dci = %x\n", SlotId, Dci));
+
+ //
+ // 1) Send Stop endpoint command to stop xHC from executing of the TDs on the endpoint
+ //
+ Status = XhcStopEndpoint(Xhc, SlotId, Dci);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcDequeueTrbFromEndpoint: Stop Endpoint Failed, Status = %r\n", Status));
+ goto Done;
+ }
+
+ //
+ // 2)Set dequeue pointer
+ //
+ Status = XhcSetTrDequeuePointer(Xhc, SlotId, Dci, Urb);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcDequeueTrbFromEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status));
+ goto Done;
+ }
+
+ //
+ // 3)Ring the doorbell to transit from stop to active
+ //
+ XhcRingDoorBell (Xhc, SlotId, Dci);
+
+Done:
+ return Status;
+}
+
+/**
+ Create XHCI event ring.
+
+ @param Xhc The XHCI Instance.
+ @param EventRing The created event ring.
+
+**/
+VOID
+CreateEventRing (
+ IN USB_XHCI_INSTANCE *Xhc,
+ OUT EVENT_RING *EventRing
+ )
+{
+ VOID *Buf;
+ EVENT_RING_SEG_TABLE_ENTRY *ERSTBase;
+ UINTN Size;
+ EFI_PHYSICAL_ADDRESS ERSTPhy;
+ EFI_PHYSICAL_ADDRESS DequeuePhy;
+
+ ASSERT (EventRing != NULL);
+
+ Size = sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER;
+ Buf = UsbHcAllocateMem (Xhc->MemPool, Size);
+ ASSERT (Buf != NULL);
+ ASSERT (((UINTN) Buf & 0x3F) == 0);
+ ZeroMem (Buf, Size);
+
+ EventRing->EventRingSeg0 = Buf;
+ EventRing->TrbNumber = EVENT_RING_TRB_NUMBER;
+ EventRing->EventRingDequeue = (TRB_TEMPLATE *) EventRing->EventRingSeg0;
+ EventRing->EventRingEnqueue = (TRB_TEMPLATE *) EventRing->EventRingSeg0;
+
+ DequeuePhy = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Buf, Size);
+
+ //
+ // 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;
+
+ Size = sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER;
+ Buf = UsbHcAllocateMem (Xhc->MemPool, Size);
+ ASSERT (Buf != NULL);
+ ASSERT (((UINTN) Buf & 0x3F) == 0);
+ ZeroMem (Buf, Size);
+
+ ERSTBase = (EVENT_RING_SEG_TABLE_ENTRY *) Buf;
+ EventRing->ERSTBase = ERSTBase;
+ ERSTBase->PtrLo = XHC_LOW_32BIT (DequeuePhy);
+ ERSTBase->PtrHi = XHC_HIGH_32BIT (DequeuePhy);
+ ERSTBase->RingTrbSize = EVENT_RING_TRB_NUMBER;
+
+ ERSTPhy = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, ERSTBase, Size);
+
+ //
+ // Program the Interrupter Event Ring Segment Table Size (ERSTSZ) register (5.5.2.3.1)
+ //
+ XhcWriteRuntimeReg (
+ Xhc,
+ XHC_ERSTSZ_OFFSET,
+ ERST_NUMBER
+ );
+ //
+ // Program the Interrupter Event Ring Dequeue Pointer (ERDP) register (5.5.2.3.3)
+ //
+ // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
+ // So divide it to two 32-bytes width register access.
+ //
+ XhcWriteRuntimeReg (
+ Xhc,
+ XHC_ERDP_OFFSET,
+ XHC_LOW_32BIT((UINT64)(UINTN)DequeuePhy)
+ );
+ XhcWriteRuntimeReg (
+ Xhc,
+ XHC_ERDP_OFFSET + 4,
+ XHC_HIGH_32BIT((UINT64)(UINTN)DequeuePhy)
+ );
+ //
+ // Program the Interrupter Event Ring Segment Table Base Address (ERSTBA) register(5.5.2.3.2)
+ //
+ // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
+ // So divide it to two 32-bytes width register access.
+ //
+ XhcWriteRuntimeReg (
+ Xhc,
+ XHC_ERSTBA_OFFSET,
+ XHC_LOW_32BIT((UINT64)(UINTN)ERSTPhy)
+ );
+ XhcWriteRuntimeReg (
+ Xhc,
+ XHC_ERSTBA_OFFSET + 4,
+ XHC_HIGH_32BIT((UINT64)(UINTN)ERSTPhy)
+ );
+ //
+ // Need set IMAN IE bit to enble the ring interrupt
+ //
+ XhcSetRuntimeRegBit (Xhc, XHC_IMAN_OFFSET, XHC_IMAN_IE);
+}
+
+/**
+ Create XHCI transfer ring.
+
+ @param Xhc The XHCI Instance.
+ @param TrbNum The number of TRB in the ring.
+ @param TransferRing The created transfer ring.
+
+**/
+VOID
+CreateTransferRing (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINTN TrbNum,
+ OUT TRANSFER_RING *TransferRing
+ )
+{
+ VOID *Buf;
+ LINK_TRB *EndTrb;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ Buf = UsbHcAllocateMem (Xhc->MemPool, sizeof (TRB_TEMPLATE) * TrbNum);
+ ASSERT (Buf != NULL);
+ ASSERT (((UINTN) Buf & 0x3F) == 0);
+ ZeroMem (Buf, sizeof (TRB_TEMPLATE) * TrbNum);
+
+ TransferRing->RingSeg0 = Buf;
+ TransferRing->TrbNumber = TrbNum;
+ TransferRing->RingEnqueue = (TRB_TEMPLATE *) TransferRing->RingSeg0;
+ TransferRing->RingDequeue = (TRB_TEMPLATE *) 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 = (LINK_TRB *) ((UINTN)Buf + sizeof (TRB_TEMPLATE) * (TrbNum - 1));
+ EndTrb->Type = TRB_TYPE_LINK;
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Buf, sizeof (TRB_TEMPLATE) * TrbNum);
+ EndTrb->PtrLo = XHC_LOW_32BIT (PhyAddr);
+ EndTrb->PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ //
+ // 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 Instance.
+ @param EventRing The event ring to be freed.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcFreeEventRing (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN EVENT_RING *EventRing
+)
+{
+ if(EventRing->EventRingSeg0 == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Free EventRing Segment 0
+ //
+ UsbHcFreeMem (Xhc->MemPool, EventRing->EventRingSeg0, sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER);
+
+ //
+ // Free ESRT table
+ //
+ UsbHcFreeMem (Xhc->MemPool, EventRing->ERSTBase, sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER);
+ return EFI_SUCCESS;
+}
+
+/**
+ Free the resouce allocated at initializing schedule.
+
+ @param Xhc The XHCI Instance.
+
+**/
+VOID
+XhcFreeSched (
+ IN USB_XHCI_INSTANCE *Xhc
+ )
+{
+ UINT32 Index;
+ UINT64 *ScratchEntry;
+
+ if (Xhc->ScratchBuf != NULL) {
+ ScratchEntry = Xhc->ScratchEntry;
+ for (Index = 0; Index < Xhc->MaxScratchpadBufs; Index++) {
+ //
+ // Free Scratchpad Buffers
+ //
+ UsbHcFreeAlignedPages (Xhc->PciIo, (VOID*)(UINTN)ScratchEntry[Index], EFI_SIZE_TO_PAGES (Xhc->PageSize), (VOID *) Xhc->ScratchEntryMap[Index]);
+ }
+ //
+ // Free Scratchpad Buffer Array
+ //
+ UsbHcFreeAlignedPages (Xhc->PciIo, Xhc->ScratchBuf, EFI_SIZE_TO_PAGES (Xhc->MaxScratchpadBufs * sizeof (UINT64)), Xhc->ScratchMap);
+ FreePool (Xhc->ScratchEntryMap);
+ FreePool (Xhc->ScratchEntry);
+ }
+
+ if (Xhc->CmdRing.RingSeg0 != NULL) {
+ UsbHcFreeMem (Xhc->MemPool, Xhc->CmdRing.RingSeg0, sizeof (TRB_TEMPLATE) * CMD_RING_TRB_NUMBER);
+ Xhc->CmdRing.RingSeg0 = NULL;
+ }
+
+ XhcFreeEventRing (Xhc,&Xhc->EventRing);
+
+ if (Xhc->DCBAA != NULL) {
+ UsbHcFreeMem (Xhc->MemPool, Xhc->DCBAA, (Xhc->MaxSlotsEn + 1) * sizeof(UINT64));
+ Xhc->DCBAA = NULL;
+ }
+
+ //
+ // Free memory pool at last
+ //
+ if (Xhc->MemPool != NULL) {
+ UsbHcFreeMemPool (Xhc->MemPool);
+ Xhc->MemPool = NULL;
+ }
+}
+
+/**
+ Check if the Trb is a transaction of the URBs in XHCI's asynchronous transfer list.
+
+ @param Xhc The XHCI Instance.
+ @param Trb The TRB to be checked.
+ @param Urb The pointer to the matched Urb.
+
+ @retval TRUE The Trb is matched with a transaction of the URBs in the async list.
+ @retval FALSE The Trb is not matched with any URBs in the async list.
+
+**/
+BOOLEAN
+IsAsyncIntTrb (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN TRB_TEMPLATE *Trb,
+ OUT URB **Urb
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ TRB_TEMPLATE *CheckedTrb;
+ URB *CheckedUrb;
+ UINTN Index;
+
+ EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) {
+ CheckedUrb = EFI_LIST_CONTAINER (Entry, URB, UrbList);
+ CheckedTrb = CheckedUrb->TrbStart;
+ for (Index = 0; Index < CheckedUrb->TrbNum; Index++) {
+ if (Trb == CheckedTrb) {
+ *Urb = CheckedUrb;
+ return TRUE;
+ }
+ CheckedTrb++;
+ if ((UINTN)CheckedTrb >= ((UINTN) CheckedUrb->Ring->RingSeg0 + sizeof (TRB_TEMPLATE) * CheckedUrb->Ring->TrbNumber)) {
+ CheckedTrb = (TRB_TEMPLATE*) CheckedUrb->Ring->RingSeg0;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Check if the Trb is a transaction of the URB.
+
+ @param Trb The TRB to be checked
+ @param Urb The transfer ring to be checked.
+
+ @retval TRUE It is a transaction of the URB.
+ @retval FALSE It is not any transaction of the URB.
+
+**/
+BOOLEAN
+IsTransferRingTrb (
+ IN TRB_TEMPLATE *Trb,
+ IN URB *Urb
+ )
+{
+ TRB_TEMPLATE *CheckedTrb;
+ UINTN Index;
+
+ CheckedTrb = Urb->Ring->RingSeg0;
+
+ ASSERT (Urb->Ring->TrbNumber == CMD_RING_TRB_NUMBER || Urb->Ring->TrbNumber == TR_RING_TRB_NUMBER);
+
+ for (Index = 0; Index < Urb->Ring->TrbNumber; Index++) {
+ if (Trb == CheckedTrb) {
+ return TRUE;
+ }
+ CheckedTrb++;
+ }
+
+ return FALSE;
+}
+
+/**
+ Check the URB's execution result and update the URB's
+ result accordingly.
+
+ @param Xhc The XHCI Instance.
+ @param Urb The URB to check result.
+
+ @return Whether the result of URB transfer is finialized.
+
+**/
+BOOLEAN
+XhcCheckUrbResult (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN URB *Urb
+ )
+{
+ EVT_TRB_TRANSFER *EvtTrb;
+ TRB_TEMPLATE *TRBPtr;
+ UINTN Index;
+ UINT8 TRBType;
+ EFI_STATUS Status;
+ URB *AsyncUrb;
+ URB *CheckedUrb;
+ UINT64 XhcDequeue;
+ UINT32 High;
+ UINT32 Low;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ ASSERT ((Xhc != NULL) && (Urb != NULL));
+
+ Status = EFI_SUCCESS;
+ AsyncUrb = NULL;
+
+ if (Urb->Finished) {
+ goto EXIT;
+ }
+
+ EvtTrb = NULL;
+
+ if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) {
+ Urb->Result |= EFI_USB_ERR_SYSTEM;
+ goto EXIT;
+ }
+
+ //
+ // Traverse the event ring to find out all new events from the previous check.
+ //
+ XhcSyncEventRing (Xhc, &Xhc->EventRing);
+ for (Index = 0; Index < Xhc->EventRing.TrbNumber; Index++) {
+ Status = XhcCheckNewEvent (Xhc, &Xhc->EventRing, ((TRB_TEMPLATE **)&EvtTrb));
+ if (Status == EFI_NOT_READY) {
+ //
+ // All new events are handled, return directly.
+ //
+ goto EXIT;
+ }
+
+ //
+ // Only handle COMMAND_COMPLETETION_EVENT and TRANSFER_EVENT.
+ //
+ if ((EvtTrb->Type != TRB_TYPE_COMMAND_COMPLT_EVENT) && (EvtTrb->Type != TRB_TYPE_TRANS_EVENT)) {
+ continue;
+ }
+
+ //
+ // Need convert pci device address to host address
+ //
+ PhyAddr = (EFI_PHYSICAL_ADDRESS)(EvtTrb->TRBPtrLo | LShiftU64 ((UINT64) EvtTrb->TRBPtrHi, 32));
+ TRBPtr = (TRB_TEMPLATE *)(UINTN) UsbHcGetHostAddrForPciAddr (Xhc->MemPool, (VOID *)(UINTN) PhyAddr, sizeof (TRB_TEMPLATE));
+
+ //
+ // Update the status of Urb according to the finished event regardless of whether
+ // the urb is current checked one or in the XHCI's async transfer list.
+ // This way is used to avoid that those completed async transfer events don't get
+ // handled in time and are flushed by newer coming events.
+ //
+ if (IsTransferRingTrb (TRBPtr, Urb)) {
+ CheckedUrb = Urb;
+ } else if (IsAsyncIntTrb (Xhc, TRBPtr, &AsyncUrb)) {
+ CheckedUrb = AsyncUrb;
+ } else {
+ continue;
+ }
+
+ switch (EvtTrb->Completecode) {
+ case TRB_COMPLETION_STALL_ERROR:
+ CheckedUrb->Result |= EFI_USB_ERR_STALL;
+ CheckedUrb->Finished = TRUE;
+ DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: STALL_ERROR! Completecode = %x\n",EvtTrb->Completecode));
+ goto EXIT;
+
+ case TRB_COMPLETION_BABBLE_ERROR:
+ CheckedUrb->Result |= EFI_USB_ERR_BABBLE;
+ CheckedUrb->Finished = TRUE;
+ DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: BABBLE_ERROR! Completecode = %x\n",EvtTrb->Completecode));
+ goto EXIT;
+
+ case TRB_COMPLETION_DATA_BUFFER_ERROR:
+ CheckedUrb->Result |= EFI_USB_ERR_BUFFER;
+ CheckedUrb->Finished = TRUE;
+ DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: ERR_BUFFER! Completecode = %x\n",EvtTrb->Completecode));
+ goto EXIT;
+
+ case TRB_COMPLETION_USB_TRANSACTION_ERROR:
+ CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT;
+ CheckedUrb->Finished = TRUE;
+ DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n",EvtTrb->Completecode));
+ goto EXIT;
+
+ case TRB_COMPLETION_SHORT_PACKET:
+ case TRB_COMPLETION_SUCCESS:
+ if (EvtTrb->Completecode == 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)) {
+ CheckedUrb->Completed += (((TRANSFER_TRB_NORMAL*)TRBPtr)->Length - EvtTrb->Length);
+ }
+
+ break;
+
+ default:
+ DEBUG ((EFI_D_ERROR, "Transfer Default Error Occur! Completecode = 0x%x!\n",EvtTrb->Completecode));
+ CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT;
+ CheckedUrb->Finished = TRUE;
+ goto EXIT;
+ }
+
+ //
+ // Only check first and end Trb event address
+ //
+ if (TRBPtr == CheckedUrb->TrbStart) {
+ CheckedUrb->StartDone = TRUE;
+ }
+
+ if (TRBPtr == CheckedUrb->TrbEnd) {
+ CheckedUrb->EndDone = TRUE;
+ }
+
+ if (CheckedUrb->StartDone && CheckedUrb->EndDone) {
+ CheckedUrb->Finished = TRUE;
+ CheckedUrb->EvtTrb = (TRB_TEMPLATE *)EvtTrb;
+ }
+ }
+
+EXIT:
+
+ //
+ // Advance event ring to last available entry
+ //
+ // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
+ // So divide it to two 32-bytes width register access.
+ //
+ Low = XhcReadRuntimeReg (Xhc, XHC_ERDP_OFFSET);
+ High = XhcReadRuntimeReg (Xhc, XHC_ERDP_OFFSET + 4);
+ XhcDequeue = (UINT64)(LShiftU64((UINT64)High, 32) | Low);
+
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Xhc->EventRing.EventRingDequeue, sizeof (TRB_TEMPLATE));
+
+ if ((XhcDequeue & (~0x0F)) != (PhyAddr & (~0x0F))) {
+ //
+ // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
+ // So divide it to two 32-bytes width register access.
+ //
+ XhcWriteRuntimeReg (Xhc, XHC_ERDP_OFFSET, XHC_LOW_32BIT (PhyAddr) | BIT3);
+ XhcWriteRuntimeReg (Xhc, XHC_ERDP_OFFSET + 4, XHC_HIGH_32BIT (PhyAddr));
+ }
+
+ return Urb->Finished;
+}
+
+
+/**
+ Execute the transfer by polling the URB. This is a synchronous operation.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN BOOLEAN CmdTransfer,
+ IN URB *Urb,
+ IN UINTN Timeout
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT64 Loop;
+ UINT8 SlotId;
+ UINT8 Dci;
+ BOOLEAN Finished;
+
+ if (CmdTransfer) {
+ SlotId = 0;
+ Dci = 0;
+ } else {
+ SlotId = XhcBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr);
+ if (SlotId == 0) {
+ return EFI_DEVICE_ERROR;
+ }
+ Dci = XhcEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));
+ ASSERT (Dci < 32);
+ }
+
+ Status = EFI_SUCCESS;
+ Loop = Timeout * XHC_1_MILLISECOND;
+ if (Timeout == 0) {
+ Loop = 0xFFFFFFFF;
+ }
+
+ XhcRingDoorBell (Xhc, SlotId, Dci);
+
+ for (Index = 0; Index < Loop; Index++) {
+ Finished = XhcCheckUrbResult (Xhc, Urb);
+ if (Finished) {
+ break;
+ }
+ gBS->Stall (XHC_1_MICROSECOND);
+ }
+
+ if (Index == Loop) {
+ Urb->Result = EFI_USB_ERR_TIMEOUT;
+ Status = EFI_TIMEOUT;
+ } else if (Urb->Result != EFI_USB_NOERROR) {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
+/**
+ Delete a single asynchronous interrupt transfer for
+ the device and endpoint.
+
+ @param Xhc The XHCI Instance.
+ @param BusAddr The logical device address assigned by UsbBus driver.
+ @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_INSTANCE *Xhc,
+ IN UINT8 BusAddr,
+ IN UINT8 EpNum
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ URB *Urb;
+ EFI_USB_DATA_DIRECTION Direction;
+
+ Direction = ((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut;
+ EpNum &= 0x0F;
+
+ Urb = NULL;
+
+ EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) {
+ Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);
+ if ((Urb->Ep.BusAddr == BusAddr) &&
+ (Urb->Ep.EpAddr == EpNum) &&
+ (Urb->Ep.Direction == Direction)) {
+ RemoveEntryList (&Urb->UrbList);
+ FreePool (Urb->Data);
+ XhcFreeUrb (Xhc, Urb);
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Remove all the asynchronous interrutp transfers.
+
+ @param Xhc The XHCI Instance.
+
+**/
+VOID
+XhciDelAllAsyncIntTransfers (
+ IN USB_XHCI_INSTANCE *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);
+ XhcFreeUrb (Xhc, Urb);
+ }
+}
+
+/**
+ Update the queue head for next round of asynchronous transfer
+
+ @param Xhc The XHCI Instance.
+ @param Urb The URB to update
+
+**/
+VOID
+XhcUpdateAsyncRequest (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN URB *Urb
+ )
+{
+ EFI_STATUS Status;
+
+ if (Urb->Result == EFI_USB_NOERROR) {
+ Status = XhcCreateTransferTrb (Xhc, Urb);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+ Status = RingIntTransferDoorBell (Xhc, Urb);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+ }
+}
+
+/**
+ Flush data from PCI controller specific address to mapped system
+ memory address.
+
+ @param Xhc The XHCI device.
+ @param Urb The URB to unmap.
+
+ @retval EFI_SUCCESS Success to flush data to mapped system memory.
+ @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
+
+**/
+EFI_STATUS
+XhcFlushAsyncIntMap (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN URB *Urb
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ EFI_PCI_IO_PROTOCOL_OPERATION MapOp;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINTN Len;
+ VOID *Map;
+
+ PciIo = Xhc->PciIo;
+ Len = Urb->DataLen;
+
+ if (Urb->Ep.Direction == EfiUsbDataIn) {
+ MapOp = EfiPciIoOperationBusMasterWrite;
+ } else {
+ MapOp = EfiPciIoOperationBusMasterRead;
+ }
+
+ if (Urb->DataMap != NULL) {
+ Status = PciIo->Unmap (PciIo, Urb->DataMap);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ Urb->DataMap = NULL;
+
+ Status = PciIo->Map (PciIo, MapOp, Urb->Data, &Len, &PhyAddr, &Map);
+ if (EFI_ERROR (Status) || (Len != Urb->DataLen)) {
+ goto ON_ERROR;
+ }
+
+ Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);
+ Urb->DataMap = Map;
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ return EFI_DEVICE_ERROR;
+}
+
+/**
+ Interrupt transfer periodic check handler.
+
+ @param Event Interrupt event.
+ @param Context Pointer to USB_XHCI_INSTANCE.
+
+**/
+VOID
+EFIAPI
+XhcMonitorAsyncRequests (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ USB_XHCI_INSTANCE *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_INSTANCE*) 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 = XhcBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr);
+ if (SlotId == 0) {
+ continue;
+ }
+
+ //
+ // Check the result of URB execution. If it is still
+ // active, check the next one.
+ //
+ XhcCheckUrbResult (Xhc, Urb);
+
+ if (!Urb->Finished) {
+ continue;
+ }
+
+ //
+ // Flush any PCI posted write transactions from a PCI host
+ // bridge to system memory.
+ //
+ Status = XhcFlushAsyncIntMap (Xhc, Urb);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
+ }
+
+ //
+ // 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 = AllocateZeroPool (Urb->Completed);
+
+ if (ProcBuf == NULL) {
+ XhcUpdateAsyncRequest (Xhc, Urb);
+ continue;
+ }
+
+ CopyMem (ProcBuf, Urb->Data, Urb->Completed);
+ }
+
+ //
+ // 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);
+ }
+
+ XhcUpdateAsyncRequest (Xhc, Urb);
+ }
+ gBS->RestoreTPL (OldTpl);
+}
+
+/**
+ Monitor the port status change. Enable/Disable device slot if there is a device attached/detached.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *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 ((PortState->PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) {
+ return EFI_SUCCESS;
+ }
+
+ if (ParentRouteChart.Dword == 0) {
+ RouteChart.Route.RouteString = 0;
+ RouteChart.Route.RootPortNum = Port + 1;
+ RouteChart.Route.TierNum = 1;
+ } else {
+ if(Port < 14) {
+ RouteChart.Route.RouteString = ParentRouteChart.Route.RouteString | (Port << (4 * (ParentRouteChart.Route.TierNum - 1)));
+ } else {
+ RouteChart.Route.RouteString = ParentRouteChart.Route.RouteString | (15 << (4 * (ParentRouteChart.Route.TierNum - 1)));
+ }
+ RouteChart.Route.RootPortNum = ParentRouteChart.Route.RootPortNum;
+ RouteChart.Route.TierNum = ParentRouteChart.Route.TierNum + 1;
+ }
+
+ SlotId = XhcRouteStringToSlotId (Xhc, RouteChart);
+ if (SlotId != 0) {
+ if (Xhc->HcCParams.Data.Csz == 0) {
+ Status = XhcDisableSlotCmd (Xhc, SlotId);
+ } else {
+ Status = XhcDisableSlotCmd64 (Xhc, SlotId);
+ }
+ }
+
+ 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 (Xhc, RouteChart);
+ if ((SlotId == 0) && ((PortState->PortChangeStatus & USB_PORT_STAT_C_RESET) != 0)) {
+ if (Xhc->HcCParams.Data.Csz == 0) {
+ Status = XhcInitializeDeviceSlot (Xhc, ParentRouteChart, Port, RouteChart, Speed);
+ } else {
+ Status = XhcInitializeDeviceSlot64 (Xhc, ParentRouteChart, Port, RouteChart, Speed);
+ }
+ }
+ }
+
+ 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 = (UINT8) (2 * EpAddr);
+ if (Direction == EfiUsbDataIn) {
+ Index += 1;
+ }
+ return Index;
+ }
+}
+
+/**
+ Find out the actual device address according to the requested device address from UsbBus.
+
+ @param Xhc The XHCI Instance.
+ @param BusDevAddr The requested device address by UsbBus upper driver.
+
+ @return The actual device address assigned to the device.
+
+**/
+UINT8
+EFIAPI
+XhcBusDevAddrToSlotId (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 BusDevAddr
+ )
+{
+ UINT8 Index;
+
+ for (Index = 0; Index < 255; Index++) {
+ if (Xhc->UsbDevContext[Index + 1].Enabled &&
+ (Xhc->UsbDevContext[Index + 1].SlotId != 0) &&
+ (Xhc->UsbDevContext[Index + 1].BusDevAddr == BusDevAddr)) {
+ break;
+ }
+ }
+
+ if (Index == 255) {
+ return 0;
+ }
+
+ return Xhc->UsbDevContext[Index + 1].SlotId;
+}
+
+/**
+ Find out the slot id according to the device's route string.
+
+ @param Xhc The XHCI Instance.
+ @param RouteString The route string described the device location.
+
+ @return The slot id used by the device.
+
+**/
+UINT8
+EFIAPI
+XhcRouteStringToSlotId (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN USB_DEV_ROUTE RouteString
+ )
+{
+ UINT8 Index;
+
+ for (Index = 0; Index < 255; Index++) {
+ if (Xhc->UsbDevContext[Index + 1].Enabled &&
+ (Xhc->UsbDevContext[Index + 1].SlotId != 0) &&
+ (Xhc->UsbDevContext[Index + 1].RouteString.Dword == RouteString.Dword)) {
+ break;
+ }
+ }
+
+ if (Index == 255) {
+ return 0;
+ }
+
+ return Xhc->UsbDevContext[Index + 1].SlotId;
+}
+
+/**
+ Synchronize the specified event ring to update the enqueue and dequeue pointer.
+
+ @param Xhc The XHCI Instance.
+ @param EvtRing The event ring to sync.
+
+ @retval EFI_SUCCESS The event ring is synchronized successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcSyncEventRing (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN EVENT_RING *EvtRing
+ )
+{
+ UINTN Index;
+ TRB_TEMPLATE *EvtTrb1;
+
+ ASSERT (EvtRing != NULL);
+
+ //
+ // Calculate the EventRingEnqueue and EventRingCCS.
+ // Note: only support single Segment
+ //
+ EvtTrb1 = EvtRing->EventRingDequeue;
+
+ for (Index = 0; Index < EvtRing->TrbNumber; Index++) {
+ if (EvtTrb1->CycleBit != EvtRing->EventRingCCS) {
+ break;
+ }
+
+ EvtTrb1++;
+
+ if ((UINTN)EvtTrb1 >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {
+ EvtTrb1 = EvtRing->EventRingSeg0;
+ EvtRing->EventRingCCS = (EvtRing->EventRingCCS) ? 0 : 1;
+ }
+ }
+
+ if (Index < EvtRing->TrbNumber) {
+ EvtRing->EventRingEnqueue = EvtTrb1;
+ } else {
+ ASSERT (FALSE);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
+
+ @param Xhc The XHCI Instance.
+ @param TrsRing The transfer ring to sync.
+
+ @retval EFI_SUCCESS The transfer ring is synchronized successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcSyncTrsRing (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN TRANSFER_RING *TrsRing
+ )
+{
+ UINTN Index;
+ TRB_TEMPLATE *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 (((LINK_TRB*)TrsTrb)->TC != 0);
+ //
+ // set cycle bit in Link TRB as normal
+ //
+ ((LINK_TRB*)TrsTrb)->CycleBit = TrsRing->RingPCS & BIT0;
+ //
+ // Toggle PCS maintained by software
+ //
+ TrsRing->RingPCS = (TrsRing->RingPCS & BIT0) ? 0 : 1;
+ TrsTrb = (TRB_TEMPLATE *) TrsRing->RingSeg0; // Use host address
+ }
+ }
+
+ ASSERT (Index != TrsRing->TrbNumber);
+
+ if (TrsTrb != TrsRing->RingEnqueue) {
+ TrsRing->RingEnqueue = TrsTrb;
+ }
+
+ //
+ // Clear the Trb context for enqueue, but reserve the PCS bit
+ //
+ TrsTrb->Parameter1 = 0;
+ TrsTrb->Parameter2 = 0;
+ TrsTrb->Status = 0;
+ TrsTrb->RsvdZ1 = 0;
+ TrsTrb->Type = 0;
+ TrsTrb->Control = 0;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Check if there is a new generated event.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN EVENT_RING *EvtRing,
+ OUT TRB_TEMPLATE **NewEvtTrb
+ )
+{
+ ASSERT (EvtRing != NULL);
+
+ *NewEvtTrb = EvtRing->EventRingDequeue;
+
+ if (EvtRing->EventRingDequeue == EvtRing->EventRingEnqueue) {
+ return EFI_NOT_READY;
+ }
+
+ 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_TEMPLATE) * EvtRing->TrbNumber)) {
+ EvtRing->EventRingDequeue = EvtRing->EventRingSeg0;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Ring the door bell to notify XHCI there is a transaction to be executed.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *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 Instance.
+ @param Urb The URB to be rung.
+
+ @retval EFI_SUCCESS Successfully ring the door bell.
+
+**/
+EFI_STATUS
+RingIntTransferDoorBell (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN URB *Urb
+ )
+{
+ UINT8 SlotId;
+ UINT8 Dci;
+
+ SlotId = XhcBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr);
+ Dci = XhcEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));
+ XhcRingDoorBell (Xhc, SlotId, Dci);
+ return EFI_SUCCESS;
+}
+
+/**
+ Assign and initialize the device slot for a new device.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN USB_DEV_ROUTE ParentRouteChart,
+ IN UINT16 ParentPort,
+ IN USB_DEV_ROUTE RouteChart,
+ IN UINT8 DeviceSpeed
+ )
+{
+ EFI_STATUS Status;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+ INPUT_CONTEXT *InputContext;
+ DEVICE_CONTEXT *OutputContext;
+ TRANSFER_RING *EndpointTransferRing;
+ CMD_TRB_ADDRESS_DEVICE CmdTrbAddr;
+ UINT8 DeviceAddress;
+ CMD_TRB_ENABLE_SLOT CmdTrb;
+ UINT8 SlotId;
+ UINT8 ParentSlotId;
+ DEVICE_CONTEXT *ParentDeviceContext;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ ZeroMem (&CmdTrb, sizeof (CMD_TRB_ENABLE_SLOT));
+ CmdTrb.CycleBit = 1;
+ CmdTrb.Type = TRB_TYPE_EN_SLOT;
+
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrb,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcInitializeDeviceSlot: Enable Slot Failed, Status = %r\n", Status));
+ return 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 (&Xhc->UsbDevContext[SlotId], sizeof (USB_DEV_CONTEXT));
+ Xhc->UsbDevContext[SlotId].Enabled = TRUE;
+ Xhc->UsbDevContext[SlotId].SlotId = SlotId;
+ Xhc->UsbDevContext[SlotId].RouteString.Dword = RouteChart.Dword;
+ Xhc->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 = UsbHcAllocateMem (Xhc->MemPool, sizeof (INPUT_CONTEXT));
+ ASSERT (InputContext != NULL);
+ ASSERT (((UINTN) InputContext & 0x3F) == 0);
+ ZeroMem (InputContext, sizeof (INPUT_CONTEXT));
+
+ Xhc->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.RouteString = RouteChart.Route.RouteString;
+ InputContext->Slot.Speed = DeviceSpeed + 1;
+ InputContext->Slot.ContextEntries = 1;
+ InputContext->Slot.RootHubPortNum = RouteChart.Route.RootPortNum;
+
+ if (RouteChart.Route.RouteString) {
+ //
+ // The device is behind of hub device.
+ //
+ ParentSlotId = XhcRouteStringToSlotId(Xhc, 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 *)Xhc->UsbDevContext[ParentSlotId].OutputContext;
+ 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 = AllocateZeroPool (sizeof (TRANSFER_RING));
+ Xhc->UsbDevContext[SlotId].EndpointTransferRing[0] = EndpointTransferRing;
+ CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->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
+ //
+ PhyAddr = UsbHcGetPciAddrForHostAddr (
+ Xhc->MemPool,
+ ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[0])->RingSeg0,
+ sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER
+ );
+ InputContext->EP[0].PtrLo = XHC_LOW_32BIT (PhyAddr) | BIT0;
+ InputContext->EP[0].PtrHi = XHC_HIGH_32BIT (PhyAddr);
+
+ //
+ // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
+ //
+ OutputContext = UsbHcAllocateMem (Xhc->MemPool, sizeof (DEVICE_CONTEXT));
+ ASSERT (OutputContext != NULL);
+ ASSERT (((UINTN) OutputContext & 0x3F) == 0);
+ ZeroMem (OutputContext, sizeof (DEVICE_CONTEXT));
+
+ Xhc->UsbDevContext[SlotId].OutputContext = OutputContext;
+ //
+ // 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).
+ //
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, OutputContext, sizeof (DEVICE_CONTEXT));
+ //
+ // Fill DCBAA with PCI device address
+ //
+ Xhc->DCBAA[SlotId] = (UINT64) (UINTN) PhyAddr;
+
+ //
+ // 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));
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT));
+ CmdTrbAddr.PtrLo = XHC_LOW_32BIT (PhyAddr);
+ CmdTrbAddr.PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ CmdTrbAddr.CycleBit = 1;
+ CmdTrbAddr.Type = TRB_TYPE_ADDRESS_DEV;
+ CmdTrbAddr.SlotId = Xhc->UsbDevContext[SlotId].SlotId;
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrbAddr,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (!EFI_ERROR (Status)) {
+ DeviceAddress = (UINT8) ((DEVICE_CONTEXT *) OutputContext)->Slot.DeviceAddress;
+ DEBUG ((EFI_D_INFO, " Address %d assigned successfully\n", DeviceAddress));
+ Xhc->UsbDevContext[SlotId].XhciDevAddr = DeviceAddress;
+ }
+
+ return Status;
+}
+
+/**
+ Assign and initialize the device slot for a new device.
+
+ @param Xhc The XHCI Instance.
+ @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
+XhcInitializeDeviceSlot64 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN USB_DEV_ROUTE ParentRouteChart,
+ IN UINT16 ParentPort,
+ IN USB_DEV_ROUTE RouteChart,
+ IN UINT8 DeviceSpeed
+ )
+{
+ EFI_STATUS Status;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+ INPUT_CONTEXT_64 *InputContext;
+ DEVICE_CONTEXT_64 *OutputContext;
+ TRANSFER_RING *EndpointTransferRing;
+ CMD_TRB_ADDRESS_DEVICE CmdTrbAddr;
+ UINT8 DeviceAddress;
+ CMD_TRB_ENABLE_SLOT CmdTrb;
+ UINT8 SlotId;
+ UINT8 ParentSlotId;
+ DEVICE_CONTEXT_64 *ParentDeviceContext;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ ZeroMem (&CmdTrb, sizeof (CMD_TRB_ENABLE_SLOT));
+ CmdTrb.CycleBit = 1;
+ CmdTrb.Type = TRB_TYPE_EN_SLOT;
+
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrb,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcInitializeDeviceSlot64: Enable Slot Failed, Status = %r\n", Status));
+ return 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 (&Xhc->UsbDevContext[SlotId], sizeof (USB_DEV_CONTEXT));
+ Xhc->UsbDevContext[SlotId].Enabled = TRUE;
+ Xhc->UsbDevContext[SlotId].SlotId = SlotId;
+ Xhc->UsbDevContext[SlotId].RouteString.Dword = RouteChart.Dword;
+ Xhc->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 = UsbHcAllocateMem (Xhc->MemPool, sizeof (INPUT_CONTEXT_64));
+ ASSERT (InputContext != NULL);
+ ASSERT (((UINTN) InputContext & 0x3F) == 0);
+ ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64));
+
+ Xhc->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.RouteString = RouteChart.Route.RouteString;
+ InputContext->Slot.Speed = DeviceSpeed + 1;
+ InputContext->Slot.ContextEntries = 1;
+ InputContext->Slot.RootHubPortNum = RouteChart.Route.RootPortNum;
+
+ if (RouteChart.Route.RouteString) {
+ //
+ // The device is behind of hub device.
+ //
+ ParentSlotId = XhcRouteStringToSlotId(Xhc, 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_64 *)Xhc->UsbDevContext[ParentSlotId].OutputContext;
+ 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 = AllocateZeroPool (sizeof (TRANSFER_RING));
+ Xhc->UsbDevContext[SlotId].EndpointTransferRing[0] = EndpointTransferRing;
+ CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->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
+ //
+ PhyAddr = UsbHcGetPciAddrForHostAddr (
+ Xhc->MemPool,
+ ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[0])->RingSeg0,
+ sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER
+ );
+ InputContext->EP[0].PtrLo = XHC_LOW_32BIT (PhyAddr) | BIT0;
+ InputContext->EP[0].PtrHi = XHC_HIGH_32BIT (PhyAddr);
+
+ //
+ // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
+ //
+ OutputContext = UsbHcAllocateMem (Xhc->MemPool, sizeof (DEVICE_CONTEXT_64));
+ ASSERT (OutputContext != NULL);
+ ASSERT (((UINTN) OutputContext & 0x3F) == 0);
+ ZeroMem (OutputContext, sizeof (DEVICE_CONTEXT_64));
+
+ Xhc->UsbDevContext[SlotId].OutputContext = OutputContext;
+ //
+ // 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).
+ //
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, OutputContext, sizeof (DEVICE_CONTEXT_64));
+ //
+ // Fill DCBAA with PCI device address
+ //
+ Xhc->DCBAA[SlotId] = (UINT64) (UINTN) PhyAddr;
+
+ //
+ // 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));
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT_64));
+ CmdTrbAddr.PtrLo = XHC_LOW_32BIT (PhyAddr);
+ CmdTrbAddr.PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ CmdTrbAddr.CycleBit = 1;
+ CmdTrbAddr.Type = TRB_TYPE_ADDRESS_DEV;
+ CmdTrbAddr.SlotId = Xhc->UsbDevContext[SlotId].SlotId;
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrbAddr,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (!EFI_ERROR (Status)) {
+ DeviceAddress = (UINT8) ((DEVICE_CONTEXT_64 *) OutputContext)->Slot.DeviceAddress;
+ DEBUG ((EFI_D_INFO, " Address %d assigned successfully\n", DeviceAddress));
+ Xhc->UsbDevContext[SlotId].XhciDevAddr = DeviceAddress;
+ }
+ return Status;
+}
+
+
+/**
+ Disable the specified device slot.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be disabled.
+
+ @retval EFI_SUCCESS Successfully disable the device slot.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcDisableSlotCmd (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId
+ )
+{
+ EFI_STATUS Status;
+ TRB_TEMPLATE *EvtTrb;
+ CMD_TRB_DISABLE_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 (!Xhc->UsbDevContext[Index + 1].Enabled ||
+ (Xhc->UsbDevContext[Index + 1].SlotId == 0) ||
+ (Xhc->UsbDevContext[Index + 1].ParentRouteString.Dword != Xhc->UsbDevContext[SlotId].RouteString.Dword)) {
+ continue;
+ }
+
+ Status = XhcDisableSlotCmd (Xhc, Xhc->UsbDevContext[Index + 1].SlotId);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
+ Xhc->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_TEMPLATE *) (UINTN) &CmdTrbDisSlot,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status));
+ return 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 (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index] != NULL) {
+ RingSeg = ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index])->RingSeg0;
+ if (RingSeg != NULL) {
+ UsbHcFreeMem (Xhc->MemPool, RingSeg, sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER);
+ }
+ FreePool (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index]);
+ Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index] = NULL;
+ }
+ }
+
+ for (Index = 0; Index < Xhc->UsbDevContext[SlotId].DevDesc.NumConfigurations; Index++) {
+ if (Xhc->UsbDevContext[SlotId].ConfDesc[Index] != NULL) {
+ FreePool (Xhc->UsbDevContext[SlotId].ConfDesc[Index]);
+ }
+ }
+
+ if (Xhc->UsbDevContext[SlotId].ActiveAlternateSetting != NULL) {
+ FreePool (Xhc->UsbDevContext[SlotId].ActiveAlternateSetting);
+ }
+
+ if (Xhc->UsbDevContext[SlotId].InputContext != NULL) {
+ UsbHcFreeMem (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT));
+ }
+
+ if (Xhc->UsbDevContext[SlotId].OutputContext != NULL) {
+ UsbHcFreeMem (Xhc->MemPool, Xhc->UsbDevContext[SlotId].OutputContext, sizeof (DEVICE_CONTEXT));
+ }
+ //
+ // 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.
+ //
+ Xhc->UsbDevContext[SlotId].Enabled = FALSE;
+ Xhc->UsbDevContext[SlotId].SlotId = 0;
+
+ return Status;
+}
+
+/**
+ Disable the specified device slot.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be disabled.
+
+ @retval EFI_SUCCESS Successfully disable the device slot.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcDisableSlotCmd64 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId
+ )
+{
+ EFI_STATUS Status;
+ TRB_TEMPLATE *EvtTrb;
+ CMD_TRB_DISABLE_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 (!Xhc->UsbDevContext[Index + 1].Enabled ||
+ (Xhc->UsbDevContext[Index + 1].SlotId == 0) ||
+ (Xhc->UsbDevContext[Index + 1].ParentRouteString.Dword != Xhc->UsbDevContext[SlotId].RouteString.Dword)) {
+ continue;
+ }
+
+ Status = XhcDisableSlotCmd64 (Xhc, Xhc->UsbDevContext[Index + 1].SlotId);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
+ Xhc->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_TEMPLATE *) (UINTN) &CmdTrbDisSlot,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status));
+ return 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 (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index] != NULL) {
+ RingSeg = ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index])->RingSeg0;
+ if (RingSeg != NULL) {
+ UsbHcFreeMem (Xhc->MemPool, RingSeg, sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER);
+ }
+ FreePool (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index]);
+ Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index] = NULL;
+ }
+ }
+
+ for (Index = 0; Index < Xhc->UsbDevContext[SlotId].DevDesc.NumConfigurations; Index++) {
+ if (Xhc->UsbDevContext[SlotId].ConfDesc[Index] != NULL) {
+ FreePool (Xhc->UsbDevContext[SlotId].ConfDesc[Index]);
+ }
+ }
+
+ if (Xhc->UsbDevContext[SlotId].ActiveAlternateSetting != NULL) {
+ FreePool (Xhc->UsbDevContext[SlotId].ActiveAlternateSetting);
+ }
+
+ if (Xhc->UsbDevContext[SlotId].InputContext != NULL) {
+ UsbHcFreeMem (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT_64));
+ }
+
+ if (Xhc->UsbDevContext[SlotId].OutputContext != NULL) {
+ UsbHcFreeMem (Xhc->MemPool, Xhc->UsbDevContext[SlotId].OutputContext, sizeof (DEVICE_CONTEXT_64));
+ }
+ //
+ // 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.
+ //
+ Xhc->UsbDevContext[SlotId].Enabled = FALSE;
+ Xhc->UsbDevContext[SlotId].SlotId = 0;
+
+ return Status;
+}
+
+/**
+ Initialize endpoint context in input context.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param DeviceSpeed The device's speed.
+ @param InputContext The pointer to the input context.
+ @param IfDesc The pointer to the usb device interface descriptor.
+
+ @return The maximum device context index of endpoint.
+
+**/
+UINT8
+EFIAPI
+XhcInitializeEndpointContext (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 DeviceSpeed,
+ IN INPUT_CONTEXT *InputContext,
+ IN USB_INTERFACE_DESCRIPTOR *IfDesc
+ )
+{
+ USB_ENDPOINT_DESCRIPTOR *EpDesc;
+ UINTN NumEp;
+ UINTN EpIndex;
+ UINT8 EpAddr;
+ UINT8 Direction;
+ UINT8 Dci;
+ UINT8 MaxDci;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ UINT8 Interval;
+ TRANSFER_RING *EndpointTransferRing;
+
+ MaxDci = 0;
+
+ 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 = (UINT8)(EpDesc->EndpointAddress & 0x0F);
+ Direction = (UINT8)((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut);
+
+ Dci = XhcEndpointToDci (EpAddr, Direction);
+ ASSERT (Dci < 32);
+ 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 (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) {
+ EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING));
+ Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing;
+ CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->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;
+ }
+ //
+ // Do not support isochronous transfer now.
+ //
+ DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);
+ continue;
+ 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;
+ //
+ // Calculate through the bInterval field of Endpoint descriptor.
+ //
+ ASSERT (Interval != 0);
+ InputContext->EP[Dci-1].Interval = (UINT32)HighBitSet32((UINT32)Interval) + 3;
+ } else if ((DeviceSpeed == EFI_USB_SPEED_HIGH) || (DeviceSpeed == EFI_USB_SPEED_SUPER)) {
+ Interval = EpDesc->Interval;
+ ASSERT (Interval >= 1 && Interval <= 16);
+ //
+ // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
+ //
+ InputContext->EP[Dci-1].Interval = Interval - 1;
+ 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 (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) {
+ EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING));
+ Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing;
+ CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]);
+ }
+ break;
+
+ case USB_ENDPOINT_CONTROL:
+ //
+ // Do not support control transfer now.
+ //
+ DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext: Unsupport Control EP found, Transfer ring is not allocated.\n"));
+ default:
+ DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext: Unknown EP found, Transfer ring is not allocated.\n"));
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);
+ continue;
+ }
+
+ PhyAddr = UsbHcGetPciAddrForHostAddr (
+ Xhc->MemPool,
+ ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0,
+ sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER
+ );
+ PhyAddr &= ~((EFI_PHYSICAL_ADDRESS)0x0F);
+ PhyAddr |= (EFI_PHYSICAL_ADDRESS)((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingPCS;
+ InputContext->EP[Dci-1].PtrLo = XHC_LOW_32BIT (PhyAddr);
+ InputContext->EP[Dci-1].PtrHi = XHC_HIGH_32BIT (PhyAddr);
+
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);
+ }
+
+ return MaxDci;
+}
+
+/**
+ Initialize endpoint context in input context.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param DeviceSpeed The device's speed.
+ @param InputContext The pointer to the input context.
+ @param IfDesc The pointer to the usb device interface descriptor.
+
+ @return The maximum device context index of endpoint.
+
+**/
+UINT8
+EFIAPI
+XhcInitializeEndpointContext64 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 DeviceSpeed,
+ IN INPUT_CONTEXT_64 *InputContext,
+ IN USB_INTERFACE_DESCRIPTOR *IfDesc
+ )
+{
+ USB_ENDPOINT_DESCRIPTOR *EpDesc;
+ UINTN NumEp;
+ UINTN EpIndex;
+ UINT8 EpAddr;
+ UINT8 Direction;
+ UINT8 Dci;
+ UINT8 MaxDci;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ UINT8 Interval;
+ TRANSFER_RING *EndpointTransferRing;
+
+ MaxDci = 0;
+
+ 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 = (UINT8)(EpDesc->EndpointAddress & 0x0F);
+ Direction = (UINT8)((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut);
+
+ Dci = XhcEndpointToDci (EpAddr, Direction);
+ ASSERT (Dci < 32);
+ 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 (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) {
+ EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING));
+ Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing;
+ CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->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;
+ }
+ //
+ // Do not support isochronous transfer now.
+ //
+ DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext64: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);
+ continue;
+ 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;
+ //
+ // Calculate through the bInterval field of Endpoint descriptor.
+ //
+ ASSERT (Interval != 0);
+ InputContext->EP[Dci-1].Interval = (UINT32)HighBitSet32((UINT32)Interval) + 3;
+ } else if ((DeviceSpeed == EFI_USB_SPEED_HIGH) || (DeviceSpeed == EFI_USB_SPEED_SUPER)) {
+ Interval = EpDesc->Interval;
+ ASSERT (Interval >= 1 && Interval <= 16);
+ //
+ // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
+ //
+ InputContext->EP[Dci-1].Interval = Interval - 1;
+ 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 (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) {
+ EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING));
+ Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing;
+ CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]);
+ }
+ break;
+
+ case USB_ENDPOINT_CONTROL:
+ //
+ // Do not support control transfer now.
+ //
+ DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext64: Unsupport Control EP found, Transfer ring is not allocated.\n"));
+ default:
+ DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext64: Unknown EP found, Transfer ring is not allocated.\n"));
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);
+ continue;
+ }
+
+ PhyAddr = UsbHcGetPciAddrForHostAddr (
+ Xhc->MemPool,
+ ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0,
+ sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER
+ );
+ PhyAddr &= ~((EFI_PHYSICAL_ADDRESS)0x0F);
+ PhyAddr |= (EFI_PHYSICAL_ADDRESS)((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingPCS;
+ InputContext->EP[Dci-1].PtrLo = XHC_LOW_32BIT (PhyAddr);
+ InputContext->EP[Dci-1].PtrHi = XHC_HIGH_32BIT (PhyAddr);
+
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);
+ }
+
+ return MaxDci;
+}
+
+/**
+ Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 DeviceSpeed,
+ IN USB_CONFIG_DESCRIPTOR *ConfigDesc
+ )
+{
+ EFI_STATUS Status;
+ USB_INTERFACE_DESCRIPTOR *IfDesc;
+ UINT8 Index;
+ UINT8 Dci;
+ UINT8 MaxDci;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP;
+ INPUT_CONTEXT *InputContext;
+ DEVICE_CONTEXT *OutputContext;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+ //
+ // 4.6.6 Configure Endpoint
+ //
+ InputContext = Xhc->UsbDevContext[SlotId].InputContext;
+ OutputContext = Xhc->UsbDevContext[SlotId].OutputContext;
+ ZeroMem (InputContext, sizeof (INPUT_CONTEXT));
+ CopyMem (&InputContext->Slot, &OutputContext->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->AlternateSetting != 0)) {
+ IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length);
+ }
+
+ Dci = XhcInitializeEndpointContext (Xhc, SlotId, DeviceSpeed, InputContext, IfDesc);
+ if (Dci > MaxDci) {
+ MaxDci = Dci;
+ }
+
+ IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length);
+ }
+
+ InputContext->InputControlContext.Dword2 |= BIT0;
+ InputContext->Slot.ContextEntries = MaxDci;
+ //
+ // configure endpoint
+ //
+ ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP));
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT));
+ CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr);
+ CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ CmdTrbCfgEP.CycleBit = 1;
+ CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT;
+ CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId;
+ DEBUG ((EFI_D_INFO, "Configure Endpoint\n"));
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcSetConfigCmd: Config Endpoint Failed, Status = %r\n", Status));
+ } else {
+ Xhc->UsbDevContext[SlotId].ActiveConfiguration = ConfigDesc->ConfigurationValue;
+ }
+
+ return Status;
+}
+
+/**
+ Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @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
+XhcSetConfigCmd64 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 DeviceSpeed,
+ IN USB_CONFIG_DESCRIPTOR *ConfigDesc
+ )
+{
+ EFI_STATUS Status;
+ USB_INTERFACE_DESCRIPTOR *IfDesc;
+ UINT8 Index;
+ UINT8 Dci;
+ UINT8 MaxDci;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP;
+ INPUT_CONTEXT_64 *InputContext;
+ DEVICE_CONTEXT_64 *OutputContext;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+ //
+ // 4.6.6 Configure Endpoint
+ //
+ InputContext = Xhc->UsbDevContext[SlotId].InputContext;
+ OutputContext = Xhc->UsbDevContext[SlotId].OutputContext;
+ ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64));
+ CopyMem (&InputContext->Slot, &OutputContext->Slot, sizeof (SLOT_CONTEXT_64));
+
+ 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->AlternateSetting != 0)) {
+ IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length);
+ }
+
+ Dci = XhcInitializeEndpointContext64 (Xhc, SlotId, DeviceSpeed, InputContext, IfDesc);
+ if (Dci > MaxDci) {
+ MaxDci = Dci;
+ }
+
+ IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length);
+ }
+
+ InputContext->InputControlContext.Dword2 |= BIT0;
+ InputContext->Slot.ContextEntries = MaxDci;
+ //
+ // configure endpoint
+ //
+ ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP));
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT_64));
+ CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr);
+ CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ CmdTrbCfgEP.CycleBit = 1;
+ CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT;
+ CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId;
+ DEBUG ((EFI_D_INFO, "Configure Endpoint\n"));
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcSetConfigCmd64: Config Endpoint Failed, Status = %r\n", Status));
+ } else {
+ Xhc->UsbDevContext[SlotId].ActiveConfiguration = ConfigDesc->ConfigurationValue;
+ }
+
+ return Status;
+}
+
+/**
+ Stop endpoint through XHCI's Stop_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param Dci The device context index of endpoint.
+
+ @retval EFI_SUCCESS Stop endpoint successfully.
+ @retval Others Failed to stop endpoint.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcStopEndpoint (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 Dci
+ )
+{
+ EFI_STATUS Status;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+ CMD_TRB_STOP_ENDPOINT CmdTrbStopED;
+
+ DEBUG ((EFI_D_INFO, "XhcStopEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId, Dci));
+
+ //
+ // Send stop endpoint command to transit Endpoint from running to stop state
+ //
+ ZeroMem (&CmdTrbStopED, sizeof (CmdTrbStopED));
+ CmdTrbStopED.CycleBit = 1;
+ CmdTrbStopED.Type = TRB_TYPE_STOP_ENDPOINT;
+ CmdTrbStopED.EDID = Dci;
+ CmdTrbStopED.SlotId = SlotId;
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrbStopED,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR(Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcStopEndpoint: Stop Endpoint Failed, Status = %r\n", Status));
+ }
+
+ return Status;
+}
+
+/**
+ Reset endpoint through XHCI's Reset_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param Dci The device context index of endpoint.
+
+ @retval EFI_SUCCESS Reset endpoint successfully.
+ @retval Others Failed to reset endpoint.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcResetEndpoint (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 Dci
+ )
+{
+ EFI_STATUS Status;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+ CMD_TRB_RESET_ENDPOINT CmdTrbResetED;
+
+ DEBUG ((EFI_D_INFO, "XhcResetEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId, Dci));
+
+ //
+ // Send stop endpoint command to transit Endpoint from running 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_TEMPLATE *) (UINTN) &CmdTrbResetED,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR(Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcResetEndpoint: Reset Endpoint Failed, Status = %r\n", Status));
+ }
+
+ return Status;
+}
+
+/**
+ Set transfer ring dequeue pointer through XHCI's Set_Tr_Dequeue_Pointer cmd.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param Dci The device context index of endpoint.
+ @param Urb The dequeue pointer of the transfer ring specified
+ by the urb to be updated.
+
+ @retval EFI_SUCCESS Set transfer ring dequeue pointer succeeds.
+ @retval Others Failed to set transfer ring dequeue pointer.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcSetTrDequeuePointer (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 Dci,
+ IN URB *Urb
+ )
+{
+ EFI_STATUS Status;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+ CMD_SET_TR_DEQ_POINTER CmdSetTRDeq;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ DEBUG ((EFI_D_INFO, "XhcSetTrDequeuePointer: Slot = 0x%x, Dci = 0x%x, Urb = 0x%x\n", SlotId, Dci, Urb));
+
+ //
+ // Send stop endpoint command to transit Endpoint from running to stop state
+ //
+ ZeroMem (&CmdSetTRDeq, sizeof (CmdSetTRDeq));
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Urb->Ring->RingEnqueue, sizeof (CMD_SET_TR_DEQ_POINTER));
+ CmdSetTRDeq.PtrLo = XHC_LOW_32BIT (PhyAddr) | Urb->Ring->RingPCS;
+ CmdSetTRDeq.PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ CmdSetTRDeq.CycleBit = 1;
+ CmdSetTRDeq.Type = TRB_TYPE_SET_TR_DEQUE;
+ CmdSetTRDeq.Endpoint = Dci;
+ CmdSetTRDeq.SlotId = SlotId;
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdSetTRDeq,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR(Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcSetTrDequeuePointer: Set TR Dequeue Pointer Failed, Status = %r\n", Status));
+ }
+
+ return Status;
+}
+
+/**
+ Set interface through XHCI's Configure_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param DeviceSpeed The device's speed.
+ @param ConfigDesc The pointer to the usb device configuration descriptor.
+ @param Request USB device request to send.
+
+ @retval EFI_SUCCESS Successfully set interface.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcSetInterface (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 DeviceSpeed,
+ IN USB_CONFIG_DESCRIPTOR *ConfigDesc,
+ IN EFI_USB_DEVICE_REQUEST *Request
+ )
+{
+ EFI_STATUS Status;
+ USB_INTERFACE_DESCRIPTOR *IfDescActive;
+ USB_INTERFACE_DESCRIPTOR *IfDescSet;
+ USB_INTERFACE_DESCRIPTOR *IfDesc;
+ USB_ENDPOINT_DESCRIPTOR *EpDesc;
+ UINTN NumEp;
+ UINTN EpIndex;
+ UINT8 EpAddr;
+ UINT8 Direction;
+ UINT8 Dci;
+ UINT8 MaxDci;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ VOID *RingSeg;
+
+ CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP;
+ INPUT_CONTEXT *InputContext;
+ DEVICE_CONTEXT *OutputContext;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+
+ Status = EFI_SUCCESS;
+
+ InputContext = Xhc->UsbDevContext[SlotId].InputContext;
+ OutputContext = Xhc->UsbDevContext[SlotId].OutputContext;
+ //
+ // XHCI 4.6.6 Configure Endpoint
+ // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
+ // Context and Add Context flags as follows:
+ // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
+ // Context and Add Context flags to '0'.
+ //
+ // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
+ // So the default Drop Context and Add Context flags can be '0' to cover 1).
+ //
+ ZeroMem (InputContext, sizeof (INPUT_CONTEXT));
+ CopyMem (&InputContext->Slot, &OutputContext->Slot, sizeof (SLOT_CONTEXT));
+
+ ASSERT (ConfigDesc != NULL);
+
+ MaxDci = 0;
+
+ IfDescActive = NULL;
+ IfDescSet = NULL;
+
+ IfDesc = (USB_INTERFACE_DESCRIPTOR *)(ConfigDesc + 1);
+ while ((UINTN) IfDesc < ((UINTN) ConfigDesc + ConfigDesc->TotalLength)) {
+ if (IfDesc->DescriptorType == USB_DESC_TYPE_INTERFACE) {
+ if (IfDesc->InterfaceNumber == (UINT8) Request->Index) {
+ if (IfDesc->AlternateSetting == Xhc->UsbDevContext[SlotId].ActiveAlternateSetting[IfDesc->InterfaceNumber]) {
+ //
+ // Find out the active interface descriptor.
+ //
+ IfDescActive = IfDesc;
+ } else if (IfDesc->AlternateSetting == (UINT8) Request->Value) {
+ //
+ // Find out the interface descriptor to set.
+ //
+ IfDescSet = IfDesc;
+ }
+ }
+ }
+ IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length);
+ }
+
+ //
+ // XHCI 4.6.6 Configure Endpoint
+ // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
+ // Context and Add Context flags as follows:
+ // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
+ // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
+ // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
+ // the Drop Context flag to '1' and Add Context flag to '0'.
+ // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
+ // and Add Context flags shall be set to '1'.
+ //
+ // Below codes are to cover 2), 3) and 4).
+ //
+
+ if ((IfDescActive != NULL) && (IfDescSet != NULL)) {
+ NumEp = IfDescActive->NumEndpoints;
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *) (IfDescActive + 1);
+ for (EpIndex = 0; EpIndex < NumEp; EpIndex++) {
+ while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) {
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);
+ }
+
+ EpAddr = (UINT8) (EpDesc->EndpointAddress & 0x0F);
+ Direction = (UINT8) ((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut);
+
+ Dci = XhcEndpointToDci (EpAddr, Direction);
+ ASSERT (Dci < 32);
+ if (Dci > MaxDci) {
+ MaxDci = Dci;
+ }
+ //
+ // XHCI 4.3.6 - Setting Alternate Interfaces
+ // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
+ //
+ Status = XhcStopEndpoint (Xhc, SlotId, Dci);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // XHCI 4.3.6 - Setting Alternate Interfaces
+ // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
+ //
+ if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1] != NULL) {
+ RingSeg = ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1])->RingSeg0;
+ if (RingSeg != NULL) {
+ UsbHcFreeMem (Xhc->MemPool, RingSeg, sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER);
+ }
+ FreePool (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1]);
+ Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1] = NULL;
+ }
+
+ //
+ // Set the Drop Context flag to '1'.
+ //
+ InputContext->InputControlContext.Dword1 |= (BIT0 << Dci);
+
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);
+ }
+
+ //
+ // XHCI 4.3.6 - Setting Alternate Interfaces
+ // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
+ // Interface setting, to '0'.
+ //
+ // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
+ //
+
+ //
+ // XHCI 4.3.6 - Setting Alternate Interfaces
+ // 4) For each endpoint enabled by the Configure Endpoint Command:
+ // a. Allocate a Transfer Ring.
+ // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
+ // c. Initialize the Endpoint Context data structure.
+ //
+ Dci = XhcInitializeEndpointContext (Xhc, SlotId, DeviceSpeed, InputContext, IfDescSet);
+ if (Dci > MaxDci) {
+ MaxDci = Dci;
+ }
+
+ InputContext->InputControlContext.Dword2 |= BIT0;
+ InputContext->Slot.ContextEntries = MaxDci;
+ //
+ // XHCI 4.3.6 - Setting Alternate Interfaces
+ // 5) Issue and successfully complete a Configure Endpoint Command.
+ //
+ ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP));
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT));
+ CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr);
+ CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ CmdTrbCfgEP.CycleBit = 1;
+ CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT;
+ CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId;
+ DEBUG ((EFI_D_INFO, "SetInterface: Configure Endpoint\n"));
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "SetInterface: Config Endpoint Failed, Status = %r\n", Status));
+ } else {
+ //
+ // Update the active AlternateSetting.
+ //
+ Xhc->UsbDevContext[SlotId].ActiveAlternateSetting[(UINT8) Request->Index] = (UINT8) Request->Value;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Set interface through XHCI's Configure_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param DeviceSpeed The device's speed.
+ @param ConfigDesc The pointer to the usb device configuration descriptor.
+ @param Request USB device request to send.
+
+ @retval EFI_SUCCESS Successfully set interface.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcSetInterface64 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 DeviceSpeed,
+ IN USB_CONFIG_DESCRIPTOR *ConfigDesc,
+ IN EFI_USB_DEVICE_REQUEST *Request
+ )
+{
+ EFI_STATUS Status;
+ USB_INTERFACE_DESCRIPTOR *IfDescActive;
+ USB_INTERFACE_DESCRIPTOR *IfDescSet;
+ USB_INTERFACE_DESCRIPTOR *IfDesc;
+ USB_ENDPOINT_DESCRIPTOR *EpDesc;
+ UINTN NumEp;
+ UINTN EpIndex;
+ UINT8 EpAddr;
+ UINT8 Direction;
+ UINT8 Dci;
+ UINT8 MaxDci;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ VOID *RingSeg;
+
+ CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP;
+ INPUT_CONTEXT_64 *InputContext;
+ DEVICE_CONTEXT_64 *OutputContext;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+
+ Status = EFI_SUCCESS;
+
+ InputContext = Xhc->UsbDevContext[SlotId].InputContext;
+ OutputContext = Xhc->UsbDevContext[SlotId].OutputContext;
+ //
+ // XHCI 4.6.6 Configure Endpoint
+ // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
+ // Context and Add Context flags as follows:
+ // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
+ // Context and Add Context flags to '0'.
+ //
+ // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
+ // So the default Drop Context and Add Context flags can be '0' to cover 1).
+ //
+ ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64));
+ CopyMem (&InputContext->Slot, &OutputContext->Slot, sizeof (SLOT_CONTEXT_64));
+
+ ASSERT (ConfigDesc != NULL);
+
+ MaxDci = 0;
+
+ IfDescActive = NULL;
+ IfDescSet = NULL;
+
+ IfDesc = (USB_INTERFACE_DESCRIPTOR *)(ConfigDesc + 1);
+ while ((UINTN) IfDesc < ((UINTN) ConfigDesc + ConfigDesc->TotalLength)) {
+ if (IfDesc->DescriptorType == USB_DESC_TYPE_INTERFACE) {
+ if (IfDesc->InterfaceNumber == (UINT8) Request->Index) {
+ if (IfDesc->AlternateSetting == Xhc->UsbDevContext[SlotId].ActiveAlternateSetting[IfDesc->InterfaceNumber]) {
+ //
+ // Find out the active interface descriptor.
+ //
+ IfDescActive = IfDesc;
+ } else if (IfDesc->AlternateSetting == (UINT8) Request->Value) {
+ //
+ // Find out the interface descriptor to set.
+ //
+ IfDescSet = IfDesc;
+ }
+ }
+ }
+ IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length);
+ }
+
+ //
+ // XHCI 4.6.6 Configure Endpoint
+ // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
+ // Context and Add Context flags as follows:
+ // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
+ // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
+ // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
+ // the Drop Context flag to '1' and Add Context flag to '0'.
+ // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
+ // and Add Context flags shall be set to '1'.
+ //
+ // Below codes are to cover 2), 3) and 4).
+ //
+
+ if ((IfDescActive != NULL) && (IfDescSet != NULL)) {
+ NumEp = IfDescActive->NumEndpoints;
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *) (IfDescActive + 1);
+ for (EpIndex = 0; EpIndex < NumEp; EpIndex++) {
+ while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) {
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);
+ }
+
+ EpAddr = (UINT8) (EpDesc->EndpointAddress & 0x0F);
+ Direction = (UINT8) ((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut);
+
+ Dci = XhcEndpointToDci (EpAddr, Direction);
+ ASSERT (Dci < 32);
+ if (Dci > MaxDci) {
+ MaxDci = Dci;
+ }
+ //
+ // XHCI 4.3.6 - Setting Alternate Interfaces
+ // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
+ //
+ Status = XhcStopEndpoint (Xhc, SlotId, Dci);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // XHCI 4.3.6 - Setting Alternate Interfaces
+ // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
+ //
+ if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1] != NULL) {
+ RingSeg = ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1])->RingSeg0;
+ if (RingSeg != NULL) {
+ UsbHcFreeMem (Xhc->MemPool, RingSeg, sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER);
+ }
+ FreePool (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1]);
+ Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1] = NULL;
+ }
+
+ //
+ // Set the Drop Context flag to '1'.
+ //
+ InputContext->InputControlContext.Dword1 |= (BIT0 << Dci);
+
+ EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);
+ }
+
+ //
+ // XHCI 4.3.6 - Setting Alternate Interfaces
+ // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
+ // Interface setting, to '0'.
+ //
+ // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
+ //
+
+ //
+ // XHCI 4.3.6 - Setting Alternate Interfaces
+ // 4) For each endpoint enabled by the Configure Endpoint Command:
+ // a. Allocate a Transfer Ring.
+ // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
+ // c. Initialize the Endpoint Context data structure.
+ //
+ Dci = XhcInitializeEndpointContext64 (Xhc, SlotId, DeviceSpeed, InputContext, IfDescSet);
+ if (Dci > MaxDci) {
+ MaxDci = Dci;
+ }
+
+ InputContext->InputControlContext.Dword2 |= BIT0;
+ InputContext->Slot.ContextEntries = MaxDci;
+ //
+ // XHCI 4.3.6 - Setting Alternate Interfaces
+ // 5) Issue and successfully complete a Configure Endpoint Command.
+ //
+ ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP));
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT_64));
+ CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr);
+ CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ CmdTrbCfgEP.CycleBit = 1;
+ CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT;
+ CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId;
+ DEBUG ((EFI_D_INFO, "SetInterface64: Configure Endpoint\n"));
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "SetInterface64: Config Endpoint Failed, Status = %r\n", Status));
+ } else {
+ //
+ // Update the active AlternateSetting.
+ //
+ Xhc->UsbDevContext[SlotId].ActiveAlternateSetting[(UINT8) Request->Index] = (UINT8) Request->Value;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT32 MaxPacketSize
+ )
+{
+ EFI_STATUS Status;
+ CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+ INPUT_CONTEXT *InputContext;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ ASSERT (Xhc->UsbDevContext[SlotId].SlotId != 0);
+
+ //
+ // 4.6.7 Evaluate Context
+ //
+ InputContext = Xhc->UsbDevContext[SlotId].InputContext;
+ ZeroMem (InputContext, sizeof (INPUT_CONTEXT));
+
+ InputContext->InputControlContext.Dword2 |= BIT1;
+ InputContext->EP[0].MaxPacketSize = MaxPacketSize;
+
+ ZeroMem (&CmdTrbEvalu, sizeof (CmdTrbEvalu));
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT));
+ CmdTrbEvalu.PtrLo = XHC_LOW_32BIT (PhyAddr);
+ CmdTrbEvalu.PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ CmdTrbEvalu.CycleBit = 1;
+ CmdTrbEvalu.Type = TRB_TYPE_EVALU_CONTXT;
+ CmdTrbEvalu.SlotId = Xhc->UsbDevContext[SlotId].SlotId;
+ DEBUG ((EFI_D_INFO, "Evaluate context\n"));
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrbEvalu,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcEvaluateContext: Evaluate Context Failed, Status = %r\n", Status));
+ }
+ return Status;
+}
+
+/**
+ Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
+
+ @param Xhc The XHCI Instance.
+ @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
+XhcEvaluateContext64 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT32 MaxPacketSize
+ )
+{
+ EFI_STATUS Status;
+ CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+ INPUT_CONTEXT_64 *InputContext;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ ASSERT (Xhc->UsbDevContext[SlotId].SlotId != 0);
+
+ //
+ // 4.6.7 Evaluate Context
+ //
+ InputContext = Xhc->UsbDevContext[SlotId].InputContext;
+ ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64));
+
+ InputContext->InputControlContext.Dword2 |= BIT1;
+ InputContext->EP[0].MaxPacketSize = MaxPacketSize;
+
+ ZeroMem (&CmdTrbEvalu, sizeof (CmdTrbEvalu));
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT_64));
+ CmdTrbEvalu.PtrLo = XHC_LOW_32BIT (PhyAddr);
+ CmdTrbEvalu.PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ CmdTrbEvalu.CycleBit = 1;
+ CmdTrbEvalu.Type = TRB_TYPE_EVALU_CONTXT;
+ CmdTrbEvalu.SlotId = Xhc->UsbDevContext[SlotId].SlotId;
+ DEBUG ((EFI_D_INFO, "Evaluate context\n"));
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrbEvalu,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcEvaluateContext64: Evaluate Context Failed, Status = %r\n", Status));
+ }
+ return Status;
+}
+
+
+/**
+ Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 PortNum,
+ IN UINT8 TTT,
+ IN UINT8 MTT
+ )
+{
+ EFI_STATUS Status;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+ INPUT_CONTEXT *InputContext;
+ DEVICE_CONTEXT *OutputContext;
+ CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ ASSERT (Xhc->UsbDevContext[SlotId].SlotId != 0);
+ InputContext = Xhc->UsbDevContext[SlotId].InputContext;
+ OutputContext = Xhc->UsbDevContext[SlotId].OutputContext;
+
+ //
+ // 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), &(OutputContext->Slot), sizeof (SLOT_CONTEXT));
+ InputContext->Slot.Hub = 1;
+ InputContext->Slot.PortNum = PortNum;
+ InputContext->Slot.TTT = TTT;
+ InputContext->Slot.MTT = MTT;
+
+ ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP));
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT));
+ CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr);
+ CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ CmdTrbCfgEP.CycleBit = 1;
+ CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT;
+ CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId;
+ DEBUG ((EFI_D_INFO, "Configure Hub Slot Context\n"));
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcConfigHubContext: Config Endpoint Failed, Status = %r\n", Status));
+ }
+ return Status;
+}
+
+/**
+ Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @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
+XhcConfigHubContext64 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 PortNum,
+ IN UINT8 TTT,
+ IN UINT8 MTT
+ )
+{
+ EFI_STATUS Status;
+ EVT_TRB_COMMAND_COMPLETION *EvtTrb;
+ INPUT_CONTEXT_64 *InputContext;
+ DEVICE_CONTEXT_64 *OutputContext;
+ CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ ASSERT (Xhc->UsbDevContext[SlotId].SlotId != 0);
+ InputContext = Xhc->UsbDevContext[SlotId].InputContext;
+ OutputContext = Xhc->UsbDevContext[SlotId].OutputContext;
+
+ //
+ // 4.6.7 Evaluate Context
+ //
+ ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64));
+
+ InputContext->InputControlContext.Dword2 |= BIT0;
+
+ //
+ // Copy the slot context from OutputContext to Input context
+ //
+ CopyMem(&(InputContext->Slot), &(OutputContext->Slot), sizeof (SLOT_CONTEXT_64));
+ InputContext->Slot.Hub = 1;
+ InputContext->Slot.PortNum = PortNum;
+ InputContext->Slot.TTT = TTT;
+ InputContext->Slot.MTT = MTT;
+
+ ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP));
+ PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT_64));
+ CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr);
+ CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr);
+ CmdTrbCfgEP.CycleBit = 1;
+ CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT;
+ CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId;
+ DEBUG ((EFI_D_INFO, "Configure Hub Slot Context\n"));
+ Status = XhcCmdTransfer (
+ Xhc,
+ (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP,
+ XHC_GENERIC_TIMEOUT,
+ (TRB_TEMPLATE **) (UINTN) &EvtTrb
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "XhcConfigHubContext64: Config Endpoint Failed, Status = %r\n", Status));
+ }
+ return Status;
+}
+
+
diff --git a/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.h b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.h new file mode 100644 index 0000000000..931c7efa0c --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.h @@ -0,0 +1,1461 @@ +/** @file
+
+ This file contains the definition for XHCI host controller schedule routines.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+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
+
+//
+// The topology string used to present usb device location
+//
+typedef struct _USB_DEV_TOPOLOGY {
+ //
+ // The tier concatenation of down stream port.
+ //
+ UINT32 RouteString:20;
+ //
+ // The root port number of the chain.
+ //
+ UINT32 RootPortNum:8;
+ //
+ // The Tier the device reside.
+ //
+ UINT32 TierNum:4;
+} USB_DEV_TOPOLOGY;
+
+//
+// USB Device's RouteChart
+//
+typedef union _USB_DEV_ROUTE {
+ UINT32 Dword;
+ USB_DEV_TOPOLOGY Route;
+} USB_DEV_ROUTE;
+
+//
+// Endpoint address and its capabilities
+//
+typedef struct _USB_ENDPOINT {
+ //
+ // Store logical device address assigned by UsbBus
+ // It's because some XHCI host controllers may assign the same physcial device
+ // address for those devices inserted at different root port.
+ //
+ UINT8 BusAddr;
+ UINT8 DevAddr;
+ UINT8 EpAddr;
+ EFI_USB_DATA_DIRECTION Direction;
+ UINT8 DevSpeed;
+ UINTN MaxPacket;
+ UINTN Type;
+} USB_ENDPOINT;
+
+//
+// TRB Template
+//
+typedef struct _TRB_TEMPLATE {
+ UINT32 Parameter1;
+
+ UINT32 Parameter2;
+
+ UINT32 Status;
+
+ UINT32 CycleBit:1;
+ UINT32 RsvdZ1:9;
+ UINT32 Type:6;
+ UINT32 Control:16;
+} TRB_TEMPLATE;
+
+typedef struct _TRANSFER_RING {
+ VOID *RingSeg0;
+ UINTN TrbNumber;
+ TRB_TEMPLATE *RingEnqueue;
+ TRB_TEMPLATE *RingDequeue;
+ UINT32 RingPCS;
+} TRANSFER_RING;
+
+typedef struct _EVENT_RING {
+ VOID *ERSTBase;
+ VOID *EventRingSeg0;
+ UINTN TrbNumber;
+ TRB_TEMPLATE *EventRingEnqueue;
+ TRB_TEMPLATE *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;
+ VOID *DataPhy;
+ VOID *DataMap;
+ 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_TEMPLATE *TrbStart;
+ TRB_TEMPLATE *TrbEnd;
+ UINTN TrbNum;
+ BOOLEAN StartDone;
+ BOOLEAN EndDone;
+ BOOLEAN Finished;
+
+ TRB_TEMPLATE *EvtTrb;
+} URB;
+
+//
+// 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 Length: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 Length: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 Length: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 Length:24;
+ UINT32 Completecode: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_COMPLETION {
+ UINT32 TRBPtrLo;
+
+ UINT32 TRBPtrHi;
+
+ UINT32 RsvdZ2:24;
+ UINT32 Completecode:8;
+
+ UINT32 CycleBit:1;
+ UINT32 RsvdZ3:9;
+ UINT32 Type:6;
+ UINT32 VFID:8;
+ UINT32 SlotId:8;
+} EVT_TRB_COMMAND_COMPLETION;
+
+typedef union _TRB {
+ TRB_TEMPLATE TrbTemplate;
+ TRANSFER_TRB_NORMAL TrbNormal;
+ TRANSFER_TRB_CONTROL_SETUP TrbCtrSetup;
+ TRANSFER_TRB_CONTROL_DATA TrbCtrData;
+ TRANSFER_TRB_CONTROL_STATUS TrbCtrStatus;
+} TRB;
+
+//
+// 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_ENABLE_SLOT {
+ UINT32 RsvdZ0;
+ UINT32 RsvdZ1;
+ UINT32 RsvdZ2;
+
+ UINT32 CycleBit:1;
+ UINT32 RsvdZ3:9;
+ UINT32 Type:6;
+ UINT32 RsvdZ4:16;
+} CMD_TRB_ENABLE_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_DISABLE_SLOT {
+ UINT32 RsvdZ0;
+ UINT32 RsvdZ1;
+ UINT32 RsvdZ2;
+
+ UINT32 CycleBit:1;
+ UINT32 RsvdZ3:9;
+ UINT32 Type:6;
+ UINT32 RsvdZ4:8;
+ UINT32 SlotId:8;
+} CMD_TRB_DISABLE_SLOT;
+
+//
+// 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_ADDRESS_DEVICE {
+ 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_ADDRESS_DEVICE;
+
+//
+// 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_TRB_CONFIG_ENDPOINT {
+ 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_TRB_CONFIG_ENDPOINT;
+
+//
+// 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_EVALUATE_CONTEXT {
+ UINT32 PtrLo;
+
+ UINT32 PtrHi;
+
+ UINT32 RsvdZ1;
+
+ UINT32 CycleBit:1;
+ UINT32 RsvdZ2:9;
+ UINT32 Type:6;
+ UINT32 RsvdZ3:8;
+ UINT32 SlotId:8;
+} CMD_TRB_EVALUATE_CONTEXT;
+
+//
+// 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_ENDPOINT {
+ 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_ENDPOINT;
+
+//
+// 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_ENDPOINT {
+ 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_ENDPOINT;
+
+//
+// 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_POINTER {
+ 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_POINTER;
+
+//
+// 6.4.4.1 Link TRB
+// A Link TRB provides support for non-contiguous TRB Rings.
+//
+typedef struct _LINK_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;
+} LINK_TRB;
+
+//
+// 6.2.2 Slot Context
+//
+typedef struct _SLOT_CONTEXT {
+ UINT32 RouteString: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;
+
+typedef struct _SLOT_CONTEXT_64 {
+ UINT32 RouteString: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;
+
+ UINT32 RsvdZ8;
+ UINT32 RsvdZ9;
+ UINT32 RsvdZ10;
+ UINT32 RsvdZ11;
+
+ UINT32 RsvdZ12;
+ UINT32 RsvdZ13;
+ UINT32 RsvdZ14;
+ UINT32 RsvdZ15;
+
+} SLOT_CONTEXT_64;
+
+
+//
+// 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;
+
+typedef struct _ENDPOINT_CONTEXT_64 {
+ 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;
+
+ UINT32 RsvdZ8;
+ UINT32 RsvdZ9;
+ UINT32 RsvdZ10;
+ UINT32 RsvdZ11;
+
+ UINT32 RsvdZ12;
+ UINT32 RsvdZ13;
+ UINT32 RsvdZ14;
+ UINT32 RsvdZ15;
+
+} ENDPOINT_CONTEXT_64;
+
+
+//
+// 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;
+
+typedef struct _INPUT_CONTRL_CONTEXT_64 {
+ UINT32 Dword1;
+ UINT32 Dword2;
+ UINT32 RsvdZ1;
+ UINT32 RsvdZ2;
+ UINT32 RsvdZ3;
+ UINT32 RsvdZ4;
+ UINT32 RsvdZ5;
+ UINT32 RsvdZ6;
+ UINT32 RsvdZ7;
+ UINT32 RsvdZ8;
+ UINT32 RsvdZ9;
+ UINT32 RsvdZ10;
+ UINT32 RsvdZ11;
+ UINT32 RsvdZ12;
+ UINT32 RsvdZ13;
+ UINT32 RsvdZ14;
+} INPUT_CONTRL_CONTEXT_64;
+
+//
+// 6.2.1 Device Context
+//
+typedef struct _DEVICE_CONTEXT {
+ SLOT_CONTEXT Slot;
+ ENDPOINT_CONTEXT EP[31];
+} DEVICE_CONTEXT;
+
+typedef struct _DEVICE_CONTEXT_64 {
+ SLOT_CONTEXT_64 Slot;
+ ENDPOINT_CONTEXT_64 EP[31];
+} DEVICE_CONTEXT_64;
+
+//
+// 6.2.5 Input Context
+//
+typedef struct _INPUT_CONTEXT {
+ INPUT_CONTRL_CONTEXT InputControlContext;
+ SLOT_CONTEXT Slot;
+ ENDPOINT_CONTEXT EP[31];
+} INPUT_CONTEXT;
+
+typedef struct _INPUT_CONTEXT_64 {
+ INPUT_CONTRL_CONTEXT_64 InputControlContext;
+ SLOT_CONTEXT_64 Slot;
+ ENDPOINT_CONTEXT_64 EP[31];
+} INPUT_CONTEXT_64;
+
+
+/**
+ Initialize the XHCI host controller for schedule.
+
+ @param Xhc The XHCI Instance to be initialized.
+
+**/
+VOID
+XhcInitSched (
+ IN USB_XHCI_INSTANCE *Xhc
+ );
+
+/**
+ Free the resouce allocated at initializing schedule.
+
+ @param Xhc The XHCI Instance.
+
+**/
+VOID
+XhcFreeSched (
+ IN USB_XHCI_INSTANCE *Xhc
+ );
+
+/**
+ Ring the door bell to notify XHCI there is a transaction to be executed through URB.
+
+ @param Xhc The XHCI Instance.
+ @param Urb The URB to be rung.
+
+ @retval EFI_SUCCESS Successfully ring the door bell.
+
+**/
+EFI_STATUS
+RingIntTransferDoorBell (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN URB *Urb
+ );
+
+/**
+ Execute the transfer by polling the URB. This is a synchronous operation.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *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 Instance.
+ @param BusAddr The logical device address assigned by UsbBus driver.
+ @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_INSTANCE *Xhc,
+ IN UINT8 BusAddr,
+ IN UINT8 EpNum
+ );
+
+/**
+ Remove all the asynchronous interrupt transfers.
+
+ @param Xhc The XHCI Instance.
+
+**/
+VOID
+XhciDelAllAsyncIntTransfers (
+ IN USB_XHCI_INSTANCE *Xhc
+ );
+
+/**
+ Set Bios Ownership
+
+ @param Xhc The XHCI Instance.
+
+**/
+VOID
+XhcSetBiosOwnership (
+ IN USB_XHCI_INSTANCE *Xhc
+ );
+
+/**
+ Clear Bios Ownership
+
+ @param Xhc The XHCI Instance.
+
+**/
+VOID
+XhcClearBiosOwnership (
+ IN USB_XHCI_INSTANCE *Xhc
+ );
+
+/**
+ Find out the slot id according to the device's route string.
+
+ @param Xhc The XHCI Instance.
+ @param RouteString The route string described the device location.
+
+ @return The slot id used by the device.
+
+**/
+UINT8
+EFIAPI
+XhcRouteStringToSlotId (
+ IN USB_XHCI_INSTANCE *Xhc,
+ 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 Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 Dci
+ );
+
+/**
+ Interrupt transfer periodic check handler.
+
+ @param Event Interrupt event.
+ @param Context Pointer to USB_XHCI_INSTANCE.
+
+**/
+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 Instance.
+ @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_INSTANCE *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 Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 PortNum,
+ IN UINT8 TTT,
+ IN UINT8 MTT
+ );
+
+
+/**
+ Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @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
+XhcConfigHubContext64 (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 DeviceSpeed,
+ IN USB_CONFIG_DESCRIPTOR *ConfigDesc
+ );
+
+
+/**
+ Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @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
+XhcSetConfigCmd64 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 DeviceSpeed,
+ IN USB_CONFIG_DESCRIPTOR *ConfigDesc
+ );
+
+/**
+ Set interface through XHCI's Configure_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param DeviceSpeed The device's speed.
+ @param ConfigDesc The pointer to the usb device configuration descriptor.
+ @param Request USB device request to send.
+
+ @retval EFI_SUCCESS Successfully set interface.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcSetInterface (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 DeviceSpeed,
+ IN USB_CONFIG_DESCRIPTOR *ConfigDesc,
+ IN EFI_USB_DEVICE_REQUEST *Request
+ );
+
+/**
+ Set interface through XHCI's Configure_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param DeviceSpeed The device's speed.
+ @param ConfigDesc The pointer to the usb device configuration descriptor.
+ @param Request USB device request to send.
+
+ @retval EFI_SUCCESS Successfully set interface.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcSetInterface64 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 DeviceSpeed,
+ IN USB_CONFIG_DESCRIPTOR *ConfigDesc,
+ IN EFI_USB_DEVICE_REQUEST *Request
+ );
+
+/**
+ Find out the actual device address according to the requested device address from UsbBus.
+
+ @param Xhc The XHCI Instance.
+ @param BusDevAddr The requested device address by UsbBus upper driver.
+
+ @return The actual device address assigned to the device.
+
+**/
+UINT8
+EFIAPI
+XhcBusDevAddrToSlotId (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 BusDevAddr
+ );
+
+/**
+ Assign and initialize the device slot for a new device.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN USB_DEV_ROUTE ParentRouteChart,
+ IN UINT16 ParentPort,
+ IN USB_DEV_ROUTE RouteChart,
+ IN UINT8 DeviceSpeed
+ );
+
+/**
+ Assign and initialize the device slot for a new device.
+
+ @param Xhc The XHCI Instance.
+ @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
+XhcInitializeDeviceSlot64 (
+ IN USB_XHCI_INSTANCE *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 Instance.
+ @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_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT32 MaxPacketSize
+ );
+
+
+/**
+ Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
+
+ @param Xhc The XHCI Instance.
+ @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
+XhcEvaluateContext64 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT32 MaxPacketSize
+ );
+
+
+/**
+ Disable the specified device slot.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be disabled.
+
+ @retval EFI_SUCCESS Successfully disable the device slot.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcDisableSlotCmd (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId
+ );
+
+
+/**
+ Disable the specified device slot.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be disabled.
+
+ @retval EFI_SUCCESS Successfully disable the device slot.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcDisableSlotCmd64 (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId
+ );
+
+
+/**
+ Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
+
+ @param Xhc The XHCI Instance.
+ @param TrsRing The transfer ring to sync.
+
+ @retval EFI_SUCCESS The transfer ring is synchronized successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcSyncTrsRing (
+ IN USB_XHCI_INSTANCE *Xhc,
+ TRANSFER_RING *TrsRing
+ );
+
+/**
+ Synchronize the specified event ring to update the enqueue and dequeue pointer.
+
+ @param Xhc The XHCI Instance.
+ @param EvtRing The event ring to sync.
+
+ @retval EFI_SUCCESS The event ring is synchronized successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcSyncEventRing (
+ IN USB_XHCI_INSTANCE *Xhc,
+ EVENT_RING *EvtRing
+ );
+
+/**
+ Check if there is a new generated event.
+
+ @param Xhc The XHCI Instance.
+ @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_INSTANCE *Xhc,
+ IN EVENT_RING *EvtRing,
+ OUT TRB_TEMPLATE **NewEvtTrb
+ );
+
+/**
+ Create XHCI transfer ring.
+
+ @param Xhc The XHCI Instance.
+ @param TrbNum The number of TRB in the ring.
+ @param TransferRing The created transfer ring.
+
+**/
+VOID
+CreateTransferRing (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINTN TrbNum,
+ OUT TRANSFER_RING *TransferRing
+ );
+
+/**
+ Create XHCI event ring.
+
+ @param Xhc The XHCI Instance.
+ @param EventRing The created event ring.
+
+**/
+VOID
+CreateEventRing (
+ IN USB_XHCI_INSTANCE *Xhc,
+ 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 Instance.
+ @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_INSTANCE *Xhc,
+ IN URB *Urb
+ );
+
+/**
+ System software shall use a Stop Endpoint Command (section 4.6.9) and the Set TR Dequeue Pointer
+ Command (section 4.6.10) to remove the timed-out TDs from the xHC transfer ring. 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 Instance.
+ @param Urb The urb which doesn't get completed in a specified timeout range.
+
+ @retval EFI_SUCCESS The dequeuing of the TDs is successful.
+ @retval Others Failed to stop the endpoint and dequeue the TDs.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcDequeueTrbFromEndpoint (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN URB *Urb
+ );
+
+/**
+ Stop endpoint through XHCI's Stop_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param Dci The device context index of endpoint.
+
+ @retval EFI_SUCCESS Stop endpoint successfully.
+ @retval Others Failed to stop endpoint.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcStopEndpoint (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 Dci
+ );
+
+/**
+ Reset endpoint through XHCI's Reset_Endpoint cmd.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param Dci The device context index of endpoint.
+
+ @retval EFI_SUCCESS Reset endpoint successfully.
+ @retval Others Failed to reset endpoint.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcResetEndpoint (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 Dci
+ );
+
+/**
+ Set transfer ring dequeue pointer through XHCI's Set_Tr_Dequeue_Pointer cmd.
+
+ @param Xhc The XHCI Instance.
+ @param SlotId The slot id to be configured.
+ @param Dci The device context index of endpoint.
+ @param Urb The dequeue pointer of the transfer ring specified
+ by the urb to be updated.
+
+ @retval EFI_SUCCESS Set transfer ring dequeue pointer succeeds.
+ @retval Others Failed to set transfer ring dequeue pointer.
+
+**/
+EFI_STATUS
+EFIAPI
+XhcSetTrDequeuePointer (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN UINT8 SlotId,
+ IN UINT8 Dci,
+ IN URB *Urb
+ );
+
+/**
+ Create a new URB for a new transaction.
+
+ @param Xhc The XHCI Instance
+ @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_INSTANCE *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
+ );
+
+/**
+ Free an allocated URB.
+
+ @param Xhc The XHCI device.
+ @param Urb The URB to free.
+
+**/
+VOID
+XhcFreeUrb (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN URB *Urb
+ );
+
+/**
+ Create a transfer TRB.
+
+ @param Xhc The XHCI Instance
+ @param Urb The urb used to construct the transfer TRB.
+
+ @return Created TRB or NULL
+
+**/
+EFI_STATUS
+XhcCreateTransferTrb (
+ IN USB_XHCI_INSTANCE *Xhc,
+ IN URB *Urb
+ );
+
+#endif
|