diff options
Diffstat (limited to 'Core/EM/usb/rt/xhci.c')
-rw-r--r-- | Core/EM/usb/rt/xhci.c | 4306 |
1 files changed, 4306 insertions, 0 deletions
diff --git a/Core/EM/usb/rt/xhci.c b/Core/EM/usb/rt/xhci.c new file mode 100644 index 0000000..870e327 --- /dev/null +++ b/Core/EM/usb/rt/xhci.c @@ -0,0 +1,4306 @@ +//**************************************************************************** +//**************************************************************************** +//** ** +//** (C)Copyright 1985-2016, American Megatrends, Inc. ** +//** ** +//** All Rights Reserved. ** +//** ** +//** 5555 Oakbrook Pkwy, Norcross, GA 30093 ** +//** ** +//** Phone (770)-246-8600 ** +//** ** +//**************************************************************************** +//**************************************************************************** + +//********************************************************************** +//<AMI_FHDR_START> +// +// Name: XHCI.C +// +// Description: AMI XHCI driver. +// +//<AMI_FHDR_END> +//********************************************************************** + +#include "amiusb.h" +#include "amidef.h" +#include "usbdef.h" +#if USB_RUNTIME_DRIVER_IN_SMM +#include <AmiBufferValidationLib.h> +#endif + +UINT8 XHCI_Start (HC_STRUC*); +UINT8 XHCI_Stop (HC_STRUC*); +UINT8 XHCI_EnumeratePorts (HC_STRUC*); +UINT8 XHCI_DisableInterrupts (HC_STRUC*); +UINT8 XHCI_EnableInterrupts (HC_STRUC*); +UINT8 XHCI_ProcessInterrupt(HC_STRUC*); +UINT8 XHCI_GetRootHubStatus (HC_STRUC*, UINT8, BOOLEAN); +UINT8 XHCI_DisableRootHub (HC_STRUC*,UINT8); +UINT8 XHCI_EnableRootHub (HC_STRUC*,UINT8); +UINT16 XHCI_ControlTransfer (HC_STRUC*,DEV_INFO*,UINT16,UINT16,UINT16,UINT8*,UINT16); +UINT32 XHCI_BulkTransfer (HC_STRUC*,DEV_INFO*,UINT8,UINT8*,UINT32); +UINT16 XHCI_InterruptTransfer (HC_STRUC*, DEV_INFO*, UINT8, UINT16, UINT8*, UINT16); +UINT8 XHCI_DeactivatePolling (HC_STRUC*,DEV_INFO*); +UINT8 XHCI_ActivatePolling (HC_STRUC*,DEV_INFO*); +UINT8 XHCI_DisableKeyRepeat (HC_STRUC*); +UINT8 XHCI_EnableKeyRepeat (HC_STRUC*); +UINT8 XHCI_EnableEndpoints (HC_STRUC*, DEV_INFO*, UINT8*); +UINT8 XHCI_InitDeviceData (HC_STRUC*,DEV_INFO*,UINT8,UINT8**); +UINT8 XHCI_DeinitDeviceData (HC_STRUC*,DEV_INFO*); +UINT8 XHCI_ResetRootHub (HC_STRUC*,UINT8); +UINT8 XHCI_ClearEndpointState(HC_STRUC*,DEV_INFO*,UINT8); //(EIP54283+) +UINT8 XHCI_GlobalSuspend (HC_STRUC*); //(EIP54018+) + +UINT8 XHCI_WaitForEvent(HC_STRUC*,XHCI_TRB*,TRB_TYPE,UINT8,UINT8,UINT8*,UINT16,VOID*); +TRB_RING* XHCI_InitXfrRing(USB3_HOST_CONTROLLER*, UINT8, UINT8); +TRB_RING* XHCI_GetXfrRing(USB3_HOST_CONTROLLER*, UINT8, UINT8); +UINT8 XHCI_GetSlotId(USB3_HOST_CONTROLLER*, DEV_INFO*); +UINT64 XHCI_Mmio64Read(HC_STRUC*, USB3_HOST_CONTROLLER*, UINTN); +VOID XHCI_Mmio64Write(HC_STRUC*, USB3_HOST_CONTROLLER*, UINTN, UINT64); +EFI_STATUS XHCI_InitRing(TRB_RING*, UINTN, UINT32, BOOLEAN); +UINT32* XHCI_GetTheDoorbell(USB3_HOST_CONTROLLER*, UINT8); +VOID UpdatePortStatusSpeed(UINT8, UINT8*); +UINT8 XHCI_ResetPort(USB3_HOST_CONTROLLER*, UINT8, BOOLEAN); +BOOLEAN XHCI_IsUsb3Port(USB3_HOST_CONTROLLER*, UINT8); +UINT8 XhciRingDoorbell(USB3_HOST_CONTROLLER*, UINT8, UINT8); +EFI_STATUS XhciExtCapParser(USB3_HOST_CONTROLLER*); +UINT8 XhciAddressDevice (HC_STRUC*, DEV_INFO*, UINT8); + +DEV_INFO* XHCI_GetDevInfo(UINTN); +VOID* XHCI_GetDeviceContext(USB3_HOST_CONTROLLER*, UINT8); +VOID* XHCI_GetContextEntry(USB3_HOST_CONTROLLER*, VOID*, UINT8); + +UINT8 UsbHubGetHubDescriptor(HC_STRUC*, DEV_INFO*, VOID*, UINT16); + +VOID MemFill (UINT8*, UINT32, UINT8); +UINT8 USB_ResetHubPort(HC_STRUC*, UINT8, UINT8); +UINT8 USB_DisconnectDevice (HC_STRUC*, UINT8, UINT8); +UINT8 USB_GetHubPortStatus(HC_STRUC*, UINT8, UINT8, BOOLEAN); + +UINT32 ReadPCIConfig(UINT16, UINT8); + +VOID USBKeyRepeat(HC_STRUC*, UINT8); + +extern USB_GLOBAL_DATA *gUsbData; +extern BOOLEAN gCheckUsbApiParameter; + +#if USB_DEV_KBD +VOID USBKBDPeriodicInterruptHandler(HC_STRUC*); +#endif + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_FillHCDEntries +// +// Description: +// This function fills the host controller driver routine pointers. +// +// Input: +// Ptr to the host controller header structure +// +// Output: +// Status: USB_SUCCESS = Success, USB_ERROR = Failure +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_FillHCDEntries ( + HCD_HEADER *fpHCDHeader +) +{ + fpHCDHeader->pfnHCDStart = XHCI_Start; + fpHCDHeader->pfnHCDStop = XHCI_Stop; + fpHCDHeader->pfnHCDEnumeratePorts = XHCI_EnumeratePorts; + fpHCDHeader->pfnHCDDisableInterrupts = XHCI_DisableInterrupts; + fpHCDHeader->pfnHCDEnableInterrupts = XHCI_EnableInterrupts; + fpHCDHeader->pfnHCDProcessInterrupt = XHCI_ProcessInterrupt; + fpHCDHeader->pfnHCDGetRootHubStatus = XHCI_GetRootHubStatus; + fpHCDHeader->pfnHCDDisableRootHub = XHCI_DisableRootHub; + fpHCDHeader->pfnHCDEnableRootHub = XHCI_EnableRootHub; + fpHCDHeader->pfnHCDControlTransfer = XHCI_ControlTransfer; + fpHCDHeader->pfnHCDBulkTransfer = XHCI_BulkTransfer; + fpHCDHeader->pfnHCDInterruptTransfer = XHCI_InterruptTransfer; + fpHCDHeader->pfnHCDDeactivatePolling = XHCI_DeactivatePolling; + fpHCDHeader->pfnHCDActivatePolling = XHCI_ActivatePolling; + fpHCDHeader->pfnHCDDisableKeyRepeat = XHCI_DisableKeyRepeat; + fpHCDHeader->pfnHCDEnableKeyRepeat = XHCI_EnableKeyRepeat; + fpHCDHeader->pfnHCDEnableEndpoints = XHCI_EnableEndpoints; + fpHCDHeader->pfnHCDInitDeviceData = XHCI_InitDeviceData; + fpHCDHeader->pfnHCDDeinitDeviceData = XHCI_DeinitDeviceData; + fpHCDHeader->pfnHCDResetRootHub = XHCI_ResetRootHub; + fpHCDHeader->pfnHCDClearEndpointState = XHCI_ClearEndpointState; //(EIP54283+) + fpHCDHeader->pfnHCDGlobalSuspend = XHCI_GlobalSuspend; //(EIP54018+) + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_Start +// +// Description: +// This API function is called to start a XHCI host controller. The input +// to the routine is the pointer to the HC structure that defines this host +// controller. The procedure flow is followed as it is described in 4.2 of +// XHCI specification. +// +// Input: +// HcStruc - Pointer to the HC structure +// +// Output: +// USB_SUCCESS = Success, USB_ERROR = Failure +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_Start ( + HC_STRUC *HcStruc +) +{ + XHCI_INTERRUPTER_REGS *Interrupter; + XHCI_ER_SEGMENT_ENTRY *Erst0Entry; + UINT32 i; + BOOLEAN PpSet = FALSE; + UINT8 PortNumber; + volatile XHCI_PORTSC *PortSC; + UINT32 LegCtlStsReg = 0; + EFI_STATUS EfiStatus = EFI_SUCCESS; + + USB3_HOST_CONTROLLER *Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + HcStruc->BaseAddress = (UINTN)Usb3Hc->CapRegs; + HcStruc->bNumPorts = Usb3Hc->MaxPorts; + +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMmioBuffer((VOID*)HcStruc->BaseAddress, HcStruc->BaseAddressSize); + if (EFI_ERROR(EfiStatus)) { + USB_DEBUG(3, "Usb Mmio address is invalid, it is in SMRAM\n"); + return USB_ERROR; + } +#endif + + if (((VOID*)Usb3Hc->OpRegs < (VOID*)HcStruc->BaseAddress) || + ((VOID*)(Usb3Hc->OpRegs + sizeof(XHCI_HC_OP_REGS)) > (VOID*)(HcStruc->BaseAddress + HcStruc->BaseAddressSize))) { + return USB_ERROR; + } + + // Wait controller ready + for (i = 0; i < 1000; i++) { + if (Usb3Hc->OpRegs->UsbSts.Field.Cnr == 0) break; + FixedDelay(100); // 100 us delay + } +// ASSERT(Usb3Hc->OpRegs->UsbSts.Field.Cnr == 0); + if (Usb3Hc->OpRegs->UsbSts.Field.Cnr) return USB_ERROR; + + // Check if the xHC is halted + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted == 0) { + Usb3Hc->OpRegs->UsbCmd.AllBits &= ~XHCI_CMD_RS; + // The xHC should halt within 16 ms. Section 5.4.1.1 + for (i = 0; i < 160; i++) { + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) break; + FixedDelay(100); // 100 us delay + } + ASSERT(Usb3Hc->OpRegs->UsbSts.Field.HcHalted); + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted == 0) return USB_ERROR; + } +#if XHCI_COMPLIANCE_MODE_WORKAROUND + for (PortNumber = 1; PortNumber <= Usb3Hc->MaxPorts; PortNumber++) { + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (PortNumber - 1))); +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMmioBuffer((VOID*)PortSC, sizeof(XHCI_PORTSC)); + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } +#endif + if (PortSC->Field.Pls == XHCI_PORT_LINK_COMPLIANCE_MODE) { + XHCI_ResetPort(Usb3Hc, PortNumber, FALSE); + } + } +#endif + // Reset controller + if ((Usb3Hc->DbCapRegs == NULL) || (Usb3Hc->DbCapRegs->DcCtrl.Dce == 0)) { + Usb3Hc->OpRegs->UsbCmd.AllBits |= XHCI_CMD_HCRST; + for (i = 0; i < 8000; i++) { + if (Usb3Hc->OpRegs->UsbCmd.Field.HcRst == 0) { + break; + } + FixedDelay(100); // 100 us delay + } + ASSERT(Usb3Hc->OpRegs->UsbCmd.Field.HcRst == 0); + if (Usb3Hc->OpRegs->UsbCmd.Field.HcRst) { + return USB_ERROR; // Controller can not be reset + } + } + + if ((Usb3Hc->CapRegs->RtsOff + (sizeof(UINT32) * 8) + (sizeof(XHCI_INTERRUPTER_REGS) * Usb3Hc->CapRegs->HcsParams1.MaxIntrs)) + > HcStruc->BaseAddressSize) { + return USB_ERROR; + } + + Usb3Hc->RtRegs = (XHCI_HC_RT_REGS*)((UINTN)Usb3Hc->CapRegs + Usb3Hc->CapRegs->RtsOff); + USB_DEBUG(3, "XHCI: RT registers are at %x\n", Usb3Hc->RtRegs); + + Usb3Hc->OpRegs->Config = Usb3Hc->MaxSlots; // Max device slots enabled + + XHCI_Mmio64Write(HcStruc, Usb3Hc, (UINTN)&Usb3Hc->OpRegs->DcbAap, (UINT64)(UINTN)Usb3Hc->DcbaaPtr); + + // Check if xHC support 64bit access capability + if (Usb3Hc->Access64) { + if(XHCI_Mmio64Read(HcStruc, Usb3Hc, (UINTN)&Usb3Hc->OpRegs->DcbAap) != (UINT64)(UINTN)Usb3Hc->DcbaaPtr) { + Usb3Hc->Access64 = 0; + XHCI_Mmio64Write(HcStruc, Usb3Hc, (UINTN)&Usb3Hc->OpRegs->DcbAap, (UINT64)(UINTN)Usb3Hc->DcbaaPtr); + } + } + + // 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. + + // Initialize Command Ring Segment: Size TRBS_PER_SEGMENT*16, 64 Bytes aligned + XHCI_InitRing(&Usb3Hc->CmdRing, (UINTN)Usb3Hc->DcbaaPtr + 0x2000, TRBS_PER_SEGMENT, TRUE); + USB_DEBUG(3, "CMD Ring is at %x\n", (UINTN)&Usb3Hc->CmdRing); + + // Write CRCR HC register with the allocated address. Set Ring Cycle State to 1. + XHCI_Mmio64Write(HcStruc, Usb3Hc, (UINTN)&Usb3Hc->OpRegs->Crcr, + (UINT64)(UINTN)Usb3Hc->CmdRing.Base + CRCR_RING_CYCLE_STATE); + + // Initialize and assign Event Ring + XHCI_InitRing(&Usb3Hc->EvtRing, (UINTN)Usb3Hc->DcbaaPtr + 0x2400, TRBS_PER_SEGMENT, FALSE); + USB_DEBUG(3, "EVT Ring is at %x\n", (UINTN)&Usb3Hc->EvtRing); + + // NOTE: This driver supports one Interrupter, hence it uses + // one Event Ring segment with TRBS_PER_SEGMENT TRBs in it. + + // Initialize ERST[0] + Erst0Entry = (XHCI_ER_SEGMENT_ENTRY*)((UINTN)Usb3Hc->DcbaaPtr + 0x1200); + Erst0Entry->RsBase = (UINT64)(UINTN)Usb3Hc->EvtRing.Base; + Erst0Entry->RsSize = TRBS_PER_SEGMENT; + + Interrupter = Usb3Hc->RtRegs->IntRegs; + + // Initialize Interrupter fields + Interrupter->Erstz = 1; // # of segments + // ER dequeue pointer + XHCI_Mmio64Write(HcStruc, Usb3Hc, (UINTN)&Interrupter->Erdp, (UINT64)(UINTN)Usb3Hc->EvtRing.QueuePtr); + // Seg Table location + XHCI_Mmio64Write(HcStruc, Usb3Hc, (UINTN)&Interrupter->Erstba, (UINT64)(UINTN)Erst0Entry); + Interrupter->IMod = XHCI_IMODI; // Max interrupt rate + Usb3Hc->OpRegs->UsbCmd.AllBits |= XHCI_CMD_INTE; + Interrupter->IMan |= 2; // Enable interrupt + + USB_DEBUG(3, "Transfer Rings structures start at %x\n", Usb3Hc->XfrRings); + + // Set PortPower unless PowerPortControl indicates otherwise + if (Usb3Hc->CapRegs->HccParams1.Ppc != 0) { + for (PortNumber = 1; PortNumber <= Usb3Hc->MaxPorts; PortNumber++) { + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (PortNumber - 1))); +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMmioBuffer((VOID*)PortSC, sizeof(XHCI_PORTSC)); + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } +#endif + if (PortSC->Field.Pp == 0) { + PortSC->Field.Pp = 1; // Set port power + PpSet = TRUE; + } + } + if (PpSet) FixedDelay(20 * 1000); // Wait for 20 ms, Section 5.4.8 + } + + // If xHC doesn't support HW SMI, should not touch USB Legacy Support Capability registers + //if (((HcStruc->dHCFlag & HC_STATE_EXTERNAL) && (XHCI_EVENT_SERVICE_MODE == 0)) || + // (USB_RUNTIME_DRIVER_IN_SMM == 0)) { + // Usb3Hc->ExtLegCap = NULL; + //} + + // Check if USB Legacy Support Capability is present. + if (Usb3Hc->ExtLegCap) { + // Set HC BIOS Owned Semaphore flag + Usb3Hc->ExtLegCap->LegSup.HcBiosOwned = 1; + //If XHCI doesn't support HW SMI, should not enable USB SMI in Legacy Support Capability register. + if (((!(HcStruc->dHCFlag & HC_STATE_EXTERNAL)) || (XHCI_EVENT_SERVICE_MODE != 0)) && + (USB_RUNTIME_DRIVER_IN_SMM != 0)) { + // Enable USB SMI, Ownership Change SMI and clear all status + LegCtlStsReg = Usb3Hc->ExtLegCap->LegCtlSts.AllBits; + LegCtlStsReg |= XHCI_SMI_ENABLE | XHCI_SMI_OWNERSHIP_CHANGE_ENABLE | + XHCI_SMI_OWNERSHIP_CHANGE | XHCI_SMI_PCI_CMD | XHCI_SMI_PCI_BAR; + Usb3Hc->ExtLegCap->LegCtlSts.AllBits = LegCtlStsReg; + } + } + + Usb3Hc->OpRegs->UsbCmd.AllBits |= XHCI_CMD_RS; + + for (i = 0; i < 100; i++) { + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted == 0) { + break; + } + FixedDelay(100); + } + ASSERT(Usb3Hc->OpRegs->UsbSts.Field.HcHalted == 0); + + HcStruc->dHCFlag |= HC_STATE_RUNNING; + + // Set USB_FLAG_DRIVER_STARTED flag when HC is running. + if (!(gUsbData->dUSBStateFlag & USB_FLAG_DRIVER_STARTED)) { + gUsbData->dUSBStateFlag |= USB_FLAG_DRIVER_STARTED; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (!(HcStruc->dHCFlag & HC_STATE_EXTERNAL)) { + UsbInstallHwSmiHandler(HcStruc); + } else { + USBSB_InstallXhciHwSmiHandler(); + } + if (HcStruc->HwSmiHandle != NULL) { + USBKeyRepeat(HcStruc, 0); + } +#endif + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_Stop +// +// Description: +// This function stops the XHCI controller. +// +// Input: +// HcStruc - Pointer to the HC structure +// +// Output: +// USB_SUCCESS or USB_ERROR +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_Stop ( + HC_STRUC *HcStruc +) +{ + UINT8 Port; + UINT32 i; + USB3_HOST_CONTROLLER *Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + XHCI_INTERRUPTER_REGS *Interrupter = NULL; + UINT32 LegCtlStsReg = 0; + UINT8 CompletionCode = 0; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + // Set the flag to aviod port enumeration + gUsbData->bEnumFlag = TRUE; // disable recursive enumeration + + for (Port = 1; Port <= Usb3Hc->MaxPorts; Port++) { + USB_DisconnectDevice(HcStruc, HcStruc->bHCNumber | BIT7, Port); + } + + // Port Change Detect bit may set by disabling ports. + //Usb3Hc->OpRegs->UsbSts.AllBits = XHCI_STS_PCD; + + if (XHCI_Mmio64Read(HcStruc, Usb3Hc, (UINTN)&Usb3Hc->OpRegs->Crcr) & CRCR_COMMAND_RUNNING) { + // Stop the command ring + XHCI_Mmio64Write(HcStruc, Usb3Hc, (UINTN)&Usb3Hc->OpRegs->Crcr, CRCR_COMMAND_STOP); + + CompletionCode = XHCI_TRB_CMDRINGSTOPPED; + XHCI_WaitForEvent( + HcStruc, NULL, XhciTCmdCompleteEvt, 0, 0, + &CompletionCode, XHCI_CMD_COMPLETE_TIMEOUT_MS, NULL); + } + + XHCI_ProcessInterrupt(HcStruc); + + // Clear the port enumeration flag + gUsbData->bEnumFlag = FALSE; + + // Disable interrupt + Usb3Hc->OpRegs->UsbCmd.AllBits &= ~XHCI_CMD_INTE; + Interrupter = Usb3Hc->RtRegs->IntRegs; + Interrupter->IMan &= ~BIT1; + + // Clear the Run/Stop bit + Usb3Hc->OpRegs->UsbCmd.AllBits &= ~XHCI_CMD_RS; + + // The xHC should halt within 16 ms. Section 5.4.1.1 + for (i = 0; i < 160; i++) { + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) break; + FixedDelay(100); + } + ASSERT(Usb3Hc->OpRegs->UsbSts.Field.HcHalted); + //if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted == 0) return USB_ERROR; + + // Check if USB Legacy Support Capability is present. + if(Usb3Hc->ExtLegCap != 0) { + // Clear HC BIOS Owned Semaphore flag + Usb3Hc->ExtLegCap->LegSup.HcBiosOwned = 0; + if (((!(HcStruc->dHCFlag & HC_STATE_EXTERNAL)) || (XHCI_EVENT_SERVICE_MODE != 0)) && + (USB_RUNTIME_DRIVER_IN_SMM != 0)) { + // Disable USB SMI and Clear all status + LegCtlStsReg = Usb3Hc->ExtLegCap->LegCtlSts.AllBits; + LegCtlStsReg &= ~XHCI_SMI_ENABLE; + LegCtlStsReg |= XHCI_SMI_OWNERSHIP_CHANGE | XHCI_SMI_PCI_CMD | XHCI_SMI_PCI_BAR; + Usb3Hc->ExtLegCap->LegCtlSts.AllBits = LegCtlStsReg; + } + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (HcStruc->HwSmiHandle != NULL) { + USBKeyRepeat(HcStruc, 3); + } +#endif + + // Set the HC state to stopped + HcStruc->dHCFlag &= ~(HC_STATE_RUNNING); + + CheckBiosOwnedHc(); + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_EnumeratePorts +// +// Description: +// This function enumerates the HC ports for devices. +// +// Input: +// HcStruc - Pointer to the HC structure +// +// Output: +// USB_SUCCESS or USB_ERROR +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_EnumeratePorts( + HC_STRUC *HcStruc +) +{ + //(EIP60327)> + UINT8 Count; + UINT8 Port; + USB3_HOST_CONTROLLER *Usb3Hc; + EFI_STATUS EfiStatus; + + if (gUsbData->bEnumFlag == TRUE) { + return USB_SUCCESS; + } + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return USB_ERROR;; + } + + USB_DEBUG(3, "XHCI_EnumeratePorts..\n"); + gUsbData->bIgnoreConnectStsChng = TRUE; //(EIP71962+) + gUsbData->bEnumFlag = TRUE; // disable recursive enumeration + + if (Usb3Hc->Usb2Protocol) { + for(Count = 0; Count < Usb3Hc->Usb2Protocol->PortCount; Count++) { + Port = Count + Usb3Hc->Usb2Protocol->PortOffset; + USBCheckPortChange(HcStruc, HcStruc->bHCNumber | BIT7, Port); + } + } + + if (Usb3Hc->Usb3Protocol) { + for(Count = 0; Count < Usb3Hc->Usb3Protocol->PortCount; Count++) { + Port = Count + Usb3Hc->Usb3Protocol->PortOffset; + if (Usb3Hc->Vid == XHCI_VL800_VID && Usb3Hc->Did == XHCI_VL800_DID) { + XHCI_ResetPort(Usb3Hc, Port , TRUE); + } + USBCheckPortChange(HcStruc, HcStruc->bHCNumber | BIT7, Port); + } + } + +// Usb3Hc->OpRegs->UsbSts.AllBits = XHCI_STS_PCD; // Clear PortChangeDetect + + gUsbData->bIgnoreConnectStsChng = FALSE; //(EIP71962+) + gUsbData->bEnumFlag = FALSE; // enable enumeration + + XHCI_ProcessInterrupt(HcStruc); + //<(EIP60327) + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_EnableInterrupts +// +// Description: +// This function enables the HC interrupts +// +// Input: +// HcStruc - Pointer to the HC structure +// +// Output: +// USB_ERROR On error, USB_SUCCESS On success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_EnableInterrupts ( + HC_STRUC* HcStruc +) +{ + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_DisableInterrupts +// +// Description: +// This function disables the HC interrupts +// +// Input: +// HcStruc - Pointer to the HC structure +// +// Output: +// USB_ERROR on error, USB_SUCCESS on success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_DisableInterrupts ( + HC_STRUC* HcStruc +) +{ + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_AdvanceEnqueuePtr +// +// Description: +// This function advances returns the pointer to the current TRB and anvances +// dequeue pointer. If the advance pointer is Link TRB, then it: 1) activates +// Link TRB by updating its cycle bit, 2) updates dequeue pointer to the value +// pointed by Link TRB. +// +// Input: +// Ring - TRB ring to be updated +// +// Output: +// TRB that can be used for command, transfer, etc. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +XHCI_TRB* +XHCI_AdvanceEnqueuePtr( + TRB_RING *Ring +) +{ + XHCI_TRB* Trb = Ring->QueuePtr; + EFI_STATUS Status = EFI_SUCCESS; + + if (Trb->TrbType == XhciTLink) { + Trb->CycleBit = Ring->CycleBit; + Ring->CycleBit ^= 1; + Ring->QueuePtr = Ring->Base; + + Trb = Ring->QueuePtr; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + Status = AmiValidateMemoryBuffer((VOID*)Trb, sizeof(XHCI_TRB)); + if (EFI_ERROR(Status)) { + return NULL; + } +#endif + // Clear the TRB + *(UINT32*)Trb = 0; + *((UINT32*)Trb + 1) = 0; + *((UINT32*)Trb + 2) = 0; + *((UINT32*)Trb + 3) &= BIT0; // Keep cycle bit + + //Trb->CycleBit = Ring->CycleBit; + Ring->QueuePtr++; + + return Trb; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_WaitForEvent +// +// Description: +// This function walks through the active TRBs in the event ring and looks for +// the command TRB to be complete. If found, returns SlotId and CompletionCode +// from the completed event TRB. In the end it processes the event ring, +// adjusting its Dequeue Pointer. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + //(EIP62376)> +UINT8 +XHCI_WaitForEvent( + HC_STRUC *HcStruc, + XHCI_TRB *TrbToCheck, + TRB_TYPE EventType, + UINT8 SlotId, + UINT8 Dci, + UINT8 *CompletionCode, + UINT16 TimeOutMs, + VOID *Data +) +{ + USB3_HOST_CONTROLLER *Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + XHCI_TRB *Trb; + UINT32 Count; + UINT8 Status; + UINT8 CycleBit; + UINT32 TimeoutValue = ((UINT32)TimeOutMs) * 100; // in 10 macrosecond unit + XHCI_NORMAL_XFR_TRB *ResidualTrb; //(EIP82555+) + EFI_STATUS EfiStatus = EFI_SUCCESS; + + for (Count = 0; TimeoutValue == 0 || Count < TimeoutValue; Count++) { + for (Trb = Usb3Hc->EvtRing.QueuePtr, + CycleBit = Usb3Hc->EvtRing.CycleBit;;) { +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMemoryBuffer((VOID*)Trb, sizeof(XHCI_TRB)); + if (EFI_ERROR(EfiStatus)) { + break; + } +#endif + if (Trb->CycleBit != CycleBit) { + // Command is not complete, break and retry + break; + } + + // Active TRB found + if (Trb->TrbType == EventType) { + if (EventType == XhciTCmdCompleteEvt) { + if (TrbToCheck) { + if((*(UINTN*)&Trb->Param1) == (UINTN)TrbToCheck) { + if (Data != NULL) { + *(UINT8*)Data = ((XHCI_CMDCOMPLETE_EVT_TRB*)Trb)->SlotId; + } + *CompletionCode = Trb->CompletionCode; + Status = Trb->CompletionCode == XHCI_TRB_SUCCESS? USB_SUCCESS:USB_ERROR; + goto DoneWaiting; + } + } else { + if (*CompletionCode != 0 && Trb->CompletionCode == *CompletionCode) { + Status = USB_SUCCESS; + goto DoneWaiting; + } + } + } else if (EventType == XhciTTransferEvt) { + if (((XHCI_TRANSFER_EVT_TRB*)Trb)->SlotId == SlotId && + ((XHCI_TRANSFER_EVT_TRB*)Trb)->EndpointId == Dci) { + if (Data != NULL) { + *(UINT32*)Data = ((XHCI_TRANSFER_EVT_TRB*)Trb)->TransferLength; + //(EIP82555+)> + if (Trb->CompletionCode == XHCI_TRB_SHORTPACKET) { + ResidualTrb = (XHCI_NORMAL_XFR_TRB*)(UINTN)((XHCI_TRANSFER_EVT_TRB*)Trb)->TrbPtr; + while (1) { + ResidualTrb->Isp = 0; + ResidualTrb->Ioc = 0; + if (ResidualTrb->Chain != 1) { + break; + } + ResidualTrb++; + if (ResidualTrb->TrbType == XhciTLink) { + ResidualTrb = (XHCI_NORMAL_XFR_TRB*)(UINTN)((XHCI_LINK_TRB*)ResidualTrb)->NextSegPtr; + } + *(UINT32*)Data += ResidualTrb->XferLength; + } + } + //<(EIP82555+) + } + *CompletionCode = Trb->CompletionCode; + Status = (Trb->CompletionCode == XHCI_TRB_SUCCESS || + Trb->CompletionCode == XHCI_TRB_SHORTPACKET)? USB_SUCCESS:USB_ERROR; + goto DoneWaiting; + } + } + } + // Advance TRB pointer + if (Trb == Usb3Hc->EvtRing.LastTrb) { + Trb = Usb3Hc->EvtRing.Base; + CycleBit ^= 1; + } else { + Trb++; + } + if (Trb == Usb3Hc->EvtRing.QueuePtr) { + // Event ring is full, return error + USB_DEBUG(3, "XHCI: Event Ring is full...\n"); + ASSERT(0); + *CompletionCode = XHCI_TRB_EVENTRINGFULL_ERROR; + Status = USB_ERROR; + break; + } + } + FixedDelay(10); // 10 us out of TimeOutMs + } + + USB_DEBUG(3, "XHCI: execution time-out.\n"); + + *CompletionCode = XHCI_TRB_EXECUTION_TIMEOUT_ERROR; + Status = USB_ERROR; + +DoneWaiting: + XHCI_ProcessInterrupt(HcStruc); + + return Status; +} + //<(EIP62376) + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_ExecuteCommand +// +// Description: +// This function places a given command in the Command Ring, rings HC doorbell, +// and waits for the command completion. +// +// Output: +// USB_ERROR on execution failure, otherwise USB_SUCCESS +// Params - pointer to the command specific data. +// +// Notes: +// Caller is responsible for a data placeholder. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_ExecuteCommand( + HC_STRUC *HcStruc, + TRB_TYPE Cmd, + VOID *Params +) +{ + volatile UINT32 *Doorbell; + UINT8 CompletionCode = 0; + UINT8 SlotId; + UINT8 Status; + USB3_HOST_CONTROLLER *Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + XHCI_TRB *Trb = XHCI_AdvanceEnqueuePtr(&Usb3Hc->CmdRing); + UINT16 TimeOut = XHCI_CMD_COMPLETE_TIMEOUT_MS; + EFI_STATUS EfiStatus = EFI_SUCCESS; + + if (Trb == NULL) { + return USB_ERROR; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMemoryBuffer((VOID*)Trb, sizeof(XHCI_TRB)); + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR;; + } +#endif + + Trb->TrbType = Cmd; // Set TRB type + + // Fill in the command TRB fields + switch (Cmd) { + case XhciTAddressDeviceCmd: + TimeOut = XHCI_ADDR_CMD_COMPLETE_TIMEOUT_MS; + ((XHCI_ADDRESSDEV_CMD_TRB*)Trb)->InpCtxAddress = (UINT64)(UINTN)Usb3Hc->InputContext; + ((XHCI_ADDRESSDEV_CMD_TRB*)Trb)->SlotId = *((UINT8*)Params); + ((XHCI_ADDRESSDEV_CMD_TRB*)Trb)->Bsr = *((UINT8*)Params + 1); + break; + case XhciTEvaluateContextCmd: + case XhciTConfigureEndpointCmd: + ((XHCI_CONFIGURE_EP_CMD_TRB*)Trb)->InpCtxAddress = (UINT64)(UINTN)Usb3Hc->InputContext; + ((XHCI_CONFIGURE_EP_CMD_TRB*)Trb)->SlotId = *((UINT8*)Params); + ((XHCI_CONFIGURE_EP_CMD_TRB*)Trb)->Dc = 0; + break; + case XhciTResetEndpointCmd: + ((XHCI_RESET_EP_CMD_TRB*)Trb)->Tsp = 0; + ((XHCI_RESET_EP_CMD_TRB*)Trb)->SlotId = *((UINT8*)Params); + ((XHCI_RESET_EP_CMD_TRB*)Trb)->EndpointId = *((UINT8*)Params+1); + break; + case XhciTSetTRDequeuePointerCmd: + ((XHCI_SET_TRPTR_CMD_TRB*)Trb)->TrPointer = ((XHCI_SET_TRPTR_CMD_TRB*)Params)->TrPointer; + ((XHCI_SET_TRPTR_CMD_TRB*)Trb)->EndpointId = ((XHCI_SET_TRPTR_CMD_TRB*)Params)->EndpointId; + ((XHCI_SET_TRPTR_CMD_TRB*)Trb)->SlotId = ((XHCI_SET_TRPTR_CMD_TRB*)Params)->SlotId; + break; + case XhciTDisableSlotCmd: + ((XHCI_DISABLESLOT_CMD_TRB*)Trb)->SlotId = *((UINT8*)Params); + break; + //(EIP54300+)> + case XhciTStopEndpointCmd: + ((XHCI_STOP_EP_CMD_TRB*)Trb)->SlotId = *((UINT8*)Params); + ((XHCI_STOP_EP_CMD_TRB*)Trb)->EndpointId = *((UINT8*)Params+1); + break; + //<(EIP54300+) + } + + Trb->CycleBit = Usb3Hc->CmdRing.CycleBit; + + // Ring the door bell and see Event Ring update + Doorbell = (UINT32*)((UINTN)Usb3Hc->CapRegs + Usb3Hc->DbOffset); + *Doorbell = 0; // HC doorbell is #0 + + Status = XHCI_WaitForEvent( + HcStruc, Trb, XhciTCmdCompleteEvt, 0, 0, //(EIP62376) + &CompletionCode, TimeOut, &SlotId); + + if (Status == USB_ERROR) { + USB_DEBUG(3, "XHCI command completion error code: %d\n", CompletionCode); + if (CompletionCode == XHCI_TRB_EXECUTION_TIMEOUT_ERROR) { + XHCI_Mmio64Write(HcStruc, Usb3Hc, (UINTN)&Usb3Hc->OpRegs->Crcr, CRCR_COMMAND_ABORT); + + CompletionCode = XHCI_TRB_COMMANDABORTED; + XHCI_WaitForEvent( + HcStruc, Trb, XhciTCmdCompleteEvt, 0, 0, + &CompletionCode, XHCI_CMD_COMPLETE_TIMEOUT_MS, NULL); + } + return Status; + } + + switch (Cmd) { + case XhciTEnableSlotCmd: + USB_DEBUG(3, "XHCI: Enable Slot command complete, SlotID %d\n", SlotId); + *((UINT8*)Params) = SlotId; + break; + case XhciTEvaluateContextCmd: + USB_DEBUG(3, "XHCI: Evaluate Context command complete.\n"); + break; + case XhciTConfigureEndpointCmd: + USB_DEBUG(3, "XHCI: Configure Endpoint command complete.\n"); + break; + case XhciTResetEndpointCmd: + USB_DEBUG(3, "XHCI: Reset Endpoint command complete (slot#%x dci#%x).\n", + *((UINT8*)Params), *((UINT8*)Params+1)); + // Xhci speci 1.1 4.6.8 Reset Endpoint + // Software shall be responsible for timing the Reset "recovery interval" required by USB. + FixedDelay(XHCI_RESET_EP_DELAY_MS * 1000); + break; + case XhciTSetTRDequeuePointerCmd: + USB_DEBUG(3, "XHCI: Set TR pointer command complete.\n"); + break; + case XhciTDisableSlotCmd: + USB_DEBUG(3, "XHCI: DisableSlot command complete.\n"); + break; + //(EIP54300+)> + case XhciTStopEndpointCmd: + USB_DEBUG(3, "XHCI: Stop Endpoint command complete (slot#%x dci#%x).\n", + *((UINT8*)Params), *((UINT8*)Params+1)); + break; + //<(EIP54300+) + } + + return USB_SUCCESS; +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_ProcessPortChanges +// +// Description: +// This function process root hub port changes. +// +// Input: +// HcStruc - Pointer to the HC structure +// +// Output: +// USB_SUCCESS or USB_ERROR +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_ProcessPortChanges( + HC_STRUC *HcStruc, + USB3_HOST_CONTROLLER *Usb3Hc +) +{ + UINT8 Port; + BOOLEAN PortChanged; + volatile XHCI_PORTSC *PortSC; + EFI_STATUS EfiStatus = EFI_SUCCESS; + + + if (gUsbData->bEnumFlag == TRUE) return USB_SUCCESS; + + gUsbData->bEnumFlag = TRUE; // disable recursive enumeration + + FixedDelay(XHCI_WAIT_PORT_STABLE_DELAY_MS * 1000); + + do { + PortChanged = FALSE; + + for (Port = 1; Port <= Usb3Hc->MaxPorts; Port++) { + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (Port - 1))); +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMmioBuffer((VOID*)PortSC, sizeof(XHCI_PORTSC)); + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } +#endif + if (PortSC->Field.Csc) { + USBCheckPortChange(HcStruc, HcStruc->bHCNumber | BIT7, Port); + PortChanged = TRUE; + } + } + } while (PortChanged); + + Usb3Hc->OpRegs->UsbSts.AllBits = XHCI_STS_PCD; // Clear PortChangeDetect + + gUsbData->bEnumFlag = FALSE; // enable enumeration + + return USB_SUCCESS; +} + + //(EIP54283+)> +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_ResetEndpoint +// +// Description: +// This function is called to reset endpoint. +// +// Input: +// Stalled EP data - SlotId and DCI +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_ResetEndpoint( + USB3_HOST_CONTROLLER *Usb3Hc, + HC_STRUC *HcStruc, + UINT8 SlotId, + UINT8 Dci +) +{ + UINT16 EpInfo; + UINT8 Status = USB_SUCCESS; + XHCI_EP_CONTEXT *EpCtx; + + EpCtx = (XHCI_EP_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, + XHCI_GetDeviceContext(Usb3Hc, SlotId), Dci); + + // The Reset Endpoint Command is issued by software to recover + // from a halted condition on an endpoint. + if (EpCtx->EpState == XHCI_EP_STATE_HALTED) { + // Reset stalled endpoint + EpInfo = (Dci << 8) + SlotId; + Status = XHCI_ExecuteCommand(HcStruc, XhciTResetEndpointCmd, &EpInfo); + } + //ASSERT(Status == USB_SUCCESS); + return Status; +} + //<(EIP54283+) + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_ClearStalledEp +// +// Description: +// This function is called to restart endpoint. After Endpoint STALLs, it +// transitions from Halted to Stopped state. It is restored back to Running +// state by moving the endpoint ring dequeue pointer past the failed control +// transfer with a Set TR Dequeue Pointer. Then it is restarted by ringing the +// doorbell. Alternatively endpint is restarted using Configure Endpoint command. +// +// Input: +// Stalled EP data - SlotId and DCI +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_ClearStalledEp( + USB3_HOST_CONTROLLER *Usb3Hc, + HC_STRUC *HcStruc, + UINT8 SlotId, + UINT8 Dci +) +{ + UINT16 EpInfo; + TRB_RING *XfrRing; + UINT8 Status; + XHCI_SET_TRPTR_CMD_TRB Trb; + XHCI_EP_CONTEXT *EpCtx; +// volatile UINT32 *Doorbell; //(EIP61849-) + +/* +Stalled Endpoints By Sarah Sharp, Linux XHCI driver developer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + When a control endpoint stalls, the next control transfer will clear the stall. +The USB core doesn't call down to the host controller driver's endpoint_reset() +method when control endpoints stall, so the xHCI driver has to do all its stall +handling for internal state in its interrupt handler. + + When the host stalls on a control endpoint, it may stop on the data phase or +status phase of the control transfer. Like other stalled endpoints, the xHCI +driver needs to queue a Reset Endpoint command and move the hardware's control +endpoint ring dequeue pointer past the failed control transfer (with a Set TR +Dequeue Pointer or a Configure Endpoint command). + + Since the USB core doesn't call usb_hcd_reset_endpoint() for control endpoints, +we need to do this in interrupt context when we get notified of the stalled +transfer. URBs may be queued to the hardware before these two commands complete. +The endpoint queue will be restarted once both commands complete. + + When an endpoint on a device under an xHCI host controller stalls, the host +controller driver must let the hardware know that the USB core has successfully +cleared the halt condition. The HCD submits a Reset Endpoint Command, which will +clear the toggle bit for USB 2.0 devices, and set the sequence number to zero for +USB 3.0 devices. + + The xHCI urb_enqueue will accept new URBs while the endpoint is halted, and +will queue them to the hardware rings. However, the endpoint doorbell will not +be rung until the Reset Endpoint Command completes. Don't queue a reset endpoint +command for root hubs. khubd clears halt conditions on the roothub during the +initialization process, but the roothub isn't a real device, so the xHCI host +controller doesn't need to know about the cleared halt. +*/ + EpCtx = (XHCI_EP_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, + XHCI_GetDeviceContext(Usb3Hc, SlotId), Dci); + + // The Reset Endpoint Command is issued by software to recover + // from a halted condition on an endpoint. + if (EpCtx->EpState == XHCI_EP_STATE_HALTED) { + // Reset stalled endpoint + EpInfo = (Dci << 8) + SlotId; + Status = XHCI_ExecuteCommand(HcStruc, XhciTResetEndpointCmd, &EpInfo); + } + //ASSERT(Status == USB_SUCCESS); + + // Set TR Dequeue Pointer command may be executed only if the target + // endpoint is in the Error or Stopped state. + if ((EpCtx->EpState == XHCI_EP_STATE_STOPPED) || + (EpCtx->EpState == XHCI_EP_STATE_ERROR)) { + + XfrRing = XHCI_GetXfrRing(Usb3Hc, SlotId, Dci-1); + + Trb.TrPointer = (UINT64)((UINTN)XfrRing->QueuePtr + XfrRing->CycleBit); // Set up DCS + Trb.EndpointId = Dci; + Trb.SlotId = SlotId; + + Status = XHCI_ExecuteCommand(HcStruc, XhciTSetTRDequeuePointerCmd, &Trb); + //ASSERT(Status == USB_SUCCESS); + } + +// Doorbell = XHCI_GetTheDoorbell(Usb3Hc, SlotId); //(EIP61849-) +// *Doorbell = Dci; //(EIP61849-) + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_ProcessXferEvt +// +// Description: +// This function processes a transfer event and gives control to the device +// specific routines. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +XHCI_ProcessXferEvt( + USB3_HOST_CONTROLLER *Usb3Hc, + HC_STRUC *HcStruc, + XHCI_TRANSFER_EVT_TRB *XferEvtTrb +) +{ + DEV_INFO *DevInfo; + UINT8 SlotId = XferEvtTrb->SlotId; + UINT8 Dci = XferEvtTrb->EndpointId; + XHCI_NORMAL_XFR_TRB *Trb = (XHCI_NORMAL_XFR_TRB*)XferEvtTrb->TrbPtr; + volatile UINT32 *Doorbell = XHCI_GetTheDoorbell(Usb3Hc, SlotId); + TRB_RING *XfrRing = XHCI_GetXfrRing(Usb3Hc, SlotId, Dci-1); + UINT16 BytesTransferred; + UINT8 PortStatus = USB_PORT_STAT_DEV_ENABLED; + UINT8 i; + + DevInfo = XHCI_GetDevInfo((UINTN)Trb->DataBuffer); + if (DevInfo == NULL) return; + + switch (XferEvtTrb->CompletionCode) { + case XHCI_TRB_SUCCESS: + case XHCI_TRB_SHORTPACKET: + // Check for the keyboard event + + //(EIP38434+)> + if ((DevInfo->bCallBackIndex) && + (DevInfo->bCallBackIndex <= MAX_CALLBACK_FUNCTION) && + (DevInfo->fpPollTDPtr != NULL)) { + + if (gUsbData->aCallBackFunctionTable[DevInfo->bCallBackIndex-1]) { + + if (gUsbData->ProcessingPeriodicList == FALSE) { + for (i = 0; i < XHCI_MAX_PENDING_INTERRUPT_TRANSFER; i++) { + if (Usb3Hc->PendingInterruptTransfer[i].Trb == NULL) { + break; + } + } + if (i != XHCI_MAX_PENDING_INTERRUPT_TRANSFER) { + Usb3Hc->PendingInterruptTransfer[i].Trb = Trb; + Usb3Hc->PendingInterruptTransfer[i].TransferredLength = + DevInfo->PollingLength - XferEvtTrb->TransferLength; + return; + } + } + // + // Get the size of data transferred + // + BytesTransferred = DevInfo->PollingLength - XferEvtTrb->TransferLength; + (*gUsbData->aCallBackFunctionTable[DevInfo->bCallBackIndex-1]) + (HcStruc, DevInfo, NULL, DevInfo->fpPollTDPtr, + BytesTransferred); + } + } + //<(EIP38434+) + break; + + case XHCI_TRB_BABBLE_ERROR: + case XHCI_TRB_TRANSACTION_ERROR: + case XHCI_TRB_STALL_ERROR: + // When the device is disconnecting, the transaction will be error, + // we need to check the port status + PortStatus = USB_GetHubPortStatus(HcStruc, DevInfo->bHubDeviceNumber, DevInfo->bHubPortNumber, FALSE); + if (PortStatus == USB_ERROR) { + PortStatus = 0; + } + if (PortStatus & USB_PORT_STAT_DEV_ENABLED) { + XHCI_ClearStalledEp(Usb3Hc, HcStruc, SlotId, Dci); + } + break; + } + // Check if this device is still enabled + if ((PortStatus & USB_PORT_STAT_DEV_ENABLED) && (DevInfo->fpPollTDPtr != NULL)) { + Trb = (XHCI_NORMAL_XFR_TRB*)XHCI_AdvanceEnqueuePtr(XfrRing); + if (Trb == NULL) { + return; + } + Trb->TrbType = XhciTNormal; + Trb->DataBuffer = (UINT64)(UINTN)DevInfo->fpPollTDPtr; + Trb->XferLength = DevInfo->PollingLength; + Trb->Isp = 1; //(EIP51478+) + Trb->Ioc = 1; + Trb->CycleBit = XfrRing->CycleBit; + + // Ring the door bell to start polling interrupt endpoint + *Doorbell = Dci; + } +} + + //(EIP60460+)> +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_ProcessPortStsChgEvt +// +// Description: +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +XHCI_ProcessPortStsChgEvt( + USB3_HOST_CONTROLLER *Usb3Hc, + HC_STRUC *HcStruc, + XHCI_PORTSTSCHG_EVT_TRB *PortStsChgEvtTrb +) +{ + volatile XHCI_PORTSC *PortSC; + DEV_INFO *DevInfo; + UINT8 i; + EFI_STATUS EfiStatus = EFI_SUCCESS; + + if (((Usb3Hc->Vid != XHCI_FL100X_VID) || (Usb3Hc->Did != XHCI_FL1000_DID && + Usb3Hc->Did != XHCI_FL1009_DID)) && (Usb3Hc->Vid != XHCI_INTEL_VID)) { + return; + } + + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (PortStsChgEvtTrb->PortId - 1))); + +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMmioBuffer((VOID*)PortSC, sizeof(XHCI_PORTSC)); + if (EFI_ERROR(EfiStatus)) { + return; + } +#endif + + if (PortSC->Field.Csc && PortSC->Field.Ccs == 0) { + for (i = 1; i < MAX_DEVICES; i++) { + DevInfo = &gUsbData->aDevInfoTable[i]; + if ((DevInfo->Flag & DEV_INFO_VALIDPRESENT) + != DEV_INFO_VALIDPRESENT) { + continue; + } + if ((DevInfo->bHubDeviceNumber == (HcStruc->bHCNumber | BIT7)) && + DevInfo->bHubPortNumber == PortStsChgEvtTrb->PortId) { + DevInfo->Flag |= DEV_INFO_DEV_DISCONNECTING; + } + } + } +} + //<(EIP60460+) + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: XHCI_ProcessInterrupt +// +// Description: This is the XHCI controller event handler. It walks through +// the Event Ring and executes the event associated code if needed. Updates +// the Event Ring Data Pointer in the xHC to let it know which events are +// completed. +// +// Input: +// HcStruc - Pointer to the HC structure +// +// Output: +// USB_ERROR - Need more Interrupt processing +// USB_SUCCESS - No interrupts pending +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_ProcessInterrupt( + HC_STRUC *HcStruc +) +{ + XHCI_TRB *Trb; + UINTN XhciBaseAddress; + UINT32 Imod; + UINT8 i; + UINT8 SlotId; + UINT8 Dci; + volatile UINT32 *Doorbell; + TRB_RING *XfrRing; + DEV_INFO *DevInfo; + XHCI_NORMAL_XFR_TRB *XfrTrb; + USB3_HOST_CONTROLLER *Usb3Hc; + EFI_STATUS EfiStatus = EFI_SUCCESS; + UINTN DeviceContextSize; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + if (!(ReadPCIConfig(HcStruc->wBusDevFuncNum, USB_REG_COMMAND) & BIT1)) { + return USB_SUCCESS; + } + + XhciBaseAddress = ReadPCIConfig(HcStruc->wBusDevFuncNum, USB_MEM_BASE_ADDRESS); + if (((XhciBaseAddress & (BIT1 | BIT2)) == BIT2) && ((sizeof(VOID*) / sizeof(UINT32) == 2))){ + XhciBaseAddress |= Shl64((UINTN)ReadPCIConfig(HcStruc->wBusDevFuncNum, + USB_MEM_BASE_ADDRESS + 0x04), 32); + } + + XhciBaseAddress &= ~(0x7F); + + if (XhciBaseAddress != (UINTN)Usb3Hc->CapRegs) { +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMmioBuffer((VOID*)XhciBaseAddress, HcStruc->BaseAddressSize); + if (EFI_ERROR(EfiStatus)) { + USB_DEBUG(3, "Usb Mmio address is invalid, it is in SMRAM\n"); + return USB_ERROR; + } +#endif + HcStruc->BaseAddress = XhciBaseAddress; + (UINTN)Usb3Hc->CapRegs = XhciBaseAddress; + Usb3Hc->OpRegs = (XHCI_HC_OP_REGS*)((UINTN)Usb3Hc->CapRegs + Usb3Hc->CapRegs->CapLength); + Usb3Hc->RtRegs = (XHCI_HC_RT_REGS*)((UINTN)Usb3Hc->CapRegs + Usb3Hc->CapRegs->RtsOff); + XhciExtCapParser(Usb3Hc); + } + + // Check if host controller interface version number is valid. + if (Usb3Hc->CapRegs->HciVersion == 0xFFFF) { + return USB_SUCCESS; + } + + // Check if USB Legacy Support Capability is present. + if (Usb3Hc->ExtLegCap) { + // Is ownership change? + if((Usb3Hc->ExtLegCap->LegCtlSts.UsbOwnershipChangeSmi == 1) && + (Usb3Hc->ExtLegCap->LegCtlSts.UsbOwnershipChangeSmiEnable==1) ) { + // Clear Ownership change SMI status + Usb3Hc->ExtLegCap->LegCtlSts.UsbOwnershipChangeSmi = 1; + // Process ownership change event + if (Usb3Hc->ExtLegCap->LegSup.HcOsOwned == 1 && + HcStruc->dHCFlag & HC_STATE_RUNNING) { + gUsbData->dUSBStateFlag &= (~USB_FLAG_ENABLE_BEEP_MESSAGE); + USB_DEBUG(3, "XHCI: Ownership change to XHCD\n"); + XHCI_Stop(HcStruc); + } else if (Usb3Hc->ExtLegCap->LegSup.HcOsOwned == 0 && + (HcStruc->dHCFlag & HC_STATE_RUNNING) == 0) { + gUsbData->dUSBStateFlag |= USB_FLAG_ENABLE_BEEP_MESSAGE; + USB_DEBUG(3, "XHCI: Ownership change to BIOS\n"); + XHCI_Start(HcStruc); + XHCI_EnumeratePorts(HcStruc); + } + return USB_SUCCESS; + } + } + + if ((HcStruc->dHCFlag & HC_STATE_RUNNING) == 0) return USB_SUCCESS; + + if ((UINT32)Usb3Hc->OpRegs->DcbAap != (UINT32)Usb3Hc->DcbaaPtr) return USB_SUCCESS; + + if (gUsbData->ProcessingPeriodicList == TRUE) { + for (i = 0; i < XHCI_MAX_PENDING_INTERRUPT_TRANSFER; i++) { + if (Usb3Hc->PendingInterruptTransfer[i].Trb != NULL) { + DevInfo = XHCI_GetDevInfo((UINTN)Usb3Hc->PendingInterruptTransfer[i].Trb->DataBuffer); + if (DevInfo == NULL) { + continue; + } + if ((DevInfo->bCallBackIndex) && + (DevInfo->bCallBackIndex <= MAX_CALLBACK_FUNCTION) && + (DevInfo->fpPollTDPtr != NULL)) { + DeviceContextSize = (XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize) * Usb3Hc->CapRegs->HcsParams1.MaxSlots; + + if ((DevInfo->DevMiscInfo < Usb3Hc->DeviceContext) || + (DevInfo->DevMiscInfo > (VOID*)((UINTN)(Usb3Hc->DeviceContext) + DeviceContextSize))) { + continue; + } + SlotId = XHCI_GetSlotId(Usb3Hc, DevInfo); + Dci = (DevInfo->IntInEndpoint & 0xF) * 2; + + if (DevInfo->IntInEndpoint & BIT7) { + Dci++; + } + + Doorbell = XHCI_GetTheDoorbell(Usb3Hc, SlotId); + + XfrRing = XHCI_GetXfrRing(Usb3Hc, SlotId, Dci - 1); + + Usb3Hc->PendingInterruptTransfer[i].Trb = NULL; + + if ((DevInfo->bCallBackIndex) && (DevInfo->fpPollTDPtr != NULL)) { + (*gUsbData->aCallBackFunctionTable[DevInfo->bCallBackIndex - 1]) + (HcStruc, DevInfo, NULL, DevInfo->fpPollTDPtr, + Usb3Hc->PendingInterruptTransfer[i].TransferredLength); + + XfrTrb = (XHCI_NORMAL_XFR_TRB*)XHCI_AdvanceEnqueuePtr(XfrRing); + if (XfrTrb == NULL) { + continue; + } + XfrTrb->TrbType = XhciTNormal; + XfrTrb->DataBuffer = (UINT64)(UINTN)DevInfo->fpPollTDPtr; + XfrTrb->XferLength = DevInfo->PollingLength; + XfrTrb->Isp = 1; + XfrTrb->Ioc = 1; + XfrTrb->CycleBit = XfrRing->CycleBit; + // Ring the door bell to start polling interrupt endpoint + *Doorbell = Dci; + } + } else { + Usb3Hc->PendingInterruptTransfer[i].Trb = NULL; + } + } + } + } + + if (Usb3Hc->OpRegs->UsbSts.Field.Pcd) { + //XHCI_EnumeratePorts(HcStruc); + XHCI_ProcessPortChanges(HcStruc, Usb3Hc); + } + +// if (Usb3Hc->OpRegs->UsbSts.Field.Eint == 0) return USB_SUCCESS; +// Usb3Hc->OpRegs->UsbSts.AllBits = XHCI_STS_EVT_INTERRUPT; // Clear event interrupt + if (Usb3Hc->OpRegs->UsbSts.Field.Eint) { + Usb3Hc->OpRegs->UsbSts.AllBits = XHCI_STS_EVT_INTERRUPT; + if ((gUsbData->fpKeyRepeatHCStruc == HcStruc) && (gUsbData->ProcessingPeriodicList == TRUE)) { + Imod = Usb3Hc->RtRegs->IntRegs[0].IMod; + if ((Imod & XHCI_KEYREPEAT_IMODI) == XHCI_KEYREPEAT_IMODI) { + USBKBDPeriodicInterruptHandler(HcStruc); + } + } + } + + // Check for pending interrupts: + // check the USBSTS[3] and IMAN [0] to determine if any interrupt generated + if (Usb3Hc->EvtRing.QueuePtr->CycleBit != Usb3Hc->EvtRing.CycleBit) { + if (gUsbData->fpKeyRepeatHCStruc == HcStruc) { + Imod = Usb3Hc->RtRegs->IntRegs[0].IMod; + if ((Imod & XHCI_KEYREPEAT_IMODI) == XHCI_KEYREPEAT_IMODI) { + Usb3Hc->RtRegs->IntRegs[0].IMan |= BIT0; + XHCI_Mmio64Write(HcStruc, Usb3Hc, + (UINTN)&Usb3Hc->RtRegs->IntRegs->Erdp, (UINT64)(UINTN)0 | BIT3); + } else { + Usb3Hc->RtRegs->IntRegs[0].IMan |= BIT0; + XHCI_Mmio64Write(HcStruc, Usb3Hc, + (UINTN)&Usb3Hc->RtRegs->IntRegs->Erdp, (UINT64)(UINTN)Usb3Hc->EvtRing.QueuePtr | BIT3); + } + } else { + Usb3Hc->RtRegs->IntRegs[0].IMan |= BIT0; + XHCI_Mmio64Write(HcStruc, Usb3Hc, + (UINTN)&Usb3Hc->RtRegs->IntRegs->Erdp, (UINT64)(UINTN)Usb3Hc->EvtRing.QueuePtr | BIT3); + } + return USB_SUCCESS; + } + + // See if there are any TRBs waiting in the event ring + //for (Count = 0; Count < Usb3Hc->EvtRing.Size; Count++) { + for (;;) { + Trb = Usb3Hc->EvtRing.QueuePtr; +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMemoryBuffer((VOID*)Trb, sizeof(XHCI_TRB)); + if (EFI_ERROR(EfiStatus)) { + break; + } +#endif + if (Trb->CycleBit != Usb3Hc->EvtRing.CycleBit) break; // past the last + + if (Usb3Hc->EvtRing.QueuePtr == Usb3Hc->EvtRing.LastTrb) { + // Reached the end of the ring, wrap around + Usb3Hc->EvtRing.QueuePtr = Usb3Hc->EvtRing.Base; + Usb3Hc->EvtRing.CycleBit ^= 1; + } else { + Usb3Hc->EvtRing.QueuePtr++; + } + // error manager + if (Trb->CompletionCode == XHCI_TRB_SHORTPACKET) { + USB_DEBUG(3, "XHCI: short packet detected."); + } + + if (Trb->CompletionCode == XHCI_TRB_STALL_ERROR) { + USB_DEBUG(3, "XHCI: device STALLs."); + } + + if (Trb->CompletionCode != XHCI_TRB_SUCCESS + && Trb->CompletionCode != XHCI_TRB_STALL_ERROR + && Trb->CompletionCode != XHCI_TRB_SHORTPACKET) { + USB_DEBUG(3, "Trb completion code: %d\n", Trb->CompletionCode); + //ASSERT(FALSE); + } + + switch (Trb->TrbType) { + case XhciTTransferEvt: +// very frequent, debug message here might affect timings, +// uncomment only when needed +// USB_DEBUG(3, "TransferEvt\n"); + XHCI_ProcessXferEvt(Usb3Hc, HcStruc, (XHCI_TRANSFER_EVT_TRB*)Trb); + break; + case XhciTCmdCompleteEvt: + USB_DEBUG(3, "CmdCompleteEvt\n"); + break; + case XhciTPortStatusChgEvt: + USB_DEBUG(3, "PortStatusChgEvt, port #%d\n", ((XHCI_PORTSTSCHG_EVT_TRB*)Trb)->PortId); + XHCI_ProcessPortStsChgEvt(Usb3Hc, HcStruc, (XHCI_PORTSTSCHG_EVT_TRB*)Trb); //(EIP60460+) + break; + case XhciTDoorbellEvt: + USB_DEBUG(3, "DoorbellEvt\n"); + break; + case XhciTHostControllerEvt: + USB_DEBUG(3, "HostControllerEvt\n"); + break; + case XhciTDevNotificationEvt: + USB_DEBUG(3, "DevNotificationEvt\n"); + break; + case XhciTMfIndexWrapEvt: + USB_DEBUG(3, "MfIndexWrapEvt\n"); + break; + default: + USB_DEBUG(3, "UNKNOWN EVENT\n"); + } + } + //ASSERT(Count<Usb3Hc->EvtRing.Size); // Event ring is full + + // Update ERDP to inform xHC that we have processed another TRB + if ((gUsbData->fpKeyRepeatHCStruc == HcStruc) && + ((Usb3Hc->RtRegs->IntRegs[0].IMod & XHCI_KEYREPEAT_IMODI) == XHCI_KEYREPEAT_IMODI)) { + Usb3Hc->RtRegs->IntRegs[0].IMan |= BIT0; + XHCI_Mmio64Write(HcStruc, Usb3Hc, + (UINTN)&Usb3Hc->RtRegs->IntRegs->Erdp, (UINT64)(UINTN)0 | BIT3); + } else { + Usb3Hc->RtRegs->IntRegs[0].IMan |= BIT0; + XHCI_Mmio64Write(HcStruc, Usb3Hc, + (UINTN)&Usb3Hc->RtRegs->IntRegs->Erdp, (UINT64)(UINTN)Usb3Hc->EvtRing.QueuePtr | BIT3); + } + + return USB_SUCCESS; // Set as interrupt processed +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_GetRootHubStatus +// +// Description: +// This function returns the port connect status for the root hub port +// +// Input: +// HcStruc - Pointer to the HC structure +// PortNum - Port in the HC whose status is requested +// +// Output: +// Port status flags (see USB_PORT_STAT_XX equates) +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_GetRootHubStatus( + HC_STRUC* HcStruc, + UINT8 PortNum, + BOOLEAN ClearChangeBits +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + volatile XHCI_PORTSC *PortSC; + UINT32 i; + UINT32 PortStCtl; + UINT8 PortStatus = USB_PORT_STAT_DEV_OWNER; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + // Find the proper MMIO access offset for a given port + + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (PortNum-1))); + +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMmioBuffer((VOID*)PortSC, sizeof(XHCI_PORTSC)); + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } +#endif + + USB_DEBUG(3, "XHCI port[%d] status: %08x\n", PortNum, PortSC->AllBits); + + for (i = 0; i < 200; i++) { + if (PortSC->Field.Pr == 0) break; + FixedDelay(1 * 1000); + } + + switch (PortSC->Field.Pls) { + case XHCI_PORT_LINK_U0: + case XHCI_PORT_LINK_RXDETECT: + break; + case XHCI_PORT_LINK_RECOVERY: + for (i = 0; i < 200; i++) { + FixedDelay(1 * 1000); + if (PortSC->Field.Pls != XHCI_PORT_LINK_RECOVERY) { + break; + } + } + break; + case XHCI_PORT_LINK_POLLING: + if (!XHCI_IsUsb3Port(Usb3Hc, PortNum)) { + break; + } + for (i = 0; i < 500; i++) { + FixedDelay(1 * 1000); + if (PortSC->Field.Pls != XHCI_PORT_LINK_POLLING) { + break; + } + } + if (PortSC->Field.Pls == XHCI_PORT_LINK_U0 || + PortSC->Field.Pls == XHCI_PORT_LINK_RXDETECT) { + break; + } + XHCI_ResetPort(Usb3Hc, PortNum, TRUE); + break; + case XHCI_PORT_LINK_INACTIVE: + for (i = 0; i < 12; i++) { + FixedDelay(1 * 1000); + if (PortSC->Field.Pls != XHCI_PORT_LINK_INACTIVE) { + break; + } + } + if (PortSC->Field.Pls == XHCI_PORT_LINK_RXDETECT) { + break; + } + case XHCI_PORT_LINK_COMPLIANCE_MODE: + XHCI_ResetPort(Usb3Hc, PortNum, TRUE); + break; + default: + PortStatus |= USB_PORT_STAT_DEV_CONNECTED; + break; + } + + if (PortSC->Field.Ccs != 0) { + PortStatus |= USB_PORT_STAT_DEV_CONNECTED; + UpdatePortStatusSpeed(PortSC->Field.PortSpeed, &PortStatus); + + // USB 3.0 device may not set Connect Status Change bit after reboot, + // set the connect change flag when we enumerate HC ports for devices. + if (gUsbData->bIgnoreConnectStsChng == TRUE) { + PortStatus |= USB_PORT_STAT_DEV_CONNECT_CHANGED; + } + + if (PortSC->Field.Ped) { + PortStatus |= USB_PORT_STAT_DEV_ENABLED; + } + } + + if (PortSC->Field.Csc != 0) { + PortStatus |= USB_PORT_STAT_DEV_CONNECT_CHANGED; + // Clear connect status change bit + if (ClearChangeBits == TRUE) { + PortSC->AllBits = XHCI_PCS_CSC | XHCI_PCS_PP; + } + } + + // Clear all status change bits + if (ClearChangeBits == TRUE) { + PortStCtl = PortSC->AllBits; + PortSC->AllBits = PortStCtl & ~XHCI_PCS_PED; // DO NOT TOUCH PED + } + + return PortStatus; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_EnableRootHub +// +// Description: +// This function enables the XHCI HC Root hub port. +// +// Input: +// HcStruc - Pointer to the HC structure +// PortNum - Port in the HC to enable +// +// Output: +// USB_SUCCESS on success, USB_ERROR on error +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_EnableRootHub( + HC_STRUC* HcStruc, + UINT8 PortNum +) +{ + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_DisableRootHub +// +// Description: +// This function disables the XHCI HC Root hub port. +// +// Input: +// HcStruc - Pointer to the HC structure +// PortNum - Port in the HC to disable +// +// Output: +// USB_SUCCESS on success, USB_ERROR on error +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_DisableRootHub( + HC_STRUC *HcStruc, + UINT8 PortNum +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + volatile XHCI_PORTSC *PortSC = NULL; + UINT8 i = 0; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + +USB_DEBUG(3, "Disable XHCI root port %d\n", PortNum); + + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (PortNum - 1))); + +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMmioBuffer((VOID*)PortSC, sizeof(XHCI_PORTSC)); + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } +#endif + + if (PortSC->Field.Ped) { + if (XHCI_IsUsb3Port(Usb3Hc, PortNum)) { + XHCI_ResetPort(Usb3Hc, PortNum, FALSE); + } else { + PortSC->AllBits = XHCI_PCS_PED | XHCI_PCS_PP; + for (i = 0; i < 200; i++) { + if (PortSC->Field.Ped == 0) { + break; + } + FixedDelay(100); + } + } + } + + return USB_SUCCESS; +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_ResetPort +// +// Description: +// This function resets the XHCI HC Root hub port. +// +// Input: +// HcStruc - Pointer to the HC structure +// PortNum - Port in the HC to disable +// +// Output: +// USB_SUCCESS on success, USB_ERROR on error +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_ResetPort( + USB3_HOST_CONTROLLER *Usb3Hc, + UINT8 Port, + BOOLEAN WarmReset +) +{ + volatile XHCI_PORTSC *PortSC; + UINT32 i; + EFI_STATUS EfiStatus = EFI_SUCCESS; + + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (Port - 1))); + +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMmioBuffer((VOID*)PortSC, sizeof(XHCI_PORTSC)); + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } +#endif + + if (WarmReset && XHCI_IsUsb3Port(Usb3Hc, Port)) { + PortSC->AllBits = XHCI_PCS_WPR | XHCI_PCS_PP; + + for (i = 0; i < 6000; i++) { //(EIP93368) + if (PortSC->Field.Wrc || PortSC->Field.Prc) { + break; + } + FixedDelay(100); + } + //ASSERT(PortSC->Field.Wrc || PortSC->Field.Prc); + if (PortSC->Field.Wrc == 0 && PortSC->Field.Prc == 0) { + return USB_ERROR; + } + + if (Usb3Hc->Vid == XHCI_EJ168A_VID && Usb3Hc->Did == XHCI_EJ168A_DID) { + FixedDelay(20 * 1000); + } + } else { + PortSC->AllBits = XHCI_PCS_PR | XHCI_PCS_PP; // Keep port power bit + + for (i = 0; i < 3000; i++) { + if (PortSC->Field.Prc) break; + FixedDelay(100); + } + //ASSERT(PortSC->Field.Prc); + if (PortSC->Field.Prc == 0) { + return USB_ERROR; + } + } + + // Clear Warm Port Reset Change and Port Reset Change bits + PortSC->AllBits = XHCI_PCS_WRC | XHCI_PCS_PRC | XHCI_PCS_PP; + // The USB System Software guarantees a minimum of 10 ms for reset recovery. + FixedDelay(XHCI_RESET_PORT_DELAY_MS * 1000); + + return USB_SUCCESS; +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_ResetRootHub +// +// Description: +// This function resets the XHCI HC Root hub port. +// +// Input: +// HcStruc - Pointer to the HC structure +// PortNum - Port in the HC to disable +// +// Output: +// USB_SUCCESS on success, USB_ERROR on error +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_ResetRootHub( + HC_STRUC* HcStruc, + UINT8 PortNum +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + UINT8 Status; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + Status = XHCI_ResetPort(Usb3Hc, PortNum, FALSE); + + if (!XHCI_IsUsb3Port(Usb3Hc, PortNum)) { + // After a short delay, SS device that was originally connected to HS port + // might get reconnected to the SS port... + FixedDelay(XHCI_SWITCH2SS_DELAY_MS * 1000); + } + + return Status; +} + + //(EIP54018+)> +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Name: XHCI_GlobalSuspend +// +// Description: +// This function suspend the XHCI HC. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_GlobalSuspend( + HC_STRUC* HcStruc +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + volatile XHCI_PORTSC *PortSC; + UINT32 Port; + UINT32 i; + UINT32 PortStCtl; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return USB_ERROR; + } + + for (Port = 1; Port <= Usb3Hc->MaxPorts; Port++) { + + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (Port - 1))); + +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMmioBuffer((VOID*)PortSC, sizeof(XHCI_PORTSC)); + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } +#endif + + USB_DEBUG(3, "XHCI port[%d] status: %08x\n", Port, PortSC->AllBits); + if ((PortSC->Field.Ped) && (PortSC->Field.Pls <XHCI_PORT_LINK_U3)){ + PortStCtl = PortSC->AllBits; + PortStCtl |= (XHCI_PCS_LWS | (UINT32)(XHCI_PORT_LINK_U3 << 5)); + PortSC->AllBits = PortStCtl & ~XHCI_PCS_PED; + for (i = 0;i < 10; i++) { + if (PortSC->Field.Pls == XHCI_PORT_LINK_U3) break; + FixedDelay(1 * 1000); + } + } + } + + + Usb3Hc->OpRegs->UsbCmd.AllBits &= ~XHCI_CMD_RS; + + for (i = 0; i < 16; i++) { + FixedDelay(1 * 1000); + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) break; + } + + HcStruc->dHCFlag &= ~(HC_STATE_RUNNING); + HcStruc->dHCFlag |= HC_STATE_SUSPEND; + + return USB_SUCCESS; +} + //<(EIP54018+) + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_UpdateEp0MaxPacket +// +// Description: +// This function verifies the MaxPacket size of the control pipe. If it does +// not match the one received as a part of GET_DESCRIPTOR, then this function +// updates the MaxPacket data in DeviceContext and HC is notified via +// EvaluateContext command. +// +// Input: +// HcStruc Pointer to the HC structure +// Device Evaluated device context pointer +// SlotId Device context index in DCBAA +// Endp0MaxPacket Max packet size obtained from the device +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +XHCI_UpdateEp0MaxPacket( + HC_STRUC *HcStruc, + UINT8 SlotId, + UINT8 Endp0MaxPacket +) +{ + USB3_HOST_CONTROLLER *Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + UINT8 Status; + UINT8 *DevCtx; + XHCI_INPUT_CONTROL_CONTEXT *CtlCtx; + XHCI_SLOT_CONTEXT *SlotCtx; + XHCI_EP_CONTEXT *EpCtx; + + DevCtx = (UINT8*)XHCI_GetDeviceContext(Usb3Hc, SlotId); + + SlotCtx = (XHCI_SLOT_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, DevCtx, 0); + if (SlotCtx->Speed != XHCI_DEVSPEED_FULL) return; + + EpCtx = (XHCI_EP_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, DevCtx, 1); + if (EpCtx->MaxPacketSize == Endp0MaxPacket) return; + + // Prepare input context for EvaluateContext comand + MemFill((UINT8*)Usb3Hc->InputContext, XHCI_INPUT_CONTEXT_ENTRIES * Usb3Hc->ContextSize, 0); + + CtlCtx = (XHCI_INPUT_CONTROL_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, 0); + CtlCtx->AddContextFlags = BIT1; + + EpCtx = (XHCI_EP_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, 2); + EpCtx->MaxPacketSize = Endp0MaxPacket; + + Status = XHCI_ExecuteCommand(HcStruc, XhciTEvaluateContextCmd, &SlotId); + ASSERT(Status == USB_SUCCESS); +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_ControlTransfer +// +// Description: +// This function executes a device request command transaction on the USB. +// One setup packet is generated containing the device request parameters +// supplied by the caller. The setup packet may be followed by data in or +// data out packets containing data sent from the host to the device or +// vice-versa. This function will not return until the request either completes +// successfully or completes in error (due to time out, etc.) +// +// Input: +// HcStruc Pointer to the HC structure +// DevInfo DeviceInfo structure (if available else 0) +// Request Request type (low byte) +// Bit 7 : Data direction +// 0 = Host sending data to device +// 1 = Device sending data to host +// Bit 6-5 : Type +// 00 = Standard USB request +// 01 = Class specific +// 10 = Vendor specific +// 11 = Reserved +// Bit 4-0 : Recipient +// 00000 = Device +// 00001 = Interface +// 00010 = Endpoint +// 00100 - 11111 = Reserved +// Request code, a one byte code describing the actual +// device request to be executed (ex: Get Configuration, +// Set Address, etc.) +// Index wIndex request parameter (meaning varies) +// Value wValue request parameter (meaning varies) +// Buffer Buffer containing data to be sent to the device or buffer +// to be used to receive data +// Length wLength request parameter, number of bytes of data to be +// transferred in or out of the host controller +// +// Output: +// Number of bytes actually transferred +// +// Notes: +// DevInfo->DevMiscInfo points to the device context +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT16 +XHCI_ControlTransfer ( + HC_STRUC *HcStruc, + DEV_INFO *DevInfo, + UINT16 Request, + UINT16 Index, + UINT16 Value, + UINT8 *Buffer, + UINT16 Length +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + XHCI_TRB *Trb; + UINT8 SlotId; + UINT8 CompletionCode; + UINT8 Status; + TRB_RING *XfrRing; + UINT16 TimeoutMs; + XHCI_SLOT_CONTEXT *SlotCtx = NULL; + EFI_STATUS EfiStatus = EFI_SUCCESS; + UINTN DeviceContextSize; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + + EfiStatus = UsbDevInfoValidation(DevInfo); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + +#if USB_RUNTIME_DRIVER_IN_SMM + if (gCheckUsbApiParameter) { + if (Length != 0) { + EfiStatus = AmiValidateMemoryBuffer((VOID*)Buffer, Length); + if (EFI_ERROR(EfiStatus)) { + USB_DEBUG(3, "Xhci ControlTransfer Invalid Pointer, Buffer is in SMRAM.\n"); + return 0; + } + } + gCheckUsbApiParameter = FALSE; + } +#endif + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return 0; + } + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return 0; + } + + ASSERT(DevInfo != NULL); + + if(DevInfo->Flag & DEV_INFO_DEV_DISCONNECTING) return 0; //(EIP60460+) + + DeviceContextSize = (XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize) * Usb3Hc->CapRegs->HcsParams1.MaxSlots; + + if ((DevInfo->DevMiscInfo < Usb3Hc->DeviceContext) || + (DevInfo->DevMiscInfo > (VOID*)((UINTN)(Usb3Hc->DeviceContext) + DeviceContextSize))) { + return 0; + } + + SlotId = XHCI_GetSlotId(Usb3Hc, DevInfo); + + // Skip SET_ADDRESS request if device is in addressed state + if (Request == USB_RQ_SET_ADDRESS) { + SlotCtx = XHCI_GetContextEntry(Usb3Hc, DevInfo->DevMiscInfo, 0); + + if (SlotCtx->SlotState == XHCI_SLOT_STATE_DEFAULT) { + Status = XhciAddressDevice(HcStruc, DevInfo, SlotId); + } + return Length; + } + + TimeoutMs = gUsbData->wTimeOutValue != 0 ? XHCI_CTL_COMPLETE_TIMEOUT_MS : 0; + + gUsbData->bLastCommandStatus &= ~(USB_CONTROL_STALLED); + gUsbData->dLastCommandStatusExtended = 0; + + // Insert Setup, Data(if needed), and Status TRBs into the transfer ring + XfrRing = XHCI_GetXfrRing(Usb3Hc, SlotId, 0); + + // Setup TRB + Trb = XHCI_AdvanceEnqueuePtr(XfrRing); + if (Trb == NULL) { + return 0; + } + Trb->TrbType = XhciTSetupStage; + ((XHCI_SETUP_XFR_TRB*)Trb)->Idt = 1; + *(UINT16*)&((XHCI_SETUP_XFR_TRB*)Trb)->bmRequestType = Request; + ((XHCI_SETUP_XFR_TRB*)Trb)->wValue = Value; + ((XHCI_SETUP_XFR_TRB*)Trb)->wIndex = Index; + ((XHCI_SETUP_XFR_TRB*)Trb)->wLength = Length; + ((XHCI_SETUP_XFR_TRB*)Trb)->XferLength = 8; + + if (Usb3Hc->HciVersion >= 0x100) { + if (Length != 0) { + if (Request & USB_REQ_TYPE_INPUT) { + ((XHCI_SETUP_XFR_TRB*)Trb)->Trt = XHCI_XFER_TYPE_DATA_IN; + } else { + ((XHCI_SETUP_XFR_TRB*)Trb)->Trt = XHCI_XFER_TYPE_DATA_OUT; + } + } else { + ((XHCI_SETUP_XFR_TRB*)Trb)->Trt = XHCI_XFER_TYPE_NO_DATA; + } + } + ((XHCI_SETUP_XFR_TRB*)Trb)->CycleBit = XfrRing->CycleBit; + + // Data TRB + if (Length != 0) { + Trb = XHCI_AdvanceEnqueuePtr(XfrRing); + if (Trb == NULL) { + return 0; + } + Trb->TrbType = XhciTDataStage; + ((XHCI_DATA_XFR_TRB*)Trb)->Dir = ((Request & USB_REQ_TYPE_INPUT) != 0)? 1 : 0; + ((XHCI_DATA_XFR_TRB*)Trb)->XferLength = Length; + ((XHCI_DATA_XFR_TRB*)Trb)->DataBuffer = (UINT64)(UINTN)Buffer; + ((XHCI_DATA_XFR_TRB*)Trb)->CycleBit = XfrRing->CycleBit; + } + + // Status TRB + Trb = XHCI_AdvanceEnqueuePtr(XfrRing); + if (Trb == NULL) { + return 0; + } + Trb->TrbType = XhciTStatusStage; + ((XHCI_STATUS_XFR_TRB*)Trb)->Ioc = 1; + if ((Request & USB_REQ_TYPE_INPUT) == 0) { + ((XHCI_STATUS_XFR_TRB*)Trb)->Dir = 1; // Status is IN + } + ((XHCI_STATUS_XFR_TRB*)Trb)->CycleBit = XfrRing->CycleBit; + + // Ring the doorbell and see Event Ring update + Status = XhciRingDoorbell(Usb3Hc, SlotId, 1); + + if (Status != USB_SUCCESS) { + return 0; + } + + Status = XHCI_WaitForEvent( + HcStruc, Trb, XhciTTransferEvt, SlotId, 1, //(EIP62376) + &CompletionCode, TimeoutMs, NULL); + + if (Status != USB_SUCCESS) { + //(EIP54283)> + switch (CompletionCode) { + case XHCI_TRB_BABBLE_ERROR: //(EIP62376+) + case XHCI_TRB_TRANSACTION_ERROR: + XHCI_ClearStalledEp(Usb3Hc, HcStruc, SlotId, 1); //(EIP60460+) + break; //(EIP60460+) + case XHCI_TRB_STALL_ERROR: + XHCI_ClearStalledEp(Usb3Hc, HcStruc, SlotId, 1); + gUsbData->bLastCommandStatus |= USB_CONTROL_STALLED; + break; + //(EIP84790+)> + case XHCI_TRB_EXECUTION_TIMEOUT_ERROR: + XHCI_ClearEndpointState(HcStruc, DevInfo, 0); + gUsbData->dLastCommandStatusExtended |= USB_TRNSFR_TIMEOUT; + break; + //<(EIP84790+) + default: + break; + } + //<(EIP54283) + return 0; + } + + if (Request == USB_RQ_GET_DESCRIPTOR && Length == 8) { + // Full speed device requires the update of MaxPacket size + XHCI_UpdateEp0MaxPacket(HcStruc, SlotId, ((DEV_DESC*)Buffer)->MaxPacketSize0); + } + + + if ((Request == (UINT16)(ENDPOINT_CLEAR_PORT_FEATURE)) && (Length == 0) && + (Value == (UINT16)ENDPOINT_HALT) && (Buffer == NULL)) { + XHCI_ClearEndpointState(HcStruc, DevInfo, (UINT8)Index); + } + + return Length; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_BulkTransfer +// +// Description: +// This function executes a bulk transaction on the USB +// +// Input: +// HcStruc Pointer to HCStruc of the host controller +// DevInfo DeviceInfo structure (if available else 0) +// XferDir Transfer direction +// Bit 7: Data direction +// 0 Host sending data to device +// 1 Device sending data to host +// Bit 6-0 : Reserved +// Buffer Buffer containing data to be sent to the device or buffer to +// be used to receive data value +// Length Length request parameter, number of bytes of data to be +// transferred in or out of the HC +// +// Output: +// Amount of data transferred +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT32 +XHCI_BulkTransfer( + HC_STRUC *HcStruc, + DEV_INFO *DevInfo, + UINT8 XferDir, + UINT8 *Buffer, + UINT32 Length +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + XHCI_TRB *Trb; + XHCI_TRB *FirstTrb; + UINT8 SlotId; + UINT8 CompletionCode; + UINT8 Status; + TRB_RING *XfrRing; + UINT8 Endpoint; + UINT8 Dci; + UINT64 DataPointer; + UINT32 ResidualData; // Transferred amount return by Transfer Event + UINT32 TransferredSize; // Total transfer amount + UINT32 RingDataSize; // One TRB ring transfer amount + UINT32 RemainingXfrSize; + UINT32 RemainingDataSize; + UINT32 XfrSize; + UINT32 XfrTdSize; + UINT16 MaxPktSize; + UINT32 TdSize; + UINT16 TimeoutMs; + EFI_STATUS EfiStatus = EFI_SUCCESS; + UINTN DeviceContextSize; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + + EfiStatus = UsbDevInfoValidation(DevInfo); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (gCheckUsbApiParameter) { + EfiStatus = AmiValidateMemoryBuffer((VOID*)Buffer, Length); + if (EFI_ERROR(EfiStatus)) { + USB_DEBUG(3, "Xhci BulkTransfer Invalid Pointer, Buffer is in SMRAM.\n"); + return 0; + } + gCheckUsbApiParameter = FALSE; + } +#endif + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return 0; + } + + // Clear HW source of error + gUsbData->bLastCommandStatus &= ~(USB_BULK_STALLED | USB_BULK_TIMEDOUT ); + gUsbData->dLastCommandStatusExtended = 0; + + if(DevInfo->Flag & DEV_INFO_DEV_DISCONNECTING) return 0; //(EIP60460+) + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + DeviceContextSize = (XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize) * Usb3Hc->CapRegs->HcsParams1.MaxSlots; + + if ((DevInfo->DevMiscInfo < Usb3Hc->DeviceContext) || + (DevInfo->DevMiscInfo > (VOID*)((UINTN)(Usb3Hc->DeviceContext) + DeviceContextSize))) { + return 0; + }; + + TimeoutMs = gUsbData->wTimeOutValue; + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return 0; + } + + SlotId = XHCI_GetSlotId(Usb3Hc, DevInfo); + Endpoint = (XferDir & BIT7)? DevInfo->bBulkInEndpoint : DevInfo->bBulkOutEndpoint; + MaxPktSize = (XferDir & BIT7)? DevInfo->wBulkInMaxPkt : DevInfo->wBulkOutMaxPkt; + Dci = (Endpoint & 0xf)* 2; + if (XferDir & BIT7) Dci++; + + XfrRing = XHCI_GetXfrRing(Usb3Hc, SlotId, Dci-1); + + // Make a chain of TDs to transfer the requested amount of data. If necessary, + // make multiple transfers in a loop. + + DataPointer = (UINT64)(UINTN)Buffer; + RemainingDataSize = Length; + + // Two loops are executing the transfer: + // The inner loop creates a transfer ring of chained TDs, XHCI_BOT_TD_MAXSIZE + // bytes each. This makes a ring capable of transferring + // XHCI_BOT_TD_MAXSIZE * (TRBS_PER_SEGMENT-1) bytes. + // The outter loop repeats the transfer if the requested transfer size exceeds + // XHCI_BOT_TD_MAXSIZE * (TRBS_PER_SEGMENT-1). + + for (TransferredSize = 0; TransferredSize < Length;) { + // Calculate the amount of data to transfer in the ring + RingDataSize = (RemainingDataSize > XHCI_BOT_MAX_XFR_SIZE)? + XHCI_BOT_MAX_XFR_SIZE : RemainingDataSize; + + RemainingXfrSize = RingDataSize; + + for (Trb = NULL, XfrSize = 0, FirstTrb = 0; XfrSize < RingDataSize;) + { + Trb = XHCI_AdvanceEnqueuePtr(XfrRing); + if (Trb == NULL) { + return 0; + } + if (FirstTrb == NULL) FirstTrb = Trb; + + Trb->TrbType = XhciTNormal; + ((XHCI_NORMAL_XFR_TRB*)Trb)->Isp = 1; + ((XHCI_NORMAL_XFR_TRB*)Trb)->DataBuffer = DataPointer; + + // See if we need a TD chain. Note that we do not need to + // place the chained TRB into Event Ring, since we will not be + // looking for it anyway. Set IOC only for the last-in-chain TRB. + if (RemainingXfrSize > XHCI_BOT_TD_MAXSIZE) { + XfrTdSize = XHCI_BOT_TD_MAXSIZE; + ((XHCI_NORMAL_XFR_TRB*)Trb)->Chain = 1; + } else { + ((XHCI_NORMAL_XFR_TRB*)Trb)->Ioc = 1; + XfrTdSize = RemainingXfrSize; + } + // Data buffers referenced by Transfer TRBs shall not span 64KB boundaries. + // If a physical data buffer spans a 64KB boundary, software shall chain + // multiple TRBs to describe the buffer. + if (XfrTdSize > (UINT32)(0x10000 - (DataPointer & (0x10000 - 1)))) { + XfrTdSize = (UINT32)(0x10000 - (DataPointer & (0x10000 - 1))); + ((XHCI_NORMAL_XFR_TRB*)Trb)->Chain = 1; + ((XHCI_NORMAL_XFR_TRB*)Trb)->Ioc = 0; + } + + ((XHCI_NORMAL_XFR_TRB*)Trb)->XferLength = XfrTdSize; + + XfrSize += XfrTdSize; + DataPointer += XfrTdSize; + RemainingXfrSize -= XfrTdSize; + + if(Usb3Hc->HciVersion >= 0x100) { + TdSize = 0; + if (RemainingXfrSize != 0) { + TdSize = RemainingXfrSize/MaxPktSize; + if (RemainingXfrSize % MaxPktSize) { + TdSize++; + } + TdSize = (TdSize > 31)? 31 : TdSize; + } + } else { + TdSize = RemainingXfrSize + XfrTdSize; + TdSize = (TdSize < 32768)? (TdSize >> 10) : 31; + } + + ((XHCI_NORMAL_XFR_TRB*)Trb)->TdSize = TdSize; + if (Trb != FirstTrb) { + ((XHCI_NORMAL_XFR_TRB*)Trb)->CycleBit = XfrRing->CycleBit; + } + } +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMemoryBuffer((VOID*)XfrRing->LastTrb, sizeof(XHCI_NORMAL_XFR_TRB)); + if (EFI_ERROR(EfiStatus)) { + break; + } +#endif + // If transfer ring crossed Link TRB, set its Chain flag + if (Trb < FirstTrb) { + ((XHCI_NORMAL_XFR_TRB*)XfrRing->LastTrb)->Chain = 1; + + } + + ((XHCI_NORMAL_XFR_TRB*)FirstTrb)->CycleBit = XfrRing->CycleBit; + if (Trb < FirstTrb) { + ((XHCI_NORMAL_XFR_TRB*)FirstTrb)->CycleBit ^= 1; + } + + // Ring the door bell and see Event Ring update + Status = XhciRingDoorbell(Usb3Hc, SlotId, Dci); + + if (Status != USB_SUCCESS) { + break; + } + + Status = XHCI_WaitForEvent( + HcStruc, Trb, XhciTTransferEvt, SlotId, Dci, //(EIP62376) + &CompletionCode, TimeoutMs, &ResidualData); + + // Clear Link TRB chain flag + ((XHCI_NORMAL_XFR_TRB*)XfrRing->LastTrb)->Chain = 0; + + if (Status != USB_SUCCESS) { + //(EIP54283)> + switch (CompletionCode) { + case XHCI_TRB_BABBLE_ERROR: //(EIP62376+) + case XHCI_TRB_TRANSACTION_ERROR: + XHCI_ClearStalledEp(Usb3Hc, HcStruc, SlotId, Dci); //(EIP60460+) + break; //(EIP60460+) + case XHCI_TRB_STALL_ERROR: + XHCI_ResetEndpoint(Usb3Hc, HcStruc, SlotId, Dci); + gUsbData->bLastCommandStatus |= USB_BULK_STALLED; + gUsbData->dLastCommandStatusExtended |= USB_TRSFR_STALLED; + break; + case XHCI_TRB_EXECUTION_TIMEOUT_ERROR: + XHCI_ClearEndpointState(HcStruc, DevInfo, Endpoint | XferDir); + gUsbData->bLastCommandStatus |= USB_BULK_TIMEDOUT; + gUsbData->dLastCommandStatusExtended |= USB_TRNSFR_TIMEOUT; //(EIP84790+) + break; + default: + break; + } + //<(EIP54283) + break; + } + + TransferredSize += (RingDataSize - ResidualData); + if (ResidualData != 0) break; // Short packet detected, no more transfers + RemainingDataSize -= RingDataSize; + } + + return TransferredSize; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_InterruptTransfer +// +// Description: +// This function executes an interrupt transaction on the USB. The data +// transfer direction is always DATA_IN. This function wil not return until +// the request either completes successfully or completes in error (due to +// time out, etc.) +// +// Input: +// HcStruc Pointer to HCStruc of the host controller +// DevInfo DeviceInfo structure (if available else 0) +// EndpointAddress The destination USB device endpoint to which the device request +// is being sent. +// MaxPktSize Indicates the maximum packet size the target endpoint is capable +// of sending or receiving. +// Buffer Buffer containing data to be sent to the device or buffer to be +// used to receive data +// Length Length request parameter, number of bytes of data to be transferred +// +// Output: +// Number of bytes transferred +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT16 +XHCI_InterruptTransfer ( + HC_STRUC *HcStruc, + DEV_INFO *DevInfo, + UINT8 EndpointAddress, + UINT16 MaxPktSize, + UINT8 *Buffer, + UINT16 Length +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + XHCI_TRB *Trb; + UINT8 SlotId; + UINT8 CompletionCode; + UINT8 Status; + TRB_RING *XfrRing; + UINT8 Dci; + UINT16 TimeoutMs; + UINT32 ResidualData; + EFI_STATUS EfiStatus = EFI_SUCCESS; + UINTN DeviceContextSize; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + + EfiStatus = UsbDevInfoValidation(DevInfo); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return 0; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (gCheckUsbApiParameter) { + EfiStatus = AmiValidateMemoryBuffer((VOID*)Buffer, Length); + if (EFI_ERROR(EfiStatus)) { + USB_DEBUG(3, "Xhci InterruptTransfer Invalid Pointer, Buffer is in SMRAM.\n"); + return 0; + } + gCheckUsbApiParameter = FALSE; + } +#endif + + gUsbData->dLastCommandStatusExtended = 0; + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + DeviceContextSize = (XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize) * Usb3Hc->CapRegs->HcsParams1.MaxSlots; + + if ((DevInfo->DevMiscInfo < Usb3Hc->DeviceContext) || + (DevInfo->DevMiscInfo > (VOID*)((UINTN)(Usb3Hc->DeviceContext) + DeviceContextSize))) { + return 0; + } + + TimeoutMs = gUsbData->wTimeOutValue; + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return 0; + } + + SlotId = XHCI_GetSlotId(Usb3Hc, DevInfo); + Dci = (EndpointAddress & 0xF) * 2; + if (EndpointAddress & BIT7) Dci++; + + XfrRing = XHCI_GetXfrRing(Usb3Hc, SlotId, Dci-1); + Trb = XHCI_AdvanceEnqueuePtr(XfrRing); + if (Trb == NULL) { + return 0; + } + Trb->TrbType = XhciTNormal; + ((XHCI_NORMAL_XFR_TRB*)Trb)->DataBuffer = (UINTN)Buffer; + ((XHCI_NORMAL_XFR_TRB*)Trb)->XferLength = Length; + ((XHCI_NORMAL_XFR_TRB*)Trb)->Isp = 1; + ((XHCI_NORMAL_XFR_TRB*)Trb)->Ioc = 1; + ((XHCI_NORMAL_XFR_TRB*)Trb)->CycleBit = XfrRing->CycleBit; + + // Ring the doorbell and see Event Ring update + Status = XhciRingDoorbell(Usb3Hc, SlotId, Dci); + + if (Status != USB_SUCCESS) { + return 0; + } + + Status = XHCI_WaitForEvent( + HcStruc, Trb, XhciTTransferEvt, SlotId, Dci, //(EIP62376) + &CompletionCode, TimeoutMs, &ResidualData); + + if (Status != USB_SUCCESS) { + //(EIP54283)> + switch (CompletionCode) { + //(EIP62376+)> + case XHCI_TRB_BABBLE_ERROR: + case XHCI_TRB_TRANSACTION_ERROR: + XHCI_ClearStalledEp(Usb3Hc, HcStruc, SlotId, Dci); + break; + //<(EIP62376+) + case XHCI_TRB_STALL_ERROR: + XHCI_ResetEndpoint(Usb3Hc, HcStruc, SlotId, Dci); + break; + case XHCI_TRB_EXECUTION_TIMEOUT_ERROR: + XHCI_ClearEndpointState(HcStruc, DevInfo, EndpointAddress); + gUsbData->dLastCommandStatusExtended |= USB_TRNSFR_TIMEOUT; + break; + default: + break; + } + //<(EIP54283) + return 0; + } else { + Length = Length - (UINT16)ResidualData; + } + + return Length; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_DeactivatePolling +// +// Description: +// This function de-activates the polling QH for the requested device. The +// device may be a USB keyboard or USB hub. +// +// Input: +// HcStruc - Pointer to the HC structure +// DevInfo - Pointer to the device information structure +// +// Output: +// USB_ERROR on error, USB_SUCCESS on success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_DeactivatePolling( + HC_STRUC* HcStruc, + DEV_INFO* DevInfo +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + UINT8 SlotId; + UINT8 Dci; + UINT16 EpInfo; + TRB_RING *XfrRing; + XHCI_SET_TRPTR_CMD_TRB Trb; + XHCI_EP_CONTEXT *EpCtx; + EFI_STATUS EfiStatus; + UINTN DeviceContextSize; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + EfiStatus = UsbDevInfoValidation(DevInfo); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return USB_ERROR; + } + + if (DevInfo->fpPollTDPtr == NULL) { + return USB_ERROR; + } + + USB_MemFree(DevInfo->fpPollTDPtr, GET_MEM_BLK_COUNT(DevInfo->PollingLength)); + + DevInfo->fpPollTDPtr = NULL; + + SlotId = XHCI_GetSlotId(Usb3Hc, DevInfo); + Dci = (DevInfo->IntInEndpoint & 0xF) * 2; + if (DevInfo->IntInEndpoint & BIT7) Dci++; + + EpInfo = (Dci << 8) + SlotId; + + DeviceContextSize = (XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize) * Usb3Hc->CapRegs->HcsParams1.MaxSlots; + + if ((DevInfo->DevMiscInfo < Usb3Hc->DeviceContext) || + (DevInfo->DevMiscInfo > (VOID*)((UINTN)(Usb3Hc->DeviceContext) + DeviceContextSize))) { + return USB_ERROR; + } + + EpCtx = (XHCI_EP_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, (UINT8*)DevInfo->DevMiscInfo, Dci); + + if (EpCtx->EpState == XHCI_EP_STATE_RUNNING) { + XHCI_ExecuteCommand(HcStruc, XhciTStopEndpointCmd, &EpInfo); + } + + // Set TR Dequeue Pointer command may be executed only if the target + // endpoint is in the Error or Stopped state. + if ((EpCtx->EpState == XHCI_EP_STATE_STOPPED) || + (EpCtx->EpState == XHCI_EP_STATE_ERROR)) { + + XfrRing = XHCI_GetXfrRing(Usb3Hc, SlotId, Dci-1); + + Trb.TrPointer = (UINT64)((UINTN)XfrRing->QueuePtr + XfrRing->CycleBit); // Set up DCS + Trb.EndpointId = Dci; + Trb.SlotId = SlotId; + XHCI_ExecuteCommand(HcStruc, XhciTSetTRDequeuePointerCmd, &Trb); + } + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_ActivatePolling +// +// Description: +// This function activates the polling QH for the requested device. The device +// may be a USB keyboard or USB hub. +// +// Input: +// HcStruc - Pointer to the HC structure +// DevInfo - Pointer to the device information structure +// +// Output: +// USB_ERROR on error, USB_SUCCESS on success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_ActivatePolling( + HC_STRUC* HcStruc, + DEV_INFO* DevInfo +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + XHCI_TRB *Trb; + volatile UINT32 *Doorbell; + UINT8 SlotId; + TRB_RING *XfrRing; + UINT8 Dci; + EFI_STATUS EfiStatus; + UINTN DeviceContextSize; + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + EfiStatus = UsbDevInfoValidation(DevInfo); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return USB_ERROR; + } + + DeviceContextSize = (XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize) * Usb3Hc->CapRegs->HcsParams1.MaxSlots; + + if ((DevInfo->DevMiscInfo < Usb3Hc->DeviceContext) || + (DevInfo->DevMiscInfo > (VOID*)((UINTN)(Usb3Hc->DeviceContext) + DeviceContextSize))) { + return USB_ERROR; + } + + SlotId = XHCI_GetSlotId(Usb3Hc, DevInfo); + Dci = (DevInfo->IntInEndpoint & 0xF) * 2; + if (DevInfo->IntInEndpoint & BIT7) Dci++; + DevInfo->fpPollTDPtr = USB_MemAlloc(GET_MEM_BLK_COUNT(DevInfo->PollingLength)); + XfrRing = XHCI_GetXfrRing(Usb3Hc, SlotId, Dci - 1); + + Trb = XHCI_AdvanceEnqueuePtr(XfrRing); + if (Trb == NULL) { + return USB_ERROR; + } + Trb->TrbType = XhciTNormal; + ((XHCI_NORMAL_XFR_TRB*)Trb)->DataBuffer = (UINT64)(UINTN)DevInfo->fpPollTDPtr; + ((XHCI_NORMAL_XFR_TRB*)Trb)->XferLength = DevInfo->PollingLength; + ((XHCI_NORMAL_XFR_TRB*)Trb)->Isp = 1; //(EIP51478+) + ((XHCI_NORMAL_XFR_TRB*)Trb)->Ioc = 1; + ((XHCI_NORMAL_XFR_TRB*)Trb)->CycleBit = XfrRing->CycleBit; + + // Ring the door bell + Doorbell = XHCI_GetTheDoorbell(Usb3Hc, SlotId); + *Doorbell = Dci; + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Name: XHCI_DisableKeyRepeat +// +// Description: +// This function disables the keyboard repeat rate logic +// +// Input: +// HcStruc - Pointer to the HC structure +// +// Output: +// USB_ERROR on error, USB_SUCCESS on success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_DisableKeyRepeat ( + HC_STRUC* HcStruc +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return USB_ERROR; + } + + XHCI_Mmio64Write(HcStruc, Usb3Hc, + (UINTN)&Usb3Hc->RtRegs->IntRegs->Erdp, (UINT64)(UINTN)Usb3Hc->EvtRing.QueuePtr | BIT3); + + Usb3Hc->RtRegs->IntRegs[0].IMod = XHCI_IMODI; + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_EnableKeyRepeat +// +// Description: +// This function disables the keyboard repeat rate logic +// +// Input: +// HcStruc - Pointer to the HC structure +// +// Output: +// USB_ERROR on error, USB_SUCCESS on success +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_EnableKeyRepeat( + HC_STRUC* HcStruc +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return USB_ERROR; + } + + XHCI_Mmio64Write(HcStruc, Usb3Hc, + (UINTN)&Usb3Hc->RtRegs->IntRegs->Erdp, (UINT64)(UINTN)0 | BIT3); + + Usb3Hc->RtRegs->IntRegs[0].IMod = (XHCI_KEYREPEAT_IMODC << 16 | XHCI_KEYREPEAT_IMODI); + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_InitXfrRing +// +// Description: +// This function initializes transfer ring of given endpoint +// +// Output: +// Pointer to the transfer ring +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +TRB_RING* +XHCI_InitXfrRing( + USB3_HOST_CONTROLLER* Usb3Hc, + UINT8 Slot, + UINT8 Ep +) +{ + TRB_RING *XfrRing = Usb3Hc->XfrRings + (Slot-1)*32 + Ep; + UINTN Base = Usb3Hc->XfrTrbs + ((Slot-1)*32+Ep)*RING_SIZE; + + XHCI_InitRing(XfrRing, Base, TRBS_PER_SEGMENT, TRUE); + + return XfrRing; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: Xhci_TranslateInterval +// +// Description: +// This routine calculates the Interval field to be used in device's endpoint +// context. Interval is calculated using the following rules (Section 6.2.3.6): +// +// For SuperSpeed bulk and control endpoints, the Interval field shall not be +// used by the xHC. For all other endpoint types and speeds, system software +// shall translate the bInterval field in the USB Endpoint Descriptor to the +// appropriate value for this field. +// +// For high-speed and SuperSpeed Interrupt and Isoch endpoints the bInterval +// field the Endpoint Descriptor is computed as 125æs * 2^(bInterval-1), where +// bInterval = 1 to 16, therefore Interval = bInterval - 1. +// +// For low-speed Interrupt and full-speed Interrupt and Isoch endpoints the +// bInterval field declared by a Full or Low-speed device is computed as +// bInterval * 1ms., where bInterval = 1 to 255. +// +// For Full- and Low-speed devices software shall round the value of Endpoint +// Context Interval field down to the nearest base 2 multiple of bInterval * 8. +// +// Input: +// EpType Endpoint type, see XHCI_EP_CONTEXT.DW1.EpType field definitions +// Speed Endpoint speed, 1..4 for XHCI_DEVSPEED_FULL, _LOW, _HIGH, _SUPER +// Interval Poll interval value from endpoint descriptor +// +// Output: +// Interval value to be written to the endpoint context +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +Xhci_TranslateInterval( + UINT8 EpType, + UINT8 Speed, + UINT8 Interval +) +{ + UINT8 TempData; + UINT8 BitCount; + + if (Interval == 0) { + return 0; + } + + if (EpType == XHCI_EPTYPE_CTL || + EpType == XHCI_EPTYPE_BULK_OUT || + EpType == XHCI_EPTYPE_BULK_IN) { + + if (Speed == XHCI_DEVSPEED_HIGH) { + for (TempData = Interval, BitCount = 0; TempData != 0; BitCount++) { + TempData >>= 1; + } + return BitCount - 1; + } else { + return 0; // Interval field will not be used for LS, FS and SS + } + } + + // Control and Bulk endpoints are processed; translate intervals for Isoc and Interrupt + // endpoints + + // Translate SS and HS endpoints + if (Speed == XHCI_DEVSPEED_SUPER || + Speed == XHCI_DEVSPEED_SUPER_PLUS || + Speed == XHCI_DEVSPEED_HIGH) { + return (Interval - 1); + } + + // Translate interval for FS and LS endpoints + ASSERT(Interval > 0); + + for (TempData = Interval, BitCount = 0; TempData != 0; BitCount++) { + TempData >>= 1; + } + return (BitCount + 2); // return value, where Interval = 0.125*2^value +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_EnableEndpoints +// +// Description: +// This function parses the device descriptor data and enables the endpoints +// by 1)assigning the Transfer TRB and 2)executing ConfigureEndpoint command +// for the slot. Section 4.3.5. +// +// Input: +// DevInfo - A device for which the endpoins are being enabled +// Desc - Device Configuration Descriptor data pointer +// +// Output: +// USB_ERROR on error, USB_SUCCESS on success +// +// Notes: +// 1) DevInfo->DevMiscInfo points to the device context +// 2) This call is executed before SET_CONFIGURATION control transfer +// 3) EP0 information is valid in the Device +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_EnableEndpoints ( + HC_STRUC *HcStruc, + DEV_INFO *DevInfo, + UINT8 *Desc +) +{ + UINT16 TotalLength; + UINT16 CurPos; + UINT8 Dci; + INTRF_DESC *IntrfDesc; + ENDP_DESC *EpDesc; + SS_ENDP_COMP_DESC *SsEpCompDesc = NULL; + HUB_DESC *HubDesc; + TRB_RING *XfrRing; + UINT8 EpType; + UINT8 Status; + UINT8 IsHub = 0; //(EIP73020) + UINT8 Speed; + XHCI_INPUT_CONTROL_CONTEXT *CtlCtx; + XHCI_SLOT_CONTEXT *SlotCtx; + XHCI_EP_CONTEXT *EpCtx; + USB3_HOST_CONTROLLER *Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + UINT8 SlotId = XHCI_GetSlotId(Usb3Hc, DevInfo); + + EFI_STATUS EfiStatus = EFI_SUCCESS; + UINTN DeviceContextSize; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + EfiStatus = UsbDevInfoValidation(DevInfo); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return USB_ERROR; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (gCheckUsbApiParameter) { + EfiStatus = AmiValidateMemoryBuffer((VOID*)Desc, sizeof(CNFG_DESC)); + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + } +#endif + + if (((CNFG_DESC*)Desc)->bDescType != DESC_TYPE_CONFIG) return USB_ERROR; + + DeviceContextSize = (XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize) * Usb3Hc->CapRegs->HcsParams1.MaxSlots; + + if ((DevInfo->DevMiscInfo < Usb3Hc->DeviceContext) || + (DevInfo->DevMiscInfo > (VOID*)((UINTN)(Usb3Hc->DeviceContext) + DeviceContextSize))) { + return USB_ERROR; + } + + SlotCtx = (XHCI_SLOT_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, (UINT8*)DevInfo->DevMiscInfo, 0); + Speed = SlotCtx->Speed; + + // Note (From 4.6.6): The Add Context flag A1 and Drop Context flags D0 and D1 + // of the Input Control Context (in the Input Context) shall be cleared to 0. + // Endpoint 0 Context does not apply to the Configure Endpoint Command and + // shall be ignored by the xHC. A0 shall be set to 1. + + // Note (From 6.2.2.2): If Hub = 1 and Speed = High-Speed (3), then the + // TT Think Time and Multi-TT (MTT) fields shall be initialized. + // If Hub = 1, then the Number of Ports field shall be initialized, else + // Number of Ports = 0. + + // Prepare input context for EvaluateContext comand + MemFill((UINT8*)Usb3Hc->InputContext, XHCI_INPUT_CONTEXT_ENTRIES * Usb3Hc->ContextSize, 0); + + CtlCtx = (XHCI_INPUT_CONTROL_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, 0); + CtlCtx->AddContextFlags = BIT0; // EP0 + + SlotCtx = (XHCI_SLOT_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, 1); + + // Collect the endpoint information and update the Device Input Context + TotalLength = ((CNFG_DESC*)Desc)->wTotalLength; + +#if USB_RUNTIME_DRIVER_IN_SMM + if (gCheckUsbApiParameter) { + EfiStatus = AmiValidateMemoryBuffer((VOID*)Desc, TotalLength); + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + gCheckUsbApiParameter = FALSE; + } +#endif + + if (TotalLength > (MAX_CONTROL_DATA_SIZE - 1)) { + TotalLength = MAX_CONTROL_DATA_SIZE - 1; + } + + for (CurPos = 0; CurPos < TotalLength; CurPos += EpDesc->bDescLength) { + EpDesc = (ENDP_DESC*)(IntrfDesc = (INTRF_DESC*)(Desc + CurPos)); + + if (IntrfDesc->bDescLength == 0) { + break; + } + + if ((CurPos + IntrfDesc->bDescLength) > TotalLength) { + break; + } + + if (IntrfDesc->bDescType == DESC_TYPE_INTERFACE) { + IsHub = IntrfDesc->bBaseClass == BASE_CLASS_HUB; + continue; + } + + if (EpDesc->bDescType != DESC_TYPE_ENDPOINT) continue; + + // Found Endpoint, fill up the information in the InputContext + + // Calculate Device Context Index (DCI), Section 4.5.1. + // 1) For Isoch, Interrupt, or Bulk type endpoints the DCI is calculated + // from the Endpoint Number and Direction with the following formula: + // DCI = (Endpoint Number * 2) + Direction, where Direction = 0 for OUT + // endpoints and 1 for IN endpoints. + // 2) For Control type endpoints: + // DCI = (Endpoint Number * 2) + 1 + // + // Also calculate XHCI EP type out of EpDesc->bEndpointFlags + + if ((EpDesc->bEndpointFlags & EP_DESC_FLAG_TYPE_BITS) == EP_DESC_FLAG_TYPE_CONT) { + Dci = (EpDesc->bEndpointAddr & 0xf) * 2 + 1; + EpType = XHCI_EPTYPE_CTL; + } else { + // Isoc, Bulk or Interrupt endpoint + Dci = (EpDesc->bEndpointAddr & 0xf) * 2; + EpType = EpDesc->bEndpointFlags & EP_DESC_FLAG_TYPE_BITS; // 1, 2, or 3 + + if (EpDesc->bEndpointAddr & BIT7) { + Dci++; // IN + EpType += 4; // 5, 6, or 7 + } + } + + // Update ContextEntries in the Slot context + if (Dci > SlotCtx->ContextEntries) { + SlotCtx->ContextEntries = Dci; + } + + EpCtx = (XHCI_EP_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, Dci + 1); + + EpCtx->EpType = EpType; + + // The Endpoint Companion descriptor shall immediately follow the + // endpoint descriptor it is associated with in the configuration information. + + if ((DevInfo->bEndpointSpeed == USB_DEV_SPEED_SUPER) || + (DevInfo->bEndpointSpeed == USB_DEV_SPEED_SUPER_PLUS)) { + SsEpCompDesc = (SS_ENDP_COMP_DESC*)(Desc + CurPos + EpDesc->bDescLength); + if (SsEpCompDesc->DescType == DESC_TYPE_SS_EP_COMP) { + EpCtx->MaxBurstSize = SsEpCompDesc->MaxBurst; + } + } + + // wMaxPacketSize + // USB 2.0 spec + // For all endpoints, bits 10..0 specify the maximum packet size (in bytes). + // For high-speed isochronous and interrupt endpoints: + // Bits 12..11 specify the number of additional transaction + // opportunities per microframe: + // 00 = None (1 transaction per microframe) + // 01 = 1 additional (2 per microframe) + // 10 = 2 additional (3 per microframe) + // 11 = Reserved + // Bits 15..13 are reserved and must be set to zero. + // USB 3.0 & 3.1 spec + // Maximum packet size this endpoint is capable of sending or receiving + // when this configuration is selected. + // For control endpoints this field shall be set to 512. For bulk endpoint + // types this field shall be set to 1024. + // For interrupt and isochronous endpoints this field shall be set to 1024 if + // this endpoint defines a value in the bMaxBurst field greater than zero. + // If the value in the bMaxBurst field is set to zero then this field can + // have any value from 0 to 1024 for an isochronous endpoint and 1 to + // 1024 for an interrupt endpoint. + + // Only reserve bits 10..0 + EpCtx->MaxPacketSize = (EpDesc->wMaxPacketSize & 0x07FF); + + // 4.14.1.1 System Bus Bandwidth Scheduling + // Reasonable initial values of Average TRB Length for Control endpoints + // Control endpoints would be 8B, Interrupt endpoints 1KB, + // and Bulk and Isoch endpoints 3KB. + + switch (EpCtx->EpType) { + case XHCI_EP_TYPE_ISO_OUT: + case XHCI_EP_TYPE_ISO_IN: + EpCtx->ErrorCount = 0; + EpCtx->AvgTrbLength = 0xC00; + break; + case XHCI_EP_TYPE_BLK_OUT: + case XHCI_EP_TYPE_BLK_IN: + EpCtx->ErrorCount = 3; + EpCtx->AvgTrbLength = 0xC00; + break; + case XHCI_EP_TYPE_INT_OUT: + case XHCI_EP_TYPE_INT_IN: + EpCtx->ErrorCount = 3; + EpCtx->AvgTrbLength = 0x400; + break; + case XHCI_EP_TYPE_CONTROL: + EpCtx->ErrorCount = 3; + EpCtx->AvgTrbLength = 0x08; + break; + default: + break; + } + + // Set Interval + EpCtx->Interval = Xhci_TranslateInterval(EpType, Speed, EpDesc->bPollInterval); + + XfrRing = XHCI_InitXfrRing(Usb3Hc, SlotId, Dci - 1); + EpCtx->TrDequeuePtr = (UINT64)(UINTN)XfrRing->Base + 1; + + CtlCtx->AddContextFlags |= (1 << Dci); + } + + // For a HUB update NumberOfPorts and TTT fields in the Slot context. For that get hub descriptor + // and use bNbrPorts and TT Think time fields (11.23.2.1 of USB2 specification) + // Notes: + // - Slot.Hub field is already updated + // - Do not set NumberOfPorts and TTT fields for 0.95 controllers + + if (IsHub) { + HubDesc = (HUB_DESC*)USB_MemAlloc(sizeof(MEM_BLK)); + UsbHubGetHubDescriptor(HcStruc, DevInfo, HubDesc, sizeof(MEM_BLK)); + //ASSERT(HubDesc->bDescType == DESC_TYPE_HUB || HubDesc->bDescType == DESC_TYPE_SS_HUB); + if ((HubDesc->bDescType == DESC_TYPE_HUB) || + (HubDesc->bDescType == DESC_TYPE_SS_HUB)) { + SlotCtx->Hub = 1; + SlotCtx->PortsNum = HubDesc->bNumPorts; + + if (Speed == XHCI_DEVSPEED_HIGH) { + SlotCtx->TThinkTime = (HubDesc->wHubFlags >> 5) & 0x3; + } + } + USB_MemFree(HubDesc, sizeof(MEM_BLK)); + } + + // Input context is updated with the endpoint information. Execute ConfigureEndpoint command. + Status = XHCI_ExecuteCommand(HcStruc, XhciTConfigureEndpointCmd, &SlotId); + ASSERT(Status == USB_SUCCESS); + + return Status; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_GetRootHubPort +// +// Description: +// This function returns a root hub number for a given device. If device is +// connected to the root through hub(s), it searches the parent's chain up +// to the root. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_GetRootHubPort( + DEV_INFO *DevInfo +) +{ + UINT8 i; + + if ((DevInfo->bHubDeviceNumber & BIT7) != 0) return DevInfo->bHubPortNumber; + + for (i = 1; i < MAX_DEVICES; i++) { + if ((gUsbData->aDevInfoTable[i].Flag & DEV_INFO_VALIDPRESENT) + != DEV_INFO_VALIDPRESENT) { + continue; + } + if (gUsbData->aDevInfoTable[i].bDeviceAddress == DevInfo->bHubDeviceNumber) { + return XHCI_GetRootHubPort(&gUsbData->aDevInfoTable[i]); + } + } + ASSERT(FALSE); // Device parent hub found + return 0; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_InitDeviceData +// +// Description: +// This is an API function for early device initialization. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_InitDeviceData ( + HC_STRUC *HcStruc, + DEV_INFO *DevInfo, + UINT8 PortStatus, + UINT8 **DeviceData +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + UINT8 Status; + UINT8 SlotId; + VOID *DevCtx; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + EfiStatus = UsbDevInfoValidation(DevInfo); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (gCheckUsbApiParameter) { + EfiStatus = AmiValidateMemoryBuffer((VOID*)DeviceData, sizeof(UINT8*)); + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + gCheckUsbApiParameter = FALSE; + } +#endif + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return USB_ERROR; + } + + // Obtain device slot using Enable Slot command, 4.3.2, 4.6.3 + Status = XHCI_ExecuteCommand(HcStruc, XhciTEnableSlotCmd, &SlotId); + //ASSERT(Status == USB_SUCCESS); + //ASSERT(SlotId != 0); + if (Status != USB_SUCCESS) { + return Status; + } + + DevCtx = XHCI_GetDeviceContext(Usb3Hc, SlotId); + MemSet(DevCtx, XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize, 0); + + // Update DCBAA with the new device pointer (index = SlotId) + Usb3Hc->DcbaaPtr->DevCntxtAddr[SlotId-1] = (UINT64)(UINTN)DevCtx; + USB_DEBUG(3, "XHCI: Slot[%d] enabled, device context at %x\n", SlotId, DevCtx); + + Status = XhciAddressDevice(HcStruc, DevInfo, SlotId); + if (Status != USB_SUCCESS) { + return Status; + } + + *DeviceData = (UINT8*)DevCtx; + return USB_SUCCESS; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_DeinitDeviceData +// +// Description: +// This is an API function for removing device related information from HC. +// For xHCI this means: +// - execute DisableSlot commnand +// - clear all endpoint's transfer rings +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_DeinitDeviceData ( + HC_STRUC *HcStruc, + DEV_INFO *DevInfo +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + UINT8 SlotId; + UINT8 Dci; + TRB_RING *XfrRing; + XHCI_SLOT_CONTEXT *SlotCtx; + XHCI_EP_CONTEXT *EpCtx; + UINT16 EpInfo; + EFI_STATUS EfiStatus; + UINTN DeviceContextSize; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + EfiStatus = UsbDevInfoValidation(DevInfo); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return USB_ERROR; + } + + DeviceContextSize = (XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize) * Usb3Hc->CapRegs->HcsParams1.MaxSlots; + + if ((DevInfo->DevMiscInfo < Usb3Hc->DeviceContext) || + (DevInfo->DevMiscInfo > (VOID*)((UINTN)(Usb3Hc->DeviceContext) + DeviceContextSize))) { + return USB_SUCCESS; + } + + SlotId = XHCI_GetSlotId(Usb3Hc, DevInfo); + if (Usb3Hc->DcbaaPtr->DevCntxtAddr[SlotId-1] == 0) return USB_SUCCESS; + + SlotCtx = (XHCI_SLOT_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, (UINT8*)DevInfo->DevMiscInfo, 0); + + // Stop transfer rings + for (Dci = 1; Dci <= SlotCtx->ContextEntries; Dci++) { + EpCtx = (XHCI_EP_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, (UINT8*)DevInfo->DevMiscInfo, Dci); + if (EpCtx->TrDequeuePtr != 0) { + if (EpCtx->EpState == XHCI_EP_STATE_RUNNING) { + EpInfo = (Dci << 8) + SlotId; + XHCI_ExecuteCommand(HcStruc, XhciTStopEndpointCmd, &EpInfo); + } + + // Clear transfer rings + XfrRing = XHCI_GetXfrRing(Usb3Hc, SlotId, Dci - 1); + MemFill((UINT8*)XfrRing->Base, RING_SIZE, 0); + } + } + + XHCI_ExecuteCommand(HcStruc, XhciTDisableSlotCmd, &SlotId); + + Usb3Hc->DcbaaPtr->DevCntxtAddr[SlotId-1] = 0; + DevInfo->DevMiscInfo = NULL; + + return USB_SUCCESS; +} + + //(EIP54283+)> +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_ClearEndpointState +// +// Description: +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_ClearEndpointState( + HC_STRUC *HcStruc, + DEV_INFO *DevInfo, + UINT8 Endpoint +) +{ + USB3_HOST_CONTROLLER *Usb3Hc; + UINT8 SlotId; + UINT8 Dci; + TRB_RING *XfrRing; + UINT8 Status = USB_SUCCESS; + XHCI_SET_TRPTR_CMD_TRB Trb; + XHCI_EP_CONTEXT *EpCtx; + UINT16 EpInfo; + EFI_STATUS EfiStatus; + UINTN DeviceContextSize; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) { + return USB_ERROR; + } + + if (DevInfo->DevMiscInfo == NULL) { + return Status; + } + + SlotId = XHCI_GetSlotId(Usb3Hc, DevInfo); + if (Endpoint != 0) { + Dci = (Endpoint & 0xF) * 2 + (Endpoint >> 7); + } else { + Dci = 1; + } + + DeviceContextSize = (XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize) * Usb3Hc->CapRegs->HcsParams1.MaxSlots; + + if ((DevInfo->DevMiscInfo < Usb3Hc->DeviceContext) || + (DevInfo->DevMiscInfo > (VOID*)((UINTN)(Usb3Hc->DeviceContext) + DeviceContextSize))) { + return USB_ERROR; + } + //<(EIP60460+) + EpCtx = (XHCI_EP_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, DevInfo->DevMiscInfo, Dci); + + if (EpCtx->EpState == XHCI_EP_STATE_RUNNING) { + EpInfo = (Dci << 8) + SlotId; + Status = XHCI_ExecuteCommand(HcStruc, XhciTStopEndpointCmd, &EpInfo); + } //<(EIP54300+) + + // Set TR Dequeue Pointer command may be executed only if the target + // endpoint is in the Error or Stopped state. + if ((EpCtx->EpState == XHCI_EP_STATE_STOPPED) || + (EpCtx->EpState == XHCI_EP_STATE_ERROR)) { + + XfrRing = XHCI_GetXfrRing(Usb3Hc, SlotId, Dci-1); + + Trb.TrPointer = (UINT64)((UINTN)XfrRing->QueuePtr + XfrRing->CycleBit); // Set up DCS + Trb.EndpointId = Dci; + Trb.SlotId = SlotId; + + Status = XHCI_ExecuteCommand(HcStruc, XhciTSetTRDequeuePointerCmd, &Trb); + } + //ASSERT(Status == USB_SUCCESS); + +// Doorbell = XHCI_GetTheDoorbell(Usb3Hc, SlotId); //(EIP61849-) +// *Doorbell = Dci; //(EIP61849-) + + return Status; +} + //<(EIP54283+) +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciAddressDevice +// +// Description: +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XhciAddressDevice ( + HC_STRUC *HcStruc, + DEV_INFO *DevInfo, + UINT8 SlotId +) +{ + USB3_HOST_CONTROLLER *Usb3Hc = (USB3_HOST_CONTROLLER*)HcStruc->usbbus_data; + XHCI_INPUT_CONTROL_CONTEXT *InputCtrl = NULL; + XHCI_SLOT_CONTEXT *InputSlot = NULL; + XHCI_SLOT_CONTEXT *OutputSlot = NULL; + XHCI_SLOT_CONTEXT *ParentHubSlotCtx = NULL; + XHCI_EP_CONTEXT *InputEp0 = NULL; + XHCI_EP_CONTEXT *OutputEp0 = NULL; + UINT8 Status = USB_ERROR; + VOID *DevCtx = XHCI_GetDeviceContext(Usb3Hc, SlotId); + VOID *InputCtx = Usb3Hc->InputContext; + TRB_RING *XfrRing = NULL; + DEV_INFO *ParentHub = NULL; + UINT8 HubPortNumber = 0; + UINT16 AddrDevParam = 0; + UINT8 Bsr = 0; + UINTN DeviceContextSize; + + OutputSlot = XHCI_GetContextEntry(Usb3Hc, DevCtx, 0); + if (OutputSlot->SlotState >= XHCI_SLOT_STATE_ADDRESSED) { + return USB_ERROR; + } + + // Zero the InputContext and DeviceContext + MemSet(InputCtx, XHCI_INPUT_CONTEXT_ENTRIES * Usb3Hc->ContextSize, 0); + + // Initialize the Input Control Context of the Input Context + // by setting the A0 flags to 1 + InputCtrl = XHCI_GetContextEntry(Usb3Hc, InputCtx, 0); + InputCtrl->AddContextFlags = BIT0 | BIT1; + + // Initialize the Input Slot Context data structure + InputSlot = XHCI_GetContextEntry(Usb3Hc, InputCtx, 1); + InputSlot->RouteString = 0; + InputSlot->ContextEntries = 1; + InputSlot->RootHubPort = XHCI_GetRootHubPort(DevInfo); + + switch (DevInfo->bEndpointSpeed) { + case USB_DEV_SPEED_SUPER_PLUS: + InputSlot->Speed = XHCI_DEVSPEED_SUPER_PLUS; + break; + case USB_DEV_SPEED_SUPER: + InputSlot->Speed = XHCI_DEVSPEED_SUPER; + break; + case USB_DEV_SPEED_HIGH: + InputSlot->Speed = XHCI_DEVSPEED_HIGH; + break; + case USB_DEV_SPEED_LOW: + InputSlot->Speed = XHCI_DEVSPEED_LOW; + break; + case USB_DEV_SPEED_FULL: + InputSlot->Speed = XHCI_DEVSPEED_FULL; + break; + } + + // Initialize Route String and TT fields + ParentHub = USB_GetDeviceInfoStruc(USB_SRCH_DEV_ADDR, + 0, DevInfo->bHubDeviceNumber, 0); + if (ParentHub != NULL) { + DeviceContextSize = (XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize) * Usb3Hc->CapRegs->HcsParams1.MaxSlots; + + if ((ParentHub->DevMiscInfo < Usb3Hc->DeviceContext) || + (ParentHub->DevMiscInfo > (VOID*)((UINTN)(Usb3Hc->DeviceContext) + DeviceContextSize))) { + return 0; + } + ParentHubSlotCtx = XHCI_GetContextEntry(Usb3Hc, ParentHub->DevMiscInfo, 0); + HubPortNumber = (DevInfo->bHubPortNumber > 15)? 15 : DevInfo->bHubPortNumber; + InputSlot->RouteString = ParentHubSlotCtx->RouteString | + (HubPortNumber << (ParentHub->HubDepth * 4)); //(EIP51503) + + // Update TT fields in the Slot context for LS/FS device connected to HS hub + if (InputSlot->Speed == XHCI_DEVSPEED_FULL || InputSlot->Speed == XHCI_DEVSPEED_LOW) { + if (ParentHubSlotCtx->Speed == XHCI_DEVSPEED_HIGH) { + InputSlot->TtHubSlotId = XHCI_GetSlotId(Usb3Hc, ParentHub); + InputSlot->TtPortNumber = DevInfo->bHubPortNumber; + InputSlot->MultiTT = ParentHubSlotCtx->MultiTT; + } else { + InputSlot->TtHubSlotId = ParentHubSlotCtx->TtHubSlotId; + InputSlot->TtPortNumber = ParentHubSlotCtx->TtPortNumber; + InputSlot->MultiTT = ParentHubSlotCtx->MultiTT; + } + } + } + + OutputEp0 = XHCI_GetContextEntry(Usb3Hc, DevCtx, 1); + switch (OutputEp0->EpState) { + case XHCI_EP_STATE_DISABLED: + XfrRing = XHCI_InitXfrRing(Usb3Hc, SlotId, 0); + break; + case XHCI_EP_STATE_RUNNING: + case XHCI_EP_STATE_STOPPED: + XfrRing = XHCI_GetXfrRing(Usb3Hc, SlotId, 0); + break; + default: + break; + } + + // Initialize the Input default control Endpoint 0 Context + InputEp0 = XHCI_GetContextEntry(Usb3Hc, InputCtx, 2); + InputEp0->EpType = XHCI_EPTYPE_CTL; + InputEp0->MaxPacketSize = DevInfo->wEndp0MaxPacket; + InputEp0->TrDequeuePtr = (UINT64)(UINTN)XfrRing->QueuePtr | XfrRing->CycleBit; + InputEp0->AvgTrbLength = 8; + InputEp0->ErrorCount = 3; + + Bsr = (InputSlot->Speed != XHCI_DEVSPEED_SUPER && + InputSlot->Speed != XHCI_DEVSPEED_SUPER_PLUS && + OutputSlot->SlotState == XHCI_SLOT_STATE_DISABLED) ? 1 : 0; + + AddrDevParam = (UINT16)SlotId | (Bsr << 8); + + // Assign a new address 4.3.4, 4.6.5 + Status = XHCI_ExecuteCommand(HcStruc, XhciTAddressDeviceCmd, &AddrDevParam); + if (Status != USB_SUCCESS) { + XHCI_ExecuteCommand(HcStruc, XhciTDisableSlotCmd, &SlotId); + Usb3Hc->DcbaaPtr->DevCntxtAddr[SlotId-1] = 0; + return Status; + } + + if (Bsr == 0) { + USB_DEBUG(3, "XHCI: new device address %d\n", OutputSlot->DevAddr); + } + + return USB_SUCCESS; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciRingDoorbell +// +// Description: +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XhciRingDoorbell( + USB3_HOST_CONTROLLER *Usb3Hc, + UINT8 SlotId, + UINT8 Dci +) +{ + volatile UINT32 *Doorbell; + XHCI_EP_CONTEXT *EpCtx = NULL; + UINT32 Count; + + Doorbell = XHCI_GetTheDoorbell(Usb3Hc, SlotId); + *Doorbell = Dci; + + if (SlotId == 0) { + return USB_ERROR; + } + + EpCtx = (XHCI_EP_CONTEXT*)XHCI_GetContextEntry(Usb3Hc, + XHCI_GetDeviceContext(Usb3Hc, SlotId), Dci); + // Wait for the endpoint running + for (Count = 0; Count < 10 * 1000; Count++) { + if (EpCtx->EpState == XHCI_EP_STATE_RUNNING) { + break; + } + FixedDelay(1); // 1 us delay + } + //ASSERT(EpCtx->EpState == XHCI_EP_STATE_RUNNING); + + if (EpCtx->EpState != XHCI_EP_STATE_RUNNING) { + return USB_ERROR; + } + + return USB_SUCCESS; +} + //<(EIP54283+) + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_Mmio64Write +// +// Description: +// HC may or may not support 64-bit writes to MMIO area. If it does, write +// Data directly, otherwise split into two DWORDs. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT64 +XHCI_Mmio64Read( + HC_STRUC *HcStruc, + USB3_HOST_CONTROLLER *Usb3Hc, + UINTN Address +) +{ + UINT64 Data = 0; + UINT32 Offset; + + Offset = (UINT32)(Address - HcStruc->BaseAddress); + + if ((Offset + sizeof(UINT64)) > HcStruc->BaseAddressSize) { + return 0; + } + + if (Usb3Hc->Access64) { + Data = *(UINT64*)Address; + } + else { + Data = *(UINT32*)Address; + Data = Shl64(*(UINT32*)(Address + sizeof(UINT32)), 32); + } + return Data; +} + +VOID +XHCI_Mmio64Write( + HC_STRUC *HcStruc, + USB3_HOST_CONTROLLER *Usb3Hc, + UINTN Address, + UINT64 Data +) +{ + UINT32 Offset; + + Offset = (UINT32)(Address - HcStruc->BaseAddress); + + if ((Offset + sizeof(UINT64)) > HcStruc->BaseAddressSize) { + return; + } + + if (Usb3Hc->Access64) { + *(UINT64*)Address = Data; + } + else { + *(UINT32*)Address = (UINT32)Data; + *(UINT32*)(Address + sizeof(UINT32)) = (UINT32)(Shr64(Data, 32)); + } +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_InitRing +// +// Description: +// Transfer ring initialization. There is an option to create a Link TRB in +// the end of the ring. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +XHCI_InitRing ( + IN OUT TRB_RING *Ring, + IN UINTN RingBase, + IN UINT32 RingSize, + IN BOOLEAN PlaceLinkTrb +) +{ + XHCI_LINK_TRB *LinkTrb; + EFI_STATUS Status = EFI_SUCCESS; + + Ring->Base = (XHCI_TRB*)RingBase; + Ring->Size = RingSize; + Ring->LastTrb = Ring->Base + RingSize - 1; + Ring->CycleBit = 1; + Ring->QueuePtr = (XHCI_TRB*)RingBase; + + // Initialize ring with zeroes + { + UINT8 *p = (UINT8*)RingBase; + UINTN i; + for (i = 0; i < RingSize*sizeof(XHCI_TRB); i++, p++) *p = 0; + } + + if (PlaceLinkTrb) { + // Place a Link TRB in the end of the ring pointing to the beginning + LinkTrb = (XHCI_LINK_TRB*)Ring->LastTrb; +#if USB_RUNTIME_DRIVER_IN_SMM + Status = AmiValidateMemoryBuffer((VOID*)LinkTrb, sizeof(XHCI_LINK_TRB)); + if (EFI_ERROR(Status)) { + return Status; + } +#endif + LinkTrb->NextSegPtr = (UINT64)(UINTN)RingBase; + LinkTrb->ToggleCycle = 1; + LinkTrb->TrbType = XhciTLink; + } + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: UpdatePortStatusSpeed +// +// Description: +// This function sets USB_PORT_STAT... fields that are related to device +// speed (LS/FS/HS/SS) in a given PortStatus variable. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +UpdatePortStatusSpeed( + UINT8 Speed, + UINT8 *PortStatus +) +{ + UINT8 PortSts = *PortStatus; + + ASSERT(Speed < 6); + PortSts &= ~USB_PORT_STAT_DEV_SPEED_MASK; + + switch (Speed) { + case XHCI_DEVSPEED_UNDEFINED: + break; + case XHCI_DEVSPEED_FULL: + PortSts |= USB_PORT_STAT_DEV_FULLSPEED; + break; + case XHCI_DEVSPEED_LOW: + PortSts |= USB_PORT_STAT_DEV_LOWSPEED; + break; + case XHCI_DEVSPEED_HIGH: + PortSts |= USB_PORT_STAT_DEV_HISPEED; + break; + case XHCI_DEVSPEED_SUPER: + PortSts |= USB_PORT_STAT_DEV_SUPERSPEED; + break; + case XHCI_DEVSPEED_SUPER_PLUS: + PortSts |= USB_PORT_STAT_DEV_SUPERSPEED_PLUS; + break; + default: + USB_DEBUG(3, "XHCI ERROR: unknown device speed.\n"); + } + + *PortStatus = PortSts; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciExtCapParser +// +// Description: +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +XhciExtCapParser( + IN USB3_HOST_CONTROLLER *Usb3Hc +) +{ + XHCI_EXT_CAP *CurPtr; + + if (Usb3Hc->CapRegs->HccParams1.Xecp == 0) return EFI_SUCCESS; + + // Starts from first capability + CurPtr = (XHCI_EXT_CAP *)((UINTN)Usb3Hc->CapRegs + (Usb3Hc->CapRegs->HccParams1.Xecp << 2)); + + // Traverse all capability structures + for(;;) { + switch (CurPtr->CapId) { + case XHCI_EXT_CAP_USB_LEGACY: + Usb3Hc->ExtLegCap = (XHCI_EXT_LEG_CAP *)CurPtr; + USB_DEBUG(3, "XHCI: USB Legacy Ext Cap Ptr %x\n", Usb3Hc->ExtLegCap); + break; + + case XHCI_EXT_CAP_SUPPORTED_PROTOCOL: + if (((XHCI_EXT_PROTOCOL*)CurPtr)->MajorRev == 0x02) { + Usb3Hc->Usb2Protocol = (XHCI_EXT_PROTOCOL*)CurPtr; + USB_DEBUG(3, "XHCI: USB2 Support Protocol %x, PortOffset %x PortCount %x\n", + Usb3Hc->Usb2Protocol, Usb3Hc->Usb2Protocol->PortOffset, Usb3Hc->Usb2Protocol->PortCount); + } else if (((XHCI_EXT_PROTOCOL*)CurPtr)->MajorRev == 0x03) { + Usb3Hc->Usb3Protocol = (XHCI_EXT_PROTOCOL*)CurPtr; + USB_DEBUG(3, "XHCI: USB3 Support Protocol %x, PortOffset %x PortCount %x\n", + Usb3Hc->Usb3Protocol, Usb3Hc->Usb3Protocol->PortOffset, Usb3Hc->Usb3Protocol->PortCount); + } + break; + + case XHCI_EXT_CAP_POWERMANAGEMENT: + case XHCI_EXT_CAP_IO_VIRTUALIZATION: + break; + case XHCI_EXT_CAP_USB_DEBUG_PORT: + Usb3Hc->DbCapRegs = (XHCI_DB_CAP_REGS*)CurPtr; + USB_DEBUG(3, "XHCI: USB Debug Capability Ptr %x\n", Usb3Hc->DbCapRegs); + break; + } + if(CurPtr->NextCapPtr == 0) break; + // Point to next capability + CurPtr=(XHCI_EXT_CAP *)((UINTN)CurPtr+ (((UINTN)CurPtr->NextCapPtr) << 2)); + } + + return EFI_SUCCESS; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: XHCI_IsUsb3Port +// +// Description: +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +BOOLEAN +XHCI_IsUsb3Port( + USB3_HOST_CONTROLLER *Usb3Hc, + UINT8 Port +) +{ + if ((Port >= Usb3Hc->Usb3Protocol->PortOffset) && + (Port < Usb3Hc->Usb3Protocol->PortOffset + Usb3Hc->Usb3Protocol->PortCount)) { + return TRUE; + } + return FALSE; +} + +//**************************************************************************** +// The following set of functions are the helpers to get the proper locations +// of xHCI data structures using the available pointers. +//**************************************************************************** + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_GetSlotId +// +// Description: +// This function calculates the slot ID out of a given DEV_INFO data pointer. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XHCI_GetSlotId( + USB3_HOST_CONTROLLER *Usb3Hc, + DEV_INFO *DevInfo +) +{ + UINT32 DevCtxSize = XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize; + return (UINT8)(((UINTN)DevInfo->DevMiscInfo - (UINTN)Usb3Hc->DeviceContext)/DevCtxSize) + 1; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XHCI_GetXfrRing +// +// Description: +// This routine calculates the address of the address ring of a particular +// Slot/Endpoint. +// +// Output: +// Pointer to the transfer ring +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +TRB_RING* +XHCI_GetXfrRing( + USB3_HOST_CONTROLLER* Usb3Hc, + UINT8 Slot, + UINT8 Ep +) +{ + return Usb3Hc->XfrRings + (Slot-1)*32 + Ep; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_GetTheDoorbell +// +// Description: +// This function calculates and returns the pointer to a doorbell for a +// given Slot. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT32* +XHCI_GetTheDoorbell( + USB3_HOST_CONTROLLER *Usb3Hc, + UINT8 SlotId +) +{ + return (UINT32*)((UINTN)Usb3Hc->CapRegs + Usb3Hc->DbOffset + sizeof(UINT32)*SlotId); +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_GetDevInfo +// +// Description: +// This function searches for DEV_INFO data pointer that belongs to a given XHCI +// device context. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +DEV_INFO* +XHCI_GetDevInfo( + UINTN PollTdPtr +) +{ + UINT8 i; + DEV_INFO *DevInfo; + + if (PollTdPtr == 0) return NULL; + + for (i=1; i<MAX_DEVICES; i++) { + DevInfo = &gUsbData->aDevInfoTable[i]; + if ((DevInfo->Flag & DEV_INFO_VALIDPRESENT) != DEV_INFO_VALIDPRESENT) { + continue; + } + if (DevInfo->fpPollTDPtr == NULL) { + continue; + } + if ((UINTN)DevInfo->fpPollTDPtr == (UINTN)PollTdPtr) { + return DevInfo; + } + } + return NULL; // Device not found +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_GetDeviceContext +// +// Description: +// This function calculates and returns the pointer to a device context for +// a given Slot. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID* +XHCI_GetDeviceContext( + USB3_HOST_CONTROLLER *Usb3Hc, + UINT8 SlotId +) +{ + UINT32 DevCtxSize = XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize; + return (UINT8*)((UINTN)Usb3Hc->DeviceContext + (SlotId - 1) * DevCtxSize); +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XHCI_GetContextEntry +// +// Description: +// This function calculates and returns the pointer to a context entry for +// a given index. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID* +XHCI_GetContextEntry( + USB3_HOST_CONTROLLER *Usb3Hc, + VOID *Context, + UINT8 Index +) +{ + return (UINT8*)((UINTN)Context + Index * Usb3Hc->ContextSize); +} + +//**************************************************************************** +//**************************************************************************** +//** ** +//** (C)Copyright 1985-2016, American Megatrends, Inc. ** +//** ** +//** All Rights Reserved. ** +//** ** +//** 5555 Oakbrook Pkwy, Norcross, GA 30093 ** +//** ** +//** Phone (770)-246-8600 ** +//** ** +//**************************************************************************** +//**************************************************************************** |