From 4b1bf81c20d5523554a83f6604df7930396f7c21 Mon Sep 17 00:00:00 2001 From: jljusten Date: Mon, 27 Jun 2011 23:30:55 +0000 Subject: MdeModulePkg: Add PEI USB drivers and related PPIs Signed-off-by: jljusten Reviewed-by: mdkinney Reviewed-by: geekboy15a git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11901 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.c | 1248 ++++++++++++ MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.h | 227 +++ MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf | 66 + MdeModulePkg/Bus/Pci/EhciPei/EhciReg.h | 310 +++ MdeModulePkg/Bus/Pci/EhciPei/EhciSched.c | 873 ++++++++ MdeModulePkg/Bus/Pci/EhciPei/EhciSched.h | 180 ++ MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c | 610 ++++++ MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.h | 331 ++++ MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.c | 493 +++++ MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.h | 77 + MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.c | 3196 ++++++++++++++++++++++++++++++ MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.h | 1332 +++++++++++++ MdeModulePkg/Bus/Pci/UhciPei/UhciPei.inf | 62 + 13 files changed, 9005 insertions(+) create mode 100644 MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.c create mode 100644 MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.h create mode 100644 MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf create mode 100644 MdeModulePkg/Bus/Pci/EhciPei/EhciReg.h create mode 100644 MdeModulePkg/Bus/Pci/EhciPei/EhciSched.c create mode 100644 MdeModulePkg/Bus/Pci/EhciPei/EhciSched.h create mode 100644 MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c create mode 100644 MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.h create mode 100644 MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.c create mode 100644 MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.h create mode 100644 MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.c create mode 100644 MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.h create mode 100644 MdeModulePkg/Bus/Pci/UhciPei/UhciPei.inf (limited to 'MdeModulePkg/Bus/Pci') diff --git a/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.c b/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.c new file mode 100644 index 0000000000..6c179b0935 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.c @@ -0,0 +1,1248 @@ +/** @file +PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid +which is used to enable recovery function from USB Drivers. + +Copyright (c) 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "EhcPeim.h" + +// +// Two arrays used to translate the EHCI port state (change) +// to the UEFI protocol's port state (change). +// +USB_PORT_STATE_MAP mUsbPortStateMap[] = { + {PORTSC_CONN, USB_PORT_STAT_CONNECTION}, + {PORTSC_ENABLED, USB_PORT_STAT_ENABLE}, + {PORTSC_SUSPEND, USB_PORT_STAT_SUSPEND}, + {PORTSC_OVERCUR, USB_PORT_STAT_OVERCURRENT}, + {PORTSC_RESET, USB_PORT_STAT_RESET}, + {PORTSC_POWER, USB_PORT_STAT_POWER}, + {PORTSC_OWNER, USB_PORT_STAT_OWNER} +}; + +USB_PORT_STATE_MAP mUsbPortChangeMap[] = { + {PORTSC_CONN_CHANGE, USB_PORT_STAT_C_CONNECTION}, + {PORTSC_ENABLE_CHANGE, USB_PORT_STAT_C_ENABLE}, + {PORTSC_OVERCUR_CHANGE, USB_PORT_STAT_C_OVERCURRENT} +}; + +/** + Read Ehc Operation register. + + @param Ehc The EHCI device. + @param Offset The operation register offset. + + @retval the register content read. + +**/ +UINT32 +EhcReadOpReg ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Offset + ) +{ + UINT32 Data; + + ASSERT (Ehc->CapLen != 0); + + Data = MmioRead32 (Ehc->UsbHostControllerBaseAddress + Ehc->CapLen + Offset); + + return Data; +} + +/** + Write the data to the EHCI operation register. + + @param Ehc The EHCI device. + @param Offset EHCI operation register offset. + @param Data The data to write. + +**/ +VOID +EhcWriteOpReg ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Offset, + IN UINT32 Data + ) +{ + + ASSERT (Ehc->CapLen != 0); + + MmioWrite32(Ehc->UsbHostControllerBaseAddress + Ehc->CapLen + Offset, Data); + +} + +/** + Set one bit of the operational register while keeping other bits. + + @param Ehc The EHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to set. + +**/ +VOID +EhcSetOpRegBit ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = EhcReadOpReg (Ehc, Offset); + Data |= Bit; + EhcWriteOpReg (Ehc, Offset, Data); +} + +/** + Clear one bit of the operational register while keeping other bits. + + @param Ehc The EHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to clear. + +**/ +VOID +EhcClearOpRegBit ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = EhcReadOpReg (Ehc, Offset); + Data &= ~Bit; + EhcWriteOpReg (Ehc, Offset, Data); +} + +/** + Wait the operation register's bit as specified by Bit + to become set (or clear). + + @param Ehc The EHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to wait for. + @param WaitToSet Wait the bit to set or clear. + @param Timeout The time to wait before abort (in millisecond). + + @retval EFI_SUCCESS The bit successfully changed by host controller. + @retval EFI_TIMEOUT The time out occurred. + +**/ +EFI_STATUS +EhcWaitOpRegBit ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Offset, + IN UINT32 Bit, + IN BOOLEAN WaitToSet, + IN UINT32 Timeout + ) +{ + UINT32 Index; + + for (Index = 0; Index < Timeout / EHC_SYNC_POLL_INTERVAL + 1; Index++) { + if (EHC_REG_BIT_IS_SET (Ehc, Offset, Bit) == WaitToSet) { + return EFI_SUCCESS; + } + + MicroSecondDelay (EHC_SYNC_POLL_INTERVAL); + } + + return EFI_TIMEOUT; +} + +/** + Read EHCI capability register. + + @param Ehc The EHCI device. + @param Offset Capability register address. + + @retval the register content read. + +**/ +UINT32 +EhcReadCapRegister ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Offset + ) +{ + UINT32 Data; + + Data = MmioRead32(Ehc->UsbHostControllerBaseAddress + Offset); + + return Data; +} + +/** + Set door bell and wait it to be ACKed by host controller. + This function is used to synchronize with the hardware. + + @param Ehc The EHCI device. + @param Timeout The time to wait before abort (in millisecond, ms). + + @retval EFI_TIMEOUT Time out happened while waiting door bell to set. + @retval EFI_SUCCESS Synchronized with the hardware. + +**/ +EFI_STATUS +EhcSetAndWaitDoorBell ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + UINT32 Data; + + EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_IAAD); + + Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_IAA, TRUE, Timeout); + + // + // ACK the IAA bit in USBSTS register. Make sure other + // interrupt bits are not ACKed. These bits are WC (Write Clean). + // + Data = EhcReadOpReg (Ehc, EHC_USBSTS_OFFSET); + Data &= ~USBSTS_INTACK_MASK; + Data |= USBSTS_IAA; + + EhcWriteOpReg (Ehc, EHC_USBSTS_OFFSET, Data); + + return Status; +} + +/** + Clear all the interrutp status bits, these bits + are Write-Clean. + + @param Ehc The EHCI device. + +**/ +VOID +EhcAckAllInterrupt ( + IN PEI_USB2_HC_DEV *Ehc + ) +{ + EhcWriteOpReg (Ehc, EHC_USBSTS_OFFSET, USBSTS_INTACK_MASK); +} + +/** + Enable the periodic schedule then wait EHC to + actually enable it. + + @param Ehc The EHCI device. + @param Timeout The time to wait before abort (in millisecond, ms). + + @retval EFI_TIMEOUT Time out happened while enabling periodic schedule. + @retval EFI_SUCCESS The periodical schedule is enabled. + +**/ +EFI_STATUS +EhcEnablePeriodSchd ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + + EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_PERIOD); + + Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_PERIOD_ENABLED, TRUE, Timeout); + return Status; +} + +/** + Enable asynchrounous schedule. + + @param Ehc The EHCI device. + @param Timeout Time to wait before abort. + + @retval EFI_SUCCESS The EHCI asynchronous schedule is enabled. + @retval Others Failed to enable the asynchronous scheudle. + +**/ +EFI_STATUS +EhcEnableAsyncSchd ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + + EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_ASYNC); + + Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_ASYNC_ENABLED, TRUE, Timeout); + return Status; +} + +/** + Check whether Ehc is halted. + + @param Ehc The EHCI device. + + @retval TRUE The controller is halted. + @retval FALSE The controller isn't halted. + +**/ +BOOLEAN +EhcIsHalt ( + IN PEI_USB2_HC_DEV *Ehc + ) +{ + return EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT); +} + +/** + Check whether system error occurred. + + @param Ehc The EHCI device. + + @retval TRUE System error happened. + @retval FALSE No system error. + +**/ +BOOLEAN +EhcIsSysError ( + IN PEI_USB2_HC_DEV *Ehc + ) +{ + return EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_SYS_ERROR); +} + +/** + Reset the host controller. + + @param Ehc The EHCI device. + @param Timeout Time to wait before abort (in millisecond, ms). + + @retval EFI_TIMEOUT The transfer failed due to time out. + @retval Others Failed to reset the host. + +**/ +EFI_STATUS +EhcResetHC ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + + // + // Host can only be reset when it is halt. If not so, halt it + // + if (!EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT)) { + Status = EhcHaltHC (Ehc, Timeout); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RESET); + Status = EhcWaitOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RESET, FALSE, Timeout); + return Status; +} + +/** + Halt the host controller. + + @param Ehc The EHCI device. + @param Timeout Time to wait before abort. + + @retval EFI_TIMEOUT Failed to halt the controller before Timeout. + @retval EFI_SUCCESS The EHCI is halt. + +**/ +EFI_STATUS +EhcHaltHC ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + + EhcClearOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN); + Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT, TRUE, Timeout); + return Status; +} + +/** + Set the EHCI to run. + + @param Ehc The EHCI device. + @param Timeout Time to wait before abort. + + @retval EFI_SUCCESS The EHCI is running. + @retval Others Failed to set the EHCI to run. + +**/ +EFI_STATUS +EhcRunHC ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + + EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN); + Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT, FALSE, Timeout); + return Status; +} + +/** + Initialize the HC hardware. + EHCI spec lists the five things to do to initialize the hardware. + 1. Program CTRLDSSEGMENT. + 2. Set USBINTR to enable interrupts. + 3. Set periodic list base. + 4. Set USBCMD, interrupt threshold, frame list size etc. + 5. Write 1 to CONFIGFLAG to route all ports to EHCI. + + @param Ehc The EHCI device. + + @retval EFI_SUCCESS The EHCI has come out of halt state. + @retval EFI_TIMEOUT Time out happened. + +**/ +EFI_STATUS +EhcInitHC ( + IN PEI_USB2_HC_DEV *Ehc + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS TempPtr; + UINTN PageNumber; + + ASSERT (EhcIsHalt (Ehc)); + + // + // Allocate the periodic frame and associated memeory + // management facilities if not already done. + // + if (Ehc->PeriodFrame != NULL) { + EhcFreeSched (Ehc); + } + PageNumber = sizeof(PEI_URB)/PAGESIZE +1; + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + PageNumber, + &TempPtr + ); + Ehc->Urb = (PEI_URB *) ((UINTN) TempPtr); + if (Ehc->Urb == NULL) { + return Status; + } + + Status = EhcInitSched (Ehc); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // 1. Program the CTRLDSSEGMENT register with the high 32 bit addr + // + EhcWriteOpReg (Ehc, EHC_CTRLDSSEG_OFFSET, Ehc->High32bitAddr); + + // + // 2. Clear USBINTR to disable all the interrupt. UEFI works by polling + // + EhcWriteOpReg (Ehc, EHC_USBINTR_OFFSET, 0); + + // + // 3. Program periodic frame list, already done in EhcInitSched + // 4. Start the Host Controller + // + EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN); + + // + // 5. Set all ports routing to EHC + // + EhcSetOpRegBit (Ehc, EHC_CONFIG_FLAG_OFFSET, CONFIGFLAG_ROUTE_EHC); + + // + // Wait roothub port power stable + // + MicroSecondDelay (EHC_ROOT_PORT_RECOVERY_STALL); + + Status = EhcEnablePeriodSchd (Ehc, EHC_GENERIC_TIMEOUT); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EhcEnableAsyncSchd (Ehc, EHC_GENERIC_TIMEOUT); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Submits bulk transfer to a bulk endpoint of a USB device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and its direction in bit 7. + @param DeviceSpeed Device speed, Low speed device doesn't support + bulk transfer. + @param MaximumPacketLength Maximum packet size the endpoint is capable of + sending or receiving. + @param Data Array of pointers to the buffers of data to transmit + from or receive into. + @param DataLength The lenght of the data buffer. + @param DataToggle On input, the initial data toggle for the transfer; + On output, it is updated to to next data toggle to use of + the subsequent bulk transfer. + @param TimeOut Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + @param Translator A pointr to the transaction translator data. + @param TransferResult A pointer to the detailed result information of the + bulk transfer. + + @retval EFI_SUCCESS The transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource. + @retval EFI_INVALID_PARAMETER Parameters are invalid. + @retval EFI_TIMEOUT The transfer failed due to timeout. + @retval EFI_DEVICE_ERROR The transfer failed due to host controller error. + +**/ +EFI_STATUS +EFIAPI +EhcBulkTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM], + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + PEI_USB2_HC_DEV *Ehc; + PEI_URB *Urb; + EFI_STATUS Status; + + // + // Validate the parameters + // + if ((DataLength == NULL) || (*DataLength == 0) || + (Data == NULL) || (Data[0] == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 0) && (*DataToggle != 1)) { + return EFI_INVALID_PARAMETER; + } + + if ((DeviceSpeed == EFI_USB_SPEED_LOW) || + ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) || + ((EFI_USB_SPEED_HIGH == DeviceSpeed) && (MaximumPacketLength > 512))) { + return EFI_INVALID_PARAMETER; + } + + Ehc =PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS(This); + *TransferResult = EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + + if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) { + EhcAckAllInterrupt (Ehc); + goto ON_EXIT; + } + + EhcAckAllInterrupt (Ehc); + + // + // Create a new URB, insert it into the asynchronous + // schedule list, then poll the execution status. + // + Urb = EhcCreateUrb ( + Ehc, + DeviceAddress, + EndPointAddress, + DeviceSpeed, + *DataToggle, + MaximumPacketLength, + Translator, + EHC_BULK_TRANSFER, + NULL, + Data[0], + *DataLength, + NULL, + NULL, + 1 + ); + + if (Urb == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + EhcLinkQhToAsync (Ehc, Urb->Qh); + Status = EhcExecTransfer (Ehc, Urb, TimeOut); + EhcUnlinkQhFromAsync (Ehc, Urb->Qh); + + *TransferResult = Urb->Result; + *DataLength = Urb->Completed; + *DataToggle = Urb->DataToggle; + + if (*TransferResult == EFI_USB_NOERROR) { + Status = EFI_SUCCESS; + } + + EhcAckAllInterrupt (Ehc); + EhcFreeUrb (Ehc, Urb); + +ON_EXIT: + return Status; +} + +/** + Retrieves the number of root hub ports. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the + PEI_USB2_HOST_CONTROLLER_PPI. + @param[out] PortNumber The pointer to the number of the root hub ports. + + @retval EFI_SUCCESS The port number was retrieved successfully. + @retval EFI_INVALID_PARAMETER PortNumber is NULL. + +**/ +EFI_STATUS +EFIAPI +EhcGetRootHubPortNumber ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + OUT UINT8 *PortNumber + ) +{ + + PEI_USB2_HC_DEV *EhcDev; + EhcDev = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This); + + if (PortNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + + *PortNumber = (UINT8)(EhcDev->HcStructParams & HCSP_NPORTS); + return EFI_SUCCESS; + +} + +/** + Clears a feature for the specified root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI. + @param PortNumber Specifies the root hub port whose feature + is requested to be cleared. + @param PortFeature Indicates the feature selector associated with the + feature clear request. + + @retval EFI_SUCCESS The feature specified by PortFeature was cleared + for the USB root hub port specified by PortNumber. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + +**/ +EFI_STATUS +EFIAPI +EhcClearRootHubPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + PEI_USB2_HC_DEV *Ehc; + UINT32 Offset; + UINT32 State; + UINT32 TotalPort; + EFI_STATUS Status; + + Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This); + Status = EFI_SUCCESS; + + TotalPort = (Ehc->HcStructParams & HCSP_NPORTS); + + if (PortNumber >= TotalPort) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Offset = EHC_PORT_STAT_OFFSET + (4 * PortNumber); + State = EhcReadOpReg (Ehc, Offset); + State &= ~PORTSC_CHANGE_MASK; + + switch (PortFeature) { + case EfiUsbPortEnable: + // + // Clear PORT_ENABLE feature means disable port. + // + State &= ~PORTSC_ENABLED; + EhcWriteOpReg (Ehc, Offset, State); + break; + + case EfiUsbPortSuspend: + // + // A write of zero to this bit is ignored by the host + // controller. The host controller will unconditionally + // set this bit to a zero when: + // 1. software sets the Forct Port Resume bit to a zero from a one. + // 2. software sets the Port Reset bit to a one frome a zero. + // + State &= ~PORSTSC_RESUME; + EhcWriteOpReg (Ehc, Offset, State); + break; + + case EfiUsbPortReset: + // + // Clear PORT_RESET means clear the reset signal. + // + State &= ~PORTSC_RESET; + EhcWriteOpReg (Ehc, Offset, State); + break; + + case EfiUsbPortOwner: + // + // Clear port owner means this port owned by EHC + // + State &= ~PORTSC_OWNER; + EhcWriteOpReg (Ehc, Offset, State); + break; + + case EfiUsbPortConnectChange: + // + // Clear connect status change + // + State |= PORTSC_CONN_CHANGE; + EhcWriteOpReg (Ehc, Offset, State); + break; + + case EfiUsbPortEnableChange: + // + // Clear enable status change + // + State |= PORTSC_ENABLE_CHANGE; + EhcWriteOpReg (Ehc, Offset, State); + break; + + case EfiUsbPortOverCurrentChange: + // + // Clear PortOverCurrent change + // + State |= PORTSC_OVERCUR_CHANGE; + EhcWriteOpReg (Ehc, Offset, State); + break; + + case EfiUsbPortPower: + case EfiUsbPortSuspendChange: + case EfiUsbPortResetChange: + // + // Not supported or not related operation + // + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; + } + +ON_EXIT: + return Status; +} + +/** + Sets a feature for the specified root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES + @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI + @param PortNumber Root hub port to set. + @param PortFeature Feature to set. + + @retval EFI_SUCCESS The feature specified by PortFeature was set. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + @retval EFI_TIMEOUT The time out occurred. + +**/ +EFI_STATUS +EFIAPI +EhcSetRootHubPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + PEI_USB2_HC_DEV *Ehc; + UINT32 Offset; + UINT32 State; + UINT32 TotalPort; + EFI_STATUS Status; + + Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This); + Status = EFI_SUCCESS; + + TotalPort = (Ehc->HcStructParams & HCSP_NPORTS); + + if (PortNumber >= TotalPort) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Offset = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber)); + State = EhcReadOpReg (Ehc, Offset); + + // + // Mask off the port status change bits, these bits are + // write clean bit + // + State &= ~PORTSC_CHANGE_MASK; + + switch (PortFeature) { + case EfiUsbPortEnable: + // + // Sofeware can't set this bit, Port can only be enable by + // EHCI as a part of the reset and enable + // + State |= PORTSC_ENABLED; + EhcWriteOpReg (Ehc, Offset, State); + break; + + case EfiUsbPortSuspend: + State |= PORTSC_SUSPEND; + EhcWriteOpReg (Ehc, Offset, State); + break; + + case EfiUsbPortReset: + // + // Make sure Host Controller not halt before reset it + // + if (EhcIsHalt (Ehc)) { + Status = EhcRunHC (Ehc, EHC_GENERIC_TIMEOUT); + + if (EFI_ERROR (Status)) { + break; + } + } + + // + // Set one to PortReset bit must also set zero to PortEnable bit + // + State |= PORTSC_RESET; + State &= ~PORTSC_ENABLED; + EhcWriteOpReg (Ehc, Offset, State); + break; + + case EfiUsbPortPower: + // + // Not supported, ignore the operation + // + Status = EFI_SUCCESS; + break; + + case EfiUsbPortOwner: + State |= PORTSC_OWNER; + EhcWriteOpReg (Ehc, Offset, State); + break; + + default: + Status = EFI_INVALID_PARAMETER; + } + +ON_EXIT: + return Status; +} + +/** + Retrieves the current status of a USB root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI. + @param PortNumber The root hub port to retrieve the state from. + @param PortStatus Variable to receive the port state. + + @retval EFI_SUCCESS The status of the USB root hub port specified. + by PortNumber was returned in PortStatus. + @retval EFI_INVALID_PARAMETER PortNumber is invalid. + +**/ +EFI_STATUS +EFIAPI +EhcGetRootHubPortStatus ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus + ) +{ + PEI_USB2_HC_DEV *Ehc; + UINT32 Offset; + UINT32 State; + UINT32 TotalPort; + UINTN Index; + UINTN MapSize; + EFI_STATUS Status; + + if (PortStatus == NULL) { + return EFI_INVALID_PARAMETER; + } + + Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS(This); + Status = EFI_SUCCESS; + + TotalPort = (Ehc->HcStructParams & HCSP_NPORTS); + + if (PortNumber >= TotalPort) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Offset = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber)); + PortStatus->PortStatus = 0; + PortStatus->PortChangeStatus = 0; + + State = EhcReadOpReg (Ehc, Offset); + + // + // Identify device speed. If in K state, it is low speed. + // If the port is enabled after reset, the device is of + // high speed. The USB bus driver should retrieve the actual + // port speed after reset. + // + if (EHC_BIT_IS_SET (State, PORTSC_LINESTATE_K)) { + PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED; + + } else if (EHC_BIT_IS_SET (State, PORTSC_ENABLED)) { + PortStatus->PortStatus |= USB_PORT_STAT_HIGH_SPEED; + } + + // + // Convert the EHCI port/port change state to UEFI status + // + MapSize = sizeof (mUsbPortStateMap) / sizeof (USB_PORT_STATE_MAP); + + for (Index = 0; Index < MapSize; Index++) { + if (EHC_BIT_IS_SET (State, mUsbPortStateMap[Index].HwState)) { + PortStatus->PortStatus = (UINT16) (PortStatus->PortStatus | mUsbPortStateMap[Index].UefiState); + } + } + + MapSize = sizeof (mUsbPortChangeMap) / sizeof (USB_PORT_STATE_MAP); + + for (Index = 0; Index < MapSize; Index++) { + if (EHC_BIT_IS_SET (State, mUsbPortChangeMap[Index].HwState)) { + PortStatus->PortChangeStatus = (UINT16) (PortStatus->PortChangeStatus | mUsbPortChangeMap[Index].UefiState); + } + } + +ON_EXIT: + return Status; +} + +/** + Submits control transfer to a target USB device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI. + @param DeviceAddress The target device address. + @param DeviceSpeed Target device speed. + @param MaximumPacketLength Maximum packet size the default control transfer + endpoint is capable of sending or receiving. + @param Request USB device request to send. + @param TransferDirection Specifies the data direction for the data stage. + @param Data Data buffer to be transmitted or received from USB device. + @param DataLength The size (in bytes) of the data buffer. + @param TimeOut Indicates the maximum timeout, in millisecond. + @param Translator Transaction translator to be used by this device. + @param TransferResult Return the result of this control transfer. + + @retval EFI_SUCCESS Transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resources. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_TIMEOUT Transfer failed due to timeout. + @retval EFI_DEVICE_ERROR Transfer failed due to host controller or device error. + +**/ +EFI_STATUS +EFIAPI +EhcControlTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 DeviceAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + PEI_USB2_HC_DEV *Ehc; + PEI_URB *Urb; + UINT8 Endpoint; + EFI_STATUS Status; + + // + // Validate parameters + // + if ((Request == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection != EfiUsbDataIn) && + (TransferDirection != EfiUsbDataOut) && + (TransferDirection != EfiUsbNoData)) { + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection == EfiUsbNoData) && + ((Data != NULL) || (*DataLength != 0))) { + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection != EfiUsbNoData) && + ((Data == NULL) || (*DataLength == 0))) { + return EFI_INVALID_PARAMETER; + } + + if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) && + (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) { + return EFI_INVALID_PARAMETER; + } + + + if ((DeviceSpeed == EFI_USB_SPEED_LOW) || + ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) || + ((EFI_USB_SPEED_HIGH == DeviceSpeed) && (MaximumPacketLength > 512))) { + return EFI_INVALID_PARAMETER; + } + + Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This); + + Status = EFI_DEVICE_ERROR; + *TransferResult = EFI_USB_ERR_SYSTEM; + + if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) { + EhcAckAllInterrupt (Ehc); + goto ON_EXIT; + } + + EhcAckAllInterrupt (Ehc); + + // + // Create a new URB, insert it into the asynchronous + // schedule list, then poll the execution status. + // + // + // Encode the direction in address, although default control + // endpoint is bidirectional. EhcCreateUrb expects this + // combination of Ep addr and its direction. + // + Endpoint = (UINT8) (0 | ((TransferDirection == EfiUsbDataIn) ? 0x80 : 0)); + Urb = EhcCreateUrb ( + Ehc, + DeviceAddress, + Endpoint, + DeviceSpeed, + 0, + MaximumPacketLength, + Translator, + EHC_CTRL_TRANSFER, + Request, + Data, + *DataLength, + NULL, + NULL, + 1 + ); + + if (Urb == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + EhcLinkQhToAsync (Ehc, Urb->Qh); + Status = EhcExecTransfer (Ehc, Urb, TimeOut); + EhcUnlinkQhFromAsync (Ehc, Urb->Qh); + + // + // Get the status from URB. The result is updated in EhcCheckUrbResult + // which is called by EhcExecTransfer + // + *TransferResult = Urb->Result; + *DataLength = Urb->Completed; + + if (*TransferResult == EFI_USB_NOERROR) { + Status = EFI_SUCCESS; + } + + EhcAckAllInterrupt (Ehc); + EhcFreeUrb (Ehc, Urb); + +ON_EXIT: + return Status; +} + +/** + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS PPI successfully installed. + +**/ +EFI_STATUS +EFIAPI +EhcPeimEntry ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + PEI_USB_CONTROLLER_PPI *ChipSetUsbControllerPpi; + EFI_STATUS Status; + UINT8 Index; + UINTN ControllerType; + UINTN BaseAddress; + UINTN MemPages; + PEI_USB2_HC_DEV *EhcDev; + EFI_PHYSICAL_ADDRESS TempPtr; + + // + // Shadow this PEIM to run from memory + // + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { + return EFI_SUCCESS; + } + + Status = PeiServicesLocatePpi ( + &gPeiUsbControllerPpiGuid, + 0, + NULL, + (VOID **) &ChipSetUsbControllerPpi + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Index = 0; + while (TRUE) { + Status = ChipSetUsbControllerPpi->GetUsbController ( + (EFI_PEI_SERVICES **) PeiServices, + ChipSetUsbControllerPpi, + Index, + &ControllerType, + &BaseAddress + ); + // + // When status is error, meant no controller is found + // + if (EFI_ERROR (Status)) { + break; + } + + // + // This PEIM is for UHC type controller. + // + if (ControllerType != PEI_EHCI_CONTROLLER) { + Index++; + continue; + } + + MemPages = sizeof (PEI_USB2_HC_DEV) / PAGESIZE + 1; + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + MemPages, + &TempPtr + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem((VOID *)(UINTN)TempPtr, MemPages*PAGESIZE); + EhcDev = (PEI_USB2_HC_DEV *) ((UINTN) TempPtr); + + EhcDev->Signature = USB2_HC_DEV_SIGNATURE; + + EhcDev->UsbHostControllerBaseAddress = (UINT32) BaseAddress; + + + EhcDev->HcStructParams = EhcReadCapRegister (EhcDev, EHC_HCSPARAMS_OFFSET); + EhcDev->HcCapParams = EhcReadCapRegister (EhcDev, EHC_HCCPARAMS_OFFSET); + EhcDev->CapLen = EhcReadCapRegister (EhcDev, EHC_CAPLENGTH_OFFSET) & 0x0FF; + // + // Initialize Uhc's hardware + // + Status = InitializeUsbHC (EhcDev); + if (EFI_ERROR (Status)) { + return Status; + } + + EhcDev->Usb2HostControllerPpi.ControlTransfer = EhcControlTransfer; + EhcDev->Usb2HostControllerPpi.BulkTransfer = EhcBulkTransfer; + EhcDev->Usb2HostControllerPpi.GetRootHubPortNumber = EhcGetRootHubPortNumber; + EhcDev->Usb2HostControllerPpi.GetRootHubPortStatus = EhcGetRootHubPortStatus; + EhcDev->Usb2HostControllerPpi.SetRootHubPortFeature = EhcSetRootHubPortFeature; + EhcDev->Usb2HostControllerPpi.ClearRootHubPortFeature = EhcClearRootHubPortFeature; + + EhcDev->PpiDescriptor.Flags = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST); + EhcDev->PpiDescriptor.Guid = &gPeiUsb2HostControllerPpiGuid; + EhcDev->PpiDescriptor.Ppi = &EhcDev->Usb2HostControllerPpi; + + Status = PeiServicesInstallPpi (&EhcDev->PpiDescriptor); + if (EFI_ERROR (Status)) { + Index++; + continue; + } + + Index++; + } + + return EFI_SUCCESS; +} + +/** + @param EhcDev EHCI Device. + + @retval EFI_SUCCESS EHCI successfully initialized. + @retval EFI_ABORTED EHCI was failed to be initialized. + +**/ +EFI_STATUS +InitializeUsbHC ( + IN PEI_USB2_HC_DEV *EhcDev + ) +{ + EFI_STATUS Status; + + + EhcResetHC (EhcDev, EHC_RESET_TIMEOUT); + + Status = EhcInitHC (EhcDev); + + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.h b/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.h new file mode 100644 index 0000000000..fa1671bd8f --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.h @@ -0,0 +1,227 @@ +/** @file +Private Header file for Usb Host Controller PEIM + +Copyright (c) 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _RECOVERY_EHC_H_ +#define _RECOVERY_EHC_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef struct _PEI_USB2_HC_DEV PEI_USB2_HC_DEV; + +#define EFI_LIST_ENTRY LIST_ENTRY + +#include "UsbHcMem.h" +#include "EhciReg.h" +#include "EhciUrb.h" +#include "EhciSched.h" + +#define EFI_USB_SPEED_FULL 0x0000 +#define EFI_USB_SPEED_LOW 0x0001 +#define EFI_USB_SPEED_HIGH 0x0002 + +#define PAGESIZE 4096 + +#define EHC_1_MICROSECOND 1 +#define EHC_1_MILLISECOND (1000 * EHC_1_MICROSECOND) +#define EHC_1_SECOND (1000 * EHC_1_MILLISECOND) + +// +// EHCI register operation timeout, set by experience +// +#define EHC_RESET_TIMEOUT (1 * EHC_1_SECOND) +#define EHC_GENERIC_TIMEOUT (10 * EHC_1_MILLISECOND) + + +// +// Wait for roothub port power stable, refers to Spec[EHCI1.0-2.3.9] +// +#define EHC_ROOT_PORT_RECOVERY_STALL (20 * EHC_1_MILLISECOND) + +// +// Sync and Async transfer polling interval, set by experience, +// and the unit of Async is 100us, means 50ms as interval. +// +#define EHC_SYNC_POLL_INTERVAL (6 * EHC_1_MILLISECOND) + +#define EHC_ASYNC_POLL_INTERVAL (50 * 10000U) + +// +//Iterate through the doule linked list. NOT delete safe +// +#define EFI_LIST_FOR_EACH(Entry, ListHead) \ + for(Entry = (ListHead)->ForwardLink; Entry != (ListHead); Entry = Entry->ForwardLink) + +// +//Iterate through the doule linked list. This is delete-safe. +//Don't touch NextEntry +// +#define EFI_LIST_FOR_EACH_SAFE(Entry, NextEntry, ListHead) \ + for(Entry = (ListHead)->ForwardLink, NextEntry = Entry->ForwardLink;\ + Entry != (ListHead); Entry = NextEntry, NextEntry = Entry->ForwardLink) + +#define EFI_LIST_CONTAINER(Entry, Type, Field) BASE_CR(Entry, Type, Field) + + +#define EHC_LOW_32BIT(Addr64) ((UINT32)(((UINTN)(Addr64)) & 0XFFFFFFFF)) +#define EHC_HIGH_32BIT(Addr64) ((UINT32)(RShiftU64((UINTN)(Addr64), 32) & 0XFFFFFFFF)) +#define EHC_BIT_IS_SET(Data, Bit) ((BOOLEAN)(((Data) & (Bit)) == (Bit))) + +#define EHC_REG_BIT_IS_SET(Ehc, Offset, Bit) \ + (EHC_BIT_IS_SET(EhcReadOpReg ((Ehc), (Offset)), (Bit))) + +#define USB2_HC_DEV_SIGNATURE SIGNATURE_32 ('e', 'h', 'c', 'i') + +struct _PEI_USB2_HC_DEV { + UINTN Signature; + PEI_USB2_HOST_CONTROLLER_PPI Usb2HostControllerPpi; + EFI_PEI_PPI_DESCRIPTOR PpiDescriptor; + UINT32 UsbHostControllerBaseAddress; + PEI_URB *Urb; + USBHC_MEM_POOL *MemPool; + + // + // Schedule data shared between asynchronous and periodic + // transfers: + // ShortReadStop, as its name indicates, is used to terminate + // the short read except the control transfer. EHCI follows + // the alternative next QTD point when a short read happens. + // For control transfer, even the short read happens, try the + // status stage. + // + PEI_EHC_QTD *ShortReadStop; + EFI_EVENT PollTimer; + + // + // Asynchronous(bulk and control) transfer schedule data: + // ReclaimHead is used as the head of the asynchronous transfer + // list. It acts as the reclamation header. + // + PEI_EHC_QH *ReclaimHead; + + // + // Peroidic (interrupt) transfer schedule data: + // + VOID *PeriodFrame; // Mapped as common buffer + VOID *PeriodFrameHost; + VOID *PeriodFrameMap; + + PEI_EHC_QH *PeriodOne; + EFI_LIST_ENTRY AsyncIntTransfers; + + // + // EHCI configuration data + // + UINT32 HcStructParams; // Cache of HC structure parameter, EHC_HCSPARAMS_OFFSET + UINT32 HcCapParams; // Cache of HC capability parameter, HCCPARAMS + UINT32 CapLen; // Capability length + UINT32 High32bitAddr; +}; + +#define PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS(a) CR (a, PEI_USB2_HC_DEV, Usb2HostControllerPpi, USB2_HC_DEV_SIGNATURE) + +/** + @param EhcDev EHCI Device. + + @retval EFI_SUCCESS EHCI successfully initialized. + @retval EFI_ABORTED EHCI was failed to be initialized. + +**/ +EFI_STATUS +InitializeUsbHC ( + IN PEI_USB2_HC_DEV *EhcDev + ); + +/** + Initialize the memory management pool for the host controller. + + @param Ehc The EHCI device. + @param Check4G Whether the host controller requires allocated memory + from one 4G address space. + @param Which4G The 4G memory area each memory allocated should be from. + + @retval EFI_SUCCESS The memory pool is initialized. + @retval EFI_OUT_OF_RESOURCE Fail to init the memory pool. + +**/ +USBHC_MEM_POOL * +UsbHcInitMemPool ( + IN PEI_USB2_HC_DEV *Ehc, + IN BOOLEAN Check4G, + IN UINT32 Which4G + ) +; + +/** + Release the memory management pool. + + @param Pool The USB memory pool to free. + + @retval EFI_DEVICE_ERROR Fail to free the memory pool. + @retval EFI_SUCCESS The memory pool is freed. + +**/ +EFI_STATUS +UsbHcFreeMemPool ( + IN USBHC_MEM_POOL *Pool + ) +; + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Ehc The EHCI device. + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +UsbHcAllocateMem ( + IN PEI_USB2_HC_DEV *Ehc, + IN USBHC_MEM_POOL *Pool, + IN UINTN Size + ) +; + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + +**/ +VOID +UsbHcFreeMem ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +; + +#endif diff --git a/MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf b/MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf new file mode 100644 index 0000000000..f10ad49e88 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf @@ -0,0 +1,66 @@ +## @file +# Component description file for EhcPeim PEIM to produce gPeiUsb2HostControllerPpiGuid +# based on gPeiUsbControllerPpiGuid which is used to enable recovery function from USB Drivers. +# +# Copyright (c) 2010, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions +# of the BSD License which accompanies this distribution. The +# full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EhciPei + FILE_GUID = BAB4F20F-0981-4b5f-A047-6EF83BEEAB3C + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = EhcPeimEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + EhcPeim.c + EhcPeim.h + EhciUrb.c + EhciSched.c + UsbHcMem.c + EhciReg.h + EhciSched.h + EhciUrb.h + UsbHcMem.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + IoLib + TimerLib + BaseMemoryLib + PeimEntryPoint + PeiServicesLib + + +[Ppis] + gPeiUsb2HostControllerPpiGuid # PPI ALWAYS_PRODUCED + gPeiUsbControllerPpiGuid # PPI ALWAYS_CONSUMED + + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid AND gPeiUsbControllerPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid + + diff --git a/MdeModulePkg/Bus/Pci/EhciPei/EhciReg.h b/MdeModulePkg/Bus/Pci/EhciPei/EhciReg.h new file mode 100644 index 0000000000..34c61d8a94 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciPei/EhciReg.h @@ -0,0 +1,310 @@ +/** @file +Private Header file for Usb Host Controller PEIM + +Copyright (c) 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_EHCI_REG_H_ +#define _EFI_EHCI_REG_H_ + + + +// +// Capability register offset +// +#define EHC_CAPLENGTH_OFFSET 0 // Capability register length offset +#define EHC_HCSPARAMS_OFFSET 0x04 // Structural Parameters 04-07h +#define EHC_HCCPARAMS_OFFSET 0x08 // Capability parameters offset + +// +// Capability register bit definition +// +#define HCSP_NPORTS 0x0F // Number of root hub port +#define HCCP_64BIT 0x01 // 64-bit addressing capability + +// +// Operational register offset +// +#define EHC_USBCMD_OFFSET 0x0 // USB command register offset +#define EHC_USBSTS_OFFSET 0x04 // Statue register offset +#define EHC_USBINTR_OFFSET 0x08 // USB interrutp offset +#define EHC_FRINDEX_OFFSET 0x0C // Frame index offset +#define EHC_CTRLDSSEG_OFFSET 0x10 // Control data structure segment offset +#define EHC_FRAME_BASE_OFFSET 0x14 // Frame list base address offset +#define EHC_ASYNC_HEAD_OFFSET 0x18 // Next asynchronous list address offset +#define EHC_CONFIG_FLAG_OFFSET 0x40 // Configure flag register offset +#define EHC_PORT_STAT_OFFSET 0x44 // Port status/control offset + +#define EHC_FRAME_LEN 1024 + +// +// Register bit definition +// +#define CONFIGFLAG_ROUTE_EHC 0x01 // Route port to EHC + +#define USBCMD_RUN 0x01 // Run/stop +#define USBCMD_RESET 0x02 // Start the host controller reset +#define USBCMD_ENABLE_PERIOD 0x10 // Enable periodic schedule +#define USBCMD_ENABLE_ASYNC 0x20 // Enable asynchronous schedule +#define USBCMD_IAAD 0x40 // Interrupt on async advance doorbell + +#define USBSTS_IAA 0x20 // Interrupt on async advance +#define USBSTS_PERIOD_ENABLED 0x4000 // Periodic schedule status +#define USBSTS_ASYNC_ENABLED 0x8000 // Asynchronous schedule status +#define USBSTS_HALT 0x1000 // Host controller halted +#define USBSTS_SYS_ERROR 0x10 // Host system error +#define USBSTS_INTACK_MASK 0x003F // Mask for the interrupt ACK, the WC + // (write clean) bits in USBSTS register + +#define PORTSC_CONN 0x01 // Current Connect Status +#define PORTSC_CONN_CHANGE 0x02 // Connect Status Change +#define PORTSC_ENABLED 0x04 // Port Enable / Disable +#define PORTSC_ENABLE_CHANGE 0x08 // Port Enable / Disable Change +#define PORTSC_OVERCUR 0x10 // Over current Active +#define PORTSC_OVERCUR_CHANGE 0x20 // Over current Change +#define PORSTSC_RESUME 0x40 // Force Port Resume +#define PORTSC_SUSPEND 0x80 // Port Suspend State +#define PORTSC_RESET 0x100 // Port Reset +#define PORTSC_LINESTATE_K 0x400 // Line Status K-state +#define PORTSC_LINESTATE_J 0x800 // Line Status J-state +#define PORTSC_POWER 0x1000 // Port Power +#define PORTSC_OWNER 0x2000 // Port Owner +#define PORTSC_CHANGE_MASK 0x2A // Mask of the port change bits, + // they are WC (write clean) +// +// PCI Configuration Registers +// +#define EHC_BAR_INDEX 0 // how many bytes away from USB_BASE to 0x10 + +#define EHC_LINK_TERMINATED(Link) (((Link) & 0x01) != 0) + +#define EHC_ADDR(High, QhHw32) \ + ((VOID *) (UINTN) (LShiftU64 ((High), 32) | ((QhHw32) & 0xFFFFFFF0))) + +#define EHCI_IS_DATAIN(EndpointAddr) EHC_BIT_IS_SET((EndpointAddr), 0x80) + +// +// Structure to map the hardware port states to the +// UEFI's port states. +// +typedef struct { + UINT16 HwState; + UINT16 UefiState; +} USB_PORT_STATE_MAP; + +// +// Ehci Data and Ctrl Structures +// +#pragma pack(1) +typedef struct { + UINT8 Pi; + UINT8 SubClassCode; + UINT8 BaseCode; +} USB_CLASSC; +#pragma pack() + + +/** + Read EHCI capability register. + + @param Ehc The EHCI device. + @param Offset Capability register address. + + @retval the register content read. + +**/ +UINT32 +EhcReadCapRegister ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Offset + ) +; + +/** + Read Ehc Operation register. + + @param Ehc The EHCI device. + @param Offset The operation register offset. + + @retval the register content read. + +**/ +UINT32 +EhcReadOpReg ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Offset + ) +; + +/** + Write the data to the EHCI operation register. + + @param Ehc The EHCI device. + @param Offset EHCI operation register offset. + @param Data The data to write. + +**/ +VOID +EhcWriteOpReg ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Offset, + IN UINT32 Data + ) +; + +/** + Stop the legacy USB SMI support. + + @param Ehc The EHCI device. + +**/ +VOID +EhcClearLegacySupport ( + IN PEI_USB2_HC_DEV *Ehc + ) +; + +/** + Set door bell and wait it to be ACKed by host controller. + This function is used to synchronize with the hardware. + + @param Ehc The EHCI device. + @param Timeout The time to wait before abort (in millisecond, ms). + + @retval EFI_TIMEOUT Time out happened while waiting door bell to set. + @retval EFI_SUCCESS Synchronized with the hardware. + +**/ +EFI_STATUS +EhcSetAndWaitDoorBell ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Timeout + ) +; + +/** + Clear all the interrutp status bits, these bits + are Write-Clean. + + @param Ehc The EHCI device. + +**/ +VOID +EhcAckAllInterrupt ( + IN PEI_USB2_HC_DEV *Ehc + ) +; + +/** + Check whether Ehc is halted. + + @param Ehc The EHCI device. + + @retval TRUE The controller is halted. + @retval FALSE The controller isn't halted. + +**/ +BOOLEAN +EhcIsHalt ( + IN PEI_USB2_HC_DEV *Ehc + ) +; + +/** + Check whether system error occurred. + + @param Ehc The EHCI device. + + @retval TRUE System error happened. + @retval FALSE No system error. + +**/ +BOOLEAN +EhcIsSysError ( + IN PEI_USB2_HC_DEV *Ehc + ) +; + +/** + Reset the host controller. + + @param Ehc The EHCI device. + @param Timeout Time to wait before abort (in millisecond, ms). + + @retval EFI_TIMEOUT The transfer failed due to time out. + @retval Others Failed to reset the host. + +**/ +EFI_STATUS +EhcResetHC ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Timeout + ) +; + +/** + Halt the host controller. + + @param Ehc The EHCI device. + @param Timeout Time to wait before abort. + + @retval EFI_TIMEOUT Failed to halt the controller before Timeout. + @retval EFI_SUCCESS The EHCI is halt. + +**/ +EFI_STATUS +EhcHaltHC ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Timeout + ) +; + +/** + Set the EHCI to run + + @param Ehc The EHCI device. + @param Timeout Time to wait before abort. + + @retval EFI_SUCCESS The EHCI is running. + @retval Others Failed to set the EHCI to run. + +**/ +EFI_STATUS +EhcRunHC ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT32 Timeout + ) +; + +/** + Initialize the HC hardware. + EHCI spec lists the five things to do to initialize the hardware. + 1. Program CTRLDSSEGMENT. + 2. Set USBINTR to enable interrupts. + 3. Set periodic list base. + 4. Set USBCMD, interrupt threshold, frame list size etc. + 5. Write 1 to CONFIGFLAG to route all ports to EHCI. + + @param Ehc The EHCI device. + + @retval EFI_SUCCESS The EHCI has come out of halt state. + @retval EFI_TIMEOUT Time out happened. + +**/ +EFI_STATUS +EhcInitHC ( + IN PEI_USB2_HC_DEV *Ehc + ) +; + +#endif diff --git a/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.c b/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.c new file mode 100644 index 0000000000..d153aa39dc --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.c @@ -0,0 +1,873 @@ +/** @file +PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid +which is used to enable recovery function from USB Drivers. + +Copyright (c) 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "EhcPeim.h" + +/** + Create helper QTD/QH for the EHCI device. + + @param Ehc The EHCI device. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH. + @retval EFI_SUCCESS Helper QH/QTD are created. + +**/ +EFI_STATUS +EhcCreateHelpQ ( + IN PEI_USB2_HC_DEV *Ehc + ) +{ + USB_ENDPOINT Ep; + PEI_EHC_QH *Qh; + QH_HW *QhHw; + PEI_EHC_QTD *Qtd; + + // + // Create an inactive Qtd to terminate the short packet read. + // + Qtd = EhcCreateQtd (Ehc, NULL, 0, QTD_PID_INPUT, 0, 64); + + if (Qtd == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Qtd->QtdHw.Status = QTD_STAT_HALTED; + Ehc->ShortReadStop = Qtd; + + // + // Create a QH to act as the EHC reclamation header. + // Set the header to loopback to itself. + // + Ep.DevAddr = 0; + Ep.EpAddr = 1; + Ep.Direction = EfiUsbDataIn; + Ep.DevSpeed = EFI_USB_SPEED_HIGH; + Ep.MaxPacket = 64; + Ep.HubAddr = 0; + Ep.HubPort = 0; + Ep.Toggle = 0; + Ep.Type = EHC_BULK_TRANSFER; + Ep.PollRate = 1; + + Qh = EhcCreateQh (Ehc, &Ep); + + if (Qh == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + QhHw = &Qh->QhHw; + QhHw->HorizonLink = QH_LINK (QhHw, EHC_TYPE_QH, FALSE); + QhHw->Status = QTD_STAT_HALTED; + QhHw->ReclaimHead = 1; + Ehc->ReclaimHead = Qh; + + // + // Create a dummy QH to act as the terminator for periodical schedule + // + Ep.EpAddr = 2; + Ep.Type = EHC_INT_TRANSFER_SYNC; + + Qh = EhcCreateQh (Ehc, &Ep); + + if (Qh == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Qh->QhHw.Status = QTD_STAT_HALTED; + Ehc->PeriodOne = Qh; + + return EFI_SUCCESS; +} + +/** + Initialize the schedule data structure such as frame list. + + @param Ehc The EHCI device to init schedule data for. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data. + @retval EFI_SUCCESS The schedule data is initialized. + +**/ +EFI_STATUS +EhcInitSched ( + IN PEI_USB2_HC_DEV *Ehc + ) +{ + EFI_PHYSICAL_ADDRESS PhyAddr; + VOID *Map; + UINTN Index; + UINT32 *Desc; + EFI_STATUS Status; + + // + // First initialize the periodical schedule data: + // 1. Allocate and map the memory for the frame list + // 2. Create the help QTD/QH + // 3. Initialize the frame entries + // 4. Set the frame list register + // + // + // The Frame List ocupies 4K bytes, + // and must be aligned on 4-Kbyte boundaries. + // + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + 1, + &PhyAddr + ); + + Map = NULL; + Ehc->PeriodFrameHost = (VOID *)(UINTN)PhyAddr; + Ehc->PeriodFrame = (VOID *)(UINTN)PhyAddr; + Ehc->PeriodFrameMap = Map; + Ehc->High32bitAddr = EHC_HIGH_32BIT (PhyAddr); + + // + // Init memory pool management then create the helper + // QTD/QH. If failed, previously allocated resources + // will be freed by EhcFreeSched + // + Ehc->MemPool = UsbHcInitMemPool ( + Ehc, + EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT), + Ehc->High32bitAddr + ); + + if (Ehc->MemPool == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = EhcCreateHelpQ (Ehc); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Initialize the frame list entries then set the registers + // + Desc = (UINT32 *) Ehc->PeriodFrame; + + for (Index = 0; Index < EHC_FRAME_LEN; Index++) { + Desc[Index] = QH_LINK (Ehc->PeriodOne, EHC_TYPE_QH, FALSE); + } + + EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (Ehc->PeriodFrame)); + + // + // Second initialize the asynchronous schedule: + // Only need to set the AsynListAddr register to + // the reclamation header + // + EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (Ehc->ReclaimHead)); + return EFI_SUCCESS; +} + +/** + Free the schedule data. It may be partially initialized. + + @param Ehc The EHCI device. + +**/ +VOID +EhcFreeSched ( + IN PEI_USB2_HC_DEV *Ehc + ) +{ + EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0); + EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0); + + if (Ehc->PeriodOne != NULL) { + UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH)); + Ehc->PeriodOne = NULL; + } + + if (Ehc->ReclaimHead != NULL) { + UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH)); + Ehc->ReclaimHead = NULL; + } + + if (Ehc->ShortReadStop != NULL) { + UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (PEI_EHC_QTD)); + Ehc->ShortReadStop = NULL; + } + + if (Ehc->MemPool != NULL) { + UsbHcFreeMemPool (Ehc->MemPool); + Ehc->MemPool = NULL; + } + + if (Ehc->PeriodFrame != NULL) { + Ehc->PeriodFrame = NULL; + } +} + +/** + Link the queue head to the asynchronous schedule list. + UEFI only supports one CTRL/BULK transfer at a time + due to its interfaces. This simplifies the AsynList + management: A reclamation header is always linked to + the AsyncListAddr, the only active QH is appended to it. + + @param Ehc The EHCI device. + @param Qh The queue head to link. + +**/ +VOID +EhcLinkQhToAsync ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_EHC_QH *Qh + ) +{ + PEI_EHC_QH *Head; + + // + // Append the queue head after the reclaim header, then + // fix the hardware visiable parts (EHCI R1.0 page 72). + // ReclaimHead is always linked to the EHCI's AsynListAddr. + // + Head = Ehc->ReclaimHead; + + Qh->NextQh = Head->NextQh; + Head->NextQh = Qh; + + Qh->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);; + Head->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE); +} + +/** + Unlink a queue head from the asynchronous schedule list. + Need to synchronize with hardware. + + @param Ehc The EHCI device. + @param Qh The queue head to unlink. + +**/ +VOID +EhcUnlinkQhFromAsync ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_EHC_QH *Qh + ) +{ + PEI_EHC_QH *Head; + EFI_STATUS Status; + + ASSERT (Ehc->ReclaimHead->NextQh == Qh); + + // + // Remove the QH from reclamation head, then update the hardware + // visiable part: Only need to loopback the ReclaimHead. The Qh + // is pointing to ReclaimHead (which is staill in the list). + // + Head = Ehc->ReclaimHead; + + Head->NextQh = Qh->NextQh; + Qh->NextQh = NULL; + + Head->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE); + + // + // Set and wait the door bell to synchronize with the hardware + // + Status = EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT); + + return; +} + +/** + Link a queue head for interrupt transfer to the periodic + schedule frame list. This code is very much the same as + that in UHCI. + + @param Ehc The EHCI device. + @param Qh The queue head to link. + +**/ +VOID +EhcLinkQhToPeriod ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_EHC_QH *Qh + ) +{ + UINT32 *Frames; + UINTN Index; + PEI_EHC_QH *Prev; + PEI_EHC_QH *Next; + + Frames = Ehc->PeriodFrame; + + for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) { + // + // First QH can't be NULL because we always keep PeriodOne + // heads on the frame list + // + ASSERT (!EHC_LINK_TERMINATED (Frames[Index])); + Next = EHC_ADDR (Ehc->High32bitAddr, Frames[Index]); + Prev = NULL; + + // + // Now, insert the queue head (Qh) into this frame: + // 1. Find a queue head with the same poll interval, just insert + // Qh after this queue head, then we are done. + // + // 2. Find the position to insert the queue head into: + // Previous head's interval is bigger than Qh's + // Next head's interval is less than Qh's + // Then, insert the Qh between then + // + while (Next->Interval > Qh->Interval) { + Prev = Next; + Next = Next->NextQh; + } + + ASSERT (Next != NULL); + + // + // The entry may have been linked into the frame by early insertation. + // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh + // with Qh.Interval == 8 on the frame. If so, we are done with this frame. + // It isn't necessary to compare all the QH with the same interval to + // Qh. This is because if there is other QH with the same interval, Qh + // should has been inserted after that at Frames[0] and at Frames[0] it is + // impossible for (Next == Qh) + // + if (Next == Qh) { + continue; + } + + if (Next->Interval == Qh->Interval) { + // + // If there is a QH with the same interval, it locates at + // Frames[0], and we can simply insert it after this QH. We + // are all done. + // + ASSERT ((Index == 0) && (Qh->NextQh == NULL)); + + Prev = Next; + Next = Next->NextQh; + + Qh->NextQh = Next; + Prev->NextQh = Qh; + + Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink; + Prev->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE); + break; + } + + // + // OK, find the right position, insert it in. If Qh's next + // link has already been set, it is in position. This is + // guarranted by 2^n polling interval. + // + if (Qh->NextQh == NULL) { + Qh->NextQh = Next; + Qh->QhHw.HorizonLink = QH_LINK (Next, EHC_TYPE_QH, FALSE); + } + + if (Prev == NULL) { + Frames[Index] = QH_LINK (Qh, EHC_TYPE_QH, FALSE); + } else { + Prev->NextQh = Qh; + Prev->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE); + } + } +} + +/** + Unlink an interrupt queue head from the periodic + schedule frame list. + + @param Ehc The EHCI device. + @param Qh The queue head to unlink. + +**/ +VOID +EhcUnlinkQhFromPeriod ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_EHC_QH *Qh + ) +{ + UINT32 *Frames; + UINTN Index; + PEI_EHC_QH *Prev; + PEI_EHC_QH *This; + + Frames = Ehc->PeriodFrame; + + for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) { + // + // Frame link can't be NULL because we always keep PeroidOne + // on the frame list + // + ASSERT (!EHC_LINK_TERMINATED (Frames[Index])); + This = EHC_ADDR (Ehc->High32bitAddr, Frames[Index]); + Prev = NULL; + + // + // Walk through the frame's QH list to find the + // queue head to remove + // + while ((This != NULL) && (This != Qh)) { + Prev = This; + This = This->NextQh; + } + + // + // Qh may have already been unlinked from this frame + // by early action. See the comments in EhcLinkQhToPeriod. + // + if (This == NULL) { + continue; + } + + if (Prev == NULL) { + // + // Qh is the first entry in the frame + // + Frames[Index] = Qh->QhHw.HorizonLink; + } else { + Prev->NextQh = Qh->NextQh; + Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink; + } + } +} + +/** + Check the URB's execution result and update the URB's + result accordingly. + + @param Ehc The EHCI device. + @param Urb The URB to check result. + + @retval TRUE URB transfer is finialized. + @retval FALSE URB transfer is not finialized. + +**/ +BOOLEAN +EhcCheckUrbResult ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_URB *Urb + ) +{ + EFI_LIST_ENTRY *Entry; + PEI_EHC_QTD *Qtd; + QTD_HW *QtdHw; + UINT8 State; + BOOLEAN Finished; + + ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL)); + + Finished = TRUE; + Urb->Completed = 0; + + Urb->Result = EFI_USB_NOERROR; + + if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) { + Urb->Result |= EFI_USB_ERR_SYSTEM; + goto ON_EXIT; + } + + EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) { + Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList); + QtdHw = &Qtd->QtdHw; + State = (UINT8) QtdHw->Status; + + if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) { + // + // EHCI will halt the queue head when met some error. + // If it is halted, the result of URB is finialized. + // + if ((State & QTD_STAT_ERR_MASK) == 0) { + Urb->Result |= EFI_USB_ERR_STALL; + } + + if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) { + Urb->Result |= EFI_USB_ERR_BABBLE; + } + + if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) { + Urb->Result |= EFI_USB_ERR_BUFFER; + } + + if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR) && (QtdHw->ErrCnt == 0)) { + Urb->Result |= EFI_USB_ERR_TIMEOUT; + } + + Finished = TRUE; + goto ON_EXIT; + + } else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) { + // + // The QTD is still active, no need to check furthur. + // + Urb->Result |= EFI_USB_ERR_NOTEXECUTE; + + Finished = FALSE; + goto ON_EXIT; + + } else { + // + // This QTD is finished OK or met short packet read. Update the + // transfer length if it isn't a setup. + // + if (QtdHw->Pid != QTD_PID_SETUP) { + Urb->Completed += Qtd->DataLen - QtdHw->TotalBytes; + } + + if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) { + //EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE)); + + // + // Short packet read condition. If it isn't a setup transfer, + // no need to check furthur: the queue head will halt at the + // ShortReadStop. If it is a setup transfer, need to check the + // Status Stage of the setup transfer to get the finial result + // + if (QtdHw->AltNext == QTD_LINK (Ehc->ShortReadStop, FALSE)) { + + Finished = TRUE; + goto ON_EXIT; + } + } + } + } + +ON_EXIT: + // + // Return the data toggle set by EHCI hardware, bulk and interrupt + // transfer will use this to initialize the next transaction. For + // Control transfer, it always start a new data toggle sequence for + // new transfer. + // + // NOTICE: don't move DT update before the loop, otherwise there is + // a race condition that DT is wrong. + // + Urb->DataToggle = (UINT8) Urb->Qh->QhHw.DataToggle; + + return Finished; +} + +/** + Execute the transfer by polling the URB. This is a synchronous operation. + + @param Ehc The EHCI device. + @param Urb The URB to execute. + @param TimeOut The time to wait before abort, in millisecond. + + @retval EFI_DEVICE_ERROR The transfer failed due to transfer error. + @retval EFI_TIMEOUT The transfer failed due to time out. + @retval EFI_SUCCESS The transfer finished OK. + +**/ +EFI_STATUS +EhcExecTransfer ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_URB *Urb, + IN UINTN TimeOut + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN Loop; + BOOLEAN Finished; + + Status = EFI_SUCCESS; + Loop = (TimeOut * EHC_1_MILLISECOND / EHC_SYNC_POLL_INTERVAL) + 1; + Finished = FALSE; + + for (Index = 0; Index < Loop; Index++) { + Finished = EhcCheckUrbResult (Ehc, Urb); + + if (Finished) { + break; + } + + MicroSecondDelay (EHC_SYNC_POLL_INTERVAL); + } + + if (!Finished) { + Status = EFI_TIMEOUT; + } else if (Urb->Result != EFI_USB_NOERROR) { + Status = EFI_DEVICE_ERROR; + } + + return Status; +} + +/** + Delete a single asynchronous interrupt transfer for + the device and endpoint. + + @param Ehc The EHCI device. + @param DevAddr The address of the target device. + @param EpNum The endpoint of the target. + @param DataToggle Return the next data toggle to use. + + @retval EFI_NOT_FOUND No transfer for the device is found. + @retval EFI_SUCCESS An asynchronous transfer is removed. + +**/ +EFI_STATUS +EhciDelAsyncIntTransfer ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT8 DevAddr, + IN UINT8 EpNum, + OUT UINT8 *DataToggle + ) +{ + EFI_LIST_ENTRY *Entry; + EFI_LIST_ENTRY *Next; + PEI_URB *Urb; + EFI_USB_DATA_DIRECTION Direction; + + Direction = (((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut); + EpNum &= 0x0F; + + EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) { + Urb = EFI_LIST_CONTAINER (Entry, PEI_URB, UrbList); + + if ((Urb->Ep.DevAddr == DevAddr) && (Urb->Ep.EpAddr == EpNum) && + (Urb->Ep.Direction == Direction)) { + // + // Check the URB status to retrieve the next data toggle + // from the associated queue head. + // + EhcCheckUrbResult (Ehc, Urb); + *DataToggle = Urb->DataToggle; + + EhcUnlinkQhFromPeriod (Ehc, Urb->Qh); + RemoveEntryList (&Urb->UrbList); + + EhcFreeUrb (Ehc, Urb); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Remove all the asynchronous interrutp transfers. + + @param Ehc The EHCI device. + +**/ +VOID +EhciDelAllAsyncIntTransfers ( + IN PEI_USB2_HC_DEV *Ehc + ) +{ + EFI_LIST_ENTRY *Entry; + EFI_LIST_ENTRY *Next; + PEI_URB *Urb; + + EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) { + Urb = EFI_LIST_CONTAINER (Entry, PEI_URB, UrbList); + + EhcUnlinkQhFromPeriod (Ehc, Urb->Qh); + RemoveEntryList (&Urb->UrbList); + + EhcFreeUrb (Ehc, Urb); + } +} + +/** + Flush data from PCI controller specific address to mapped system + memory address. + + @param Ehc The EHCI device. + @param Urb The URB to unmap. + + @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory. + @retval EFI_SUCCESS Success to flush data to mapped system memory. + +**/ +EFI_STATUS +EhcFlushAsyncIntMap ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_URB *Urb + ) +{ + EFI_PHYSICAL_ADDRESS PhyAddr; + + Urb->DataMap = NULL; + PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) Urb->Data; + Urb->DataPhy = (VOID *) ((UINTN) PhyAddr); + return EFI_SUCCESS; +} + +/** + Update the queue head for next round of asynchronous transfer. + + @param Urb The URB to update. + +**/ +VOID +EhcUpdateAsyncRequest ( + IN PEI_URB *Urb + ) +{ + EFI_LIST_ENTRY *Entry; + PEI_EHC_QTD *FirstQtd; + QH_HW *QhHw; + PEI_EHC_QTD *Qtd; + QTD_HW *QtdHw; + UINTN Index; + + Qtd = NULL; + + if (Urb->Result == EFI_USB_NOERROR) { + FirstQtd = NULL; + + EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) { + Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList); + + if (FirstQtd == NULL) { + FirstQtd = Qtd; + } + + // + // Update the QTD for next round of transfer. Host control + // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/ + // Current Offset. These fields need to be updated. DT isn't + // used by interrupt transfer. It uses DT in queue head. + // Current Offset is in Page[0], only need to reset Page[0] + // to initial data buffer. + // + QtdHw = &Qtd->QtdHw; + QtdHw->Status = QTD_STAT_ACTIVE; + QtdHw->ErrCnt = QTD_MAX_ERR; + QtdHw->CurPage = 0; + QtdHw->TotalBytes = (UINT32) Qtd->DataLen; + QtdHw->Page[0] = EHC_LOW_32BIT (Qtd->Data); + } + + // + // Update QH for next round of transfer. Host control only + // touch the fields in transfer overlay area. Only need to + // zero out the overlay area and set NextQtd to the first + // QTD. DateToggle bit is left untouched. + // + QhHw = &Urb->Qh->QhHw; + QhHw->CurQtd = QTD_LINK (0, TRUE); + QhHw->AltQtd = 0; + + QhHw->Status = 0; + QhHw->Pid = 0; + QhHw->ErrCnt = 0; + QhHw->CurPage = 0; + QhHw->Ioc = 0; + QhHw->TotalBytes = 0; + + for (Index = 0; Index < 5; Index++) { + QhHw->Page[Index] = 0; + QhHw->PageHigh[Index] = 0; + } + + QhHw->NextQtd = QTD_LINK (FirstQtd, FALSE); + } + + return ; +} + +/** + Remove all the asynchronous interrutp transfers. + + @param Event Interrupt event. + @param Context Pointer to PEI_USB2_HC_DEV. + +**/ +VOID +EFIAPI +EhcMoniteAsyncRequests ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + PEI_USB2_HC_DEV *Ehc; + EFI_LIST_ENTRY *Entry; + EFI_LIST_ENTRY *Next; + BOOLEAN Finished; + UINT8 *ProcBuf; + PEI_URB *Urb; + EFI_STATUS Status; + UINTN PageNumber; + + Ehc = (PEI_USB2_HC_DEV *) Context; + + EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) { + Urb = EFI_LIST_CONTAINER (Entry, PEI_URB, UrbList); + + // + // Check the result of URB execution. If it is still + // active, check the next one. + // + Finished = EhcCheckUrbResult (Ehc, Urb); + + if (!Finished) { + continue; + } + + // + // Flush any PCI posted write transactions from a PCI host + // bridge to system memory. + // + Status = EhcFlushAsyncIntMap (Ehc, Urb); + + // + // Allocate a buffer then copy the transferred data for user. + // If failed to allocate the buffer, update the URB for next + // round of transfer. Ignore the data of this round. + // + ProcBuf = NULL; + + if (Urb->Result == EFI_USB_NOERROR) { + ASSERT (Urb->Completed <= Urb->DataLen); + PageNumber = Urb->Completed/PAGESIZE +1; + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + PageNumber, + (EFI_PHYSICAL_ADDRESS *)ProcBuf + ); + if (ProcBuf == NULL) { + EhcUpdateAsyncRequest (Urb); + continue; + } + + CopyMem (ProcBuf, Urb->Data, Urb->Completed); + } + + EhcUpdateAsyncRequest (Urb); + + // + // Leave error recovery to its related device driver. A + // common case of the error recovery is to re-submit the + // interrupt transfer which is linked to the head of the + // list. This function scans from head to tail. So the + // re-submitted interrupt transfer's callback function + // will not be called again in this round. Don't touch this + // URB after the callback, it may have been removed by the + // callback. + // + if (Urb->Callback != NULL) { + (Urb->Callback) (ProcBuf, Urb->Completed, Urb->Context, Urb->Result); + } + + if (ProcBuf != NULL) { + } + } +} diff --git a/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.h b/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.h new file mode 100644 index 0000000000..0eb90f86d1 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.h @@ -0,0 +1,180 @@ +/** @file +Private Header file for Usb Host Controller PEIM + +Copyright (c) 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_EHCI_SCHED_H_ +#define _EFI_EHCI_SCHED_H_ + +/** + Initialize the schedule data structure such as frame list. + + @param Ehc The EHCI device to init schedule data for. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data. + @retval EFI_SUCCESS The schedule data is initialized. + +**/ +EFI_STATUS +EhcInitSched ( + IN PEI_USB2_HC_DEV *Ehc + ) +; + +/** + Free the schedule data. It may be partially initialized. + + @param Ehc The EHCI device. + +**/ +VOID +EhcFreeSched ( + IN PEI_USB2_HC_DEV *Ehc + ) +; + +/** + Link the queue head to the asynchronous schedule list. + UEFI only supports one CTRL/BULK transfer at a time + due to its interfaces. This simplifies the AsynList + management: A reclamation header is always linked to + the AsyncListAddr, the only active QH is appended to it. + + @param Ehc The EHCI device. + @param Qh The queue head to link. + +**/ +VOID +EhcLinkQhToAsync ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_EHC_QH *Qh + ) +; + +/** + Unlink a queue head from the asynchronous schedule list. + Need to synchronize with hardware. + + @param Ehc The EHCI device. + @param Qh The queue head to unlink. + +**/ +VOID +EhcUnlinkQhFromAsync ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_EHC_QH *Qh + ) +; + +/** + Link a queue head for interrupt transfer to the periodic + schedule frame list. This code is very much the same as + that in UHCI. + + @param Ehc The EHCI device. + @param Qh The queue head to link. + +**/ +VOID +EhcLinkQhToPeriod ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_EHC_QH *Qh + ) +; + +/** + Unlink an interrupt queue head from the periodic + schedule frame list. + + @param Ehc The EHCI device. + @param Qh The queue head to unlink. + +**/ +VOID +EhcUnlinkQhFromPeriod ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_EHC_QH *Qh + ) +; + +/** + Execute the transfer by polling the URB. This is a synchronous operation. + + @param Ehc The EHCI device. + @param Urb The URB to execute. + @param TimeOut The time to wait before abort, in millisecond. + + @retval EFI_DEVICE_ERROR The transfer failed due to transfer error. + @retval EFI_TIMEOUT The transfer failed due to time out. + @retval EFI_SUCCESS The transfer finished OK. + +**/ +EFI_STATUS +EhcExecTransfer ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_URB *Urb, + IN UINTN TimeOut + ) +; + +/** + Delete a single asynchronous interrupt transfer for + the device and endpoint. + + @param Ehc The EHCI device. + @param DevAddr The address of the target device. + @param EpNum The endpoint of the target. + @param DataToggle Return the next data toggle to use. + + @retval EFI_NOT_FOUND No transfer for the device is found. + @retval EFI_SUCCESS An asynchronous transfer is removed. + +**/ +EFI_STATUS +EhciDelAsyncIntTransfer ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT8 DevAddr, + IN UINT8 EpNum, + OUT UINT8 *DataToggle + ) +; + +/** + Remove all the asynchronous interrutp transfers. + + @param Ehc The EHCI device. + +**/ +VOID +EhciDelAllAsyncIntTransfers ( + IN PEI_USB2_HC_DEV *Ehc + ) +; + +/** + Remove all the asynchronous interrutp transfers. + + @param Event Interrupt event. + @param Context Pointer to PEI_USB2_HC_DEV. + +**/ +VOID +EFIAPI +EhcMoniteAsyncRequests ( + IN EFI_EVENT Event, + IN VOID *Context + ) +; + +#endif diff --git a/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c b/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c new file mode 100644 index 0000000000..597a4947f5 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c @@ -0,0 +1,610 @@ +/** @file +PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid +which is used to enable recovery function from USB Drivers. + +Copyright (c) 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "EhcPeim.h" + +/** + Delete a single asynchronous interrupt transfer for + the device and endpoint. + + @param Ehc The EHCI device. + @param Data Current data not associated with a QTD. + @param DataLen The length of the data. + @param PktId Packet ID to use in the QTD. + @param Toggle Data toggle to use in the QTD. + @param MaxPacket Maximu packet length of the endpoint. + + @retval the pointer to the created QTD or NULL if failed to create one. + +**/ +PEI_EHC_QTD * +EhcCreateQtd ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT8 *Data, + IN UINTN DataLen, + IN UINT8 PktId, + IN UINT8 Toggle, + IN UINTN MaxPacket + ) +{ + PEI_EHC_QTD *Qtd; + QTD_HW *QtdHw; + UINTN Index; + UINTN Len; + UINTN ThisBufLen; + + ASSERT (Ehc != NULL); + + Qtd = UsbHcAllocateMem (Ehc, Ehc->MemPool, sizeof (PEI_EHC_QTD)); + + if (Qtd == NULL) { + return NULL; + } + + Qtd->Signature = EHC_QTD_SIG; + Qtd->Data = Data; + Qtd->DataLen = 0; + + InitializeListHead (&Qtd->QtdList); + + QtdHw = &Qtd->QtdHw; + QtdHw->NextQtd = QTD_LINK (NULL, TRUE); + QtdHw->AltNext = QTD_LINK (NULL, TRUE); + QtdHw->Status = QTD_STAT_ACTIVE; + QtdHw->Pid = PktId; + QtdHw->ErrCnt = QTD_MAX_ERR; + QtdHw->Ioc = 0; + QtdHw->TotalBytes = 0; + QtdHw->DataToggle = Toggle; + + // + // Fill in the buffer points + // + if (Data != NULL) { + Len = 0; + + for (Index = 0; Index <= QTD_MAX_BUFFER; Index++) { + // + // Set the buffer point (Check page 41 EHCI Spec 1.0). No need to + // compute the offset and clear Reserved fields. This is already + // done in the data point. + // + QtdHw->Page[Index] = EHC_LOW_32BIT (Data); + QtdHw->PageHigh[Index] = EHC_HIGH_32BIT (Data); + + ThisBufLen = QTD_BUF_LEN - (EHC_LOW_32BIT (Data) & QTD_BUF_MASK); + + if (Len + ThisBufLen >= DataLen) { + Len = DataLen; + break; + } + + Len += ThisBufLen; + Data += ThisBufLen; + } + + // + // Need to fix the last pointer if the Qtd can't hold all the + // user's data to make sure that the length is in the unit of + // max packets. If it can hold all the data, there is no such + // need. + // + if (Len < DataLen) { + Len = Len - Len % MaxPacket; + } + + QtdHw->TotalBytes = (UINT32) Len; + Qtd->DataLen = Len; + } + + return Qtd; +} + +/** + Initialize the queue head for interrupt transfer, + that is, initialize the following three fields: + 1. SplitXState in the Status field. + 2. Microframe S-mask. + 3. Microframe C-mask. + + @param Ep The queue head's related endpoint. + @param QhHw The queue head to initialize. + +**/ +VOID +EhcInitIntQh ( + IN USB_ENDPOINT *Ep, + IN QH_HW *QhHw + ) +{ + // + // Because UEFI interface can't utilitize an endpoint with + // poll rate faster than 1ms, only need to set one bit in + // the queue head. simple. But it may be changed later. If + // sub-1ms interrupt is supported, need to update the S-Mask + // here + // + if (Ep->DevSpeed == EFI_USB_SPEED_HIGH) { + QhHw->SMask = QH_MICROFRAME_0; + return ; + } + + // + // For low/full speed device, the transfer must go through + // the split transaction. Need to update three fields + // 1. SplitXState in the status + // 2. Microframe S-Mask + // 3. Microframe C-Mask + // UEFI USB doesn't exercise admission control. It simplely + // schedule the high speed transactions in microframe 0, and + // full/low speed transactions at microframe 1. This also + // avoid the use of FSTN. + // + QhHw->SMask = QH_MICROFRAME_1; + QhHw->CMask = QH_MICROFRAME_3 | QH_MICROFRAME_4 | QH_MICROFRAME_5; +} + +/** + Allocate and initialize a EHCI queue head. + + @param Ehci The EHCI device. + @param Ep The endpoint to create queue head for. + + @retval the pointer to the created queue head or NULL if failed to create one. + +**/ +PEI_EHC_QH * +EhcCreateQh ( + IN PEI_USB2_HC_DEV *Ehci, + IN USB_ENDPOINT *Ep + ) +{ + PEI_EHC_QH *Qh; + QH_HW *QhHw; + + Qh = UsbHcAllocateMem (Ehci, Ehci->MemPool, sizeof (PEI_EHC_QH)); + + if (Qh == NULL) { + return NULL; + } + + Qh->Signature = EHC_QH_SIG; + Qh->NextQh = NULL; + Qh->Interval = Ep->PollRate; + + InitializeListHead (&Qh->Qtds); + + QhHw = &Qh->QhHw; + QhHw->HorizonLink = QH_LINK (NULL, 0, TRUE); + QhHw->DeviceAddr = Ep->DevAddr; + QhHw->Inactive = 0; + QhHw->EpNum = Ep->EpAddr; + QhHw->EpSpeed = Ep->DevSpeed; + QhHw->DtCtrl = 0; + QhHw->ReclaimHead = 0; + QhHw->MaxPacketLen = (UINT32) Ep->MaxPacket; + QhHw->CtrlEp = 0; + QhHw->NakReload = QH_NAK_RELOAD; + QhHw->HubAddr = Ep->HubAddr; + QhHw->PortNum = Ep->HubPort; + QhHw->Multiplier = 1; + QhHw->DataToggle = Ep->Toggle; + + if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) { + QhHw->Status |= QTD_STAT_DO_SS; + } + + switch (Ep->Type) { + case EHC_CTRL_TRANSFER: + // + // Special initialization for the control transfer: + // 1. Control transfer initialize data toggle from each QTD + // 2. Set the Control Endpoint Flag (C) for low/full speed endpoint. + // + QhHw->DtCtrl = 1; + + if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) { + QhHw->CtrlEp = 1; + } + break; + + case EHC_INT_TRANSFER_ASYNC: + case EHC_INT_TRANSFER_SYNC: + // + // Special initialization for the interrupt transfer + // to set the S-Mask and C-Mask + // + QhHw->NakReload = 0; + EhcInitIntQh (Ep, QhHw); + break; + + case EHC_BULK_TRANSFER: + if ((Ep->DevSpeed == EFI_USB_SPEED_HIGH) && (Ep->Direction == EfiUsbDataOut)) { + QhHw->Status |= QTD_STAT_DO_PING; + } + + break; + } + + return Qh; +} + +/** + Convert the poll interval from application to that + be used by EHCI interface data structure. Only need + to get the max 2^n that is less than interval. UEFI + can't support high speed endpoint with a interval less + than 8 microframe because interval is specified in + the unit of ms (millisecond). + + @param Interval The interval to convert. + + @retval The converted interval. + +**/ +UINTN +EhcConvertPollRate ( + IN UINTN Interval + ) +{ + UINTN BitCount; + + if (Interval == 0) { + return 1; + } + + // + // Find the index (1 based) of the highest non-zero bit + // + BitCount = 0; + + while (Interval != 0) { + Interval >>= 1; + BitCount++; + } + + return (UINTN)1 << (BitCount - 1); +} + +/** + Free a list of QTDs. + + @param Ehc The EHCI device. + @param Qtds The list head of the QTD. + +**/ +VOID +EhcFreeQtds ( + IN PEI_USB2_HC_DEV *Ehc, + IN EFI_LIST_ENTRY *Qtds + ) +{ + EFI_LIST_ENTRY *Entry; + EFI_LIST_ENTRY *Next; + PEI_EHC_QTD *Qtd; + + EFI_LIST_FOR_EACH_SAFE (Entry, Next, Qtds) { + Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList); + + RemoveEntryList (&Qtd->QtdList); + UsbHcFreeMem (Ehc->MemPool, Qtd, sizeof (PEI_EHC_QTD)); + } +} + +/** + Free an allocated URB. It is possible for it to be partially inited. + + @param Ehc The EHCI device. + @param Urb The URB to free. + +**/ +VOID +EhcFreeUrb ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_URB *Urb + ) +{ + if (Urb->Qh != NULL) { + // + // Ensure that this queue head has been unlinked from the + // schedule data structures. Free all the associated QTDs + // + EhcFreeQtds (Ehc, &Urb->Qh->Qtds); + UsbHcFreeMem (Ehc->MemPool, Urb->Qh, sizeof (PEI_EHC_QH)); + } +} + +/** + Create a list of QTDs for the URB. + + @param Ehc The EHCI device. + @param Urb The URB to create QTDs for. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for QTD. + @retval EFI_SUCCESS The QTDs are allocated for the URB. + +**/ +EFI_STATUS +EhcCreateQtds ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_URB *Urb + ) +{ + USB_ENDPOINT *Ep; + PEI_EHC_QH *Qh; + PEI_EHC_QTD *Qtd; + PEI_EHC_QTD *StatusQtd; + PEI_EHC_QTD *NextQtd; + EFI_LIST_ENTRY *Entry; + UINT32 AlterNext; + UINT8 Toggle; + UINTN Len; + UINT8 Pid; + + ASSERT ((Urb != NULL) && (Urb->Qh != NULL)); + + // + // EHCI follows the alternet next QTD pointer if it meets + // a short read and the AlterNext pointer is valid. UEFI + // EHCI driver should terminate the transfer except the + // control transfer. + // + Toggle = 0; + Qh = Urb->Qh; + Ep = &Urb->Ep; + StatusQtd = NULL; + AlterNext = QTD_LINK (NULL, TRUE); + + if (Ep->Direction == EfiUsbDataIn) { + AlterNext = QTD_LINK (Ehc->ShortReadStop, FALSE); + } + + // + // Build the Setup and status packets for control transfer + // + if (Urb->Ep.Type == EHC_CTRL_TRANSFER) { + Len = sizeof (EFI_USB_DEVICE_REQUEST); + Qtd = EhcCreateQtd (Ehc, Urb->RequestPhy, Len, QTD_PID_SETUP, 0, Ep->MaxPacket); + + if (Qtd == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InsertTailList (&Qh->Qtds, &Qtd->QtdList); + + // + // Create the status packet now. Set the AlterNext to it. So, when + // EHCI meets a short control read, it can resume at the status stage. + // Use the opposite direction of the data stage, or IN if there is + // no data stage. + // + if (Ep->Direction == EfiUsbDataIn) { + Pid = QTD_PID_OUTPUT; + } else { + Pid = QTD_PID_INPUT; + } + + StatusQtd = EhcCreateQtd (Ehc, NULL, 0, Pid, 1, Ep->MaxPacket); + + if (StatusQtd == NULL) { + goto ON_ERROR; + } + + if (Ep->Direction == EfiUsbDataIn) { + AlterNext = QTD_LINK (StatusQtd, FALSE); + } + + Toggle = 1; + } + + // + // Build the data packets for all the transfers + // + if (Ep->Direction == EfiUsbDataIn) { + Pid = QTD_PID_INPUT; + } else { + Pid = QTD_PID_OUTPUT; + } + + Qtd = NULL; + Len = 0; + + while (Len < Urb->DataLen) { + Qtd = EhcCreateQtd ( + Ehc, + (UINT8 *) Urb->DataPhy + Len, + Urb->DataLen - Len, + Pid, + Toggle, + Ep->MaxPacket + ); + + if (Qtd == NULL) { + goto ON_ERROR; + } + + Qtd->QtdHw.AltNext = AlterNext; + InsertTailList (&Qh->Qtds, &Qtd->QtdList); + + // + // Switch the Toggle bit if odd number of packets are included in the QTD. + // + if (((Qtd->DataLen + Ep->MaxPacket - 1) / Ep->MaxPacket) % 2) { + Toggle = (UINT8) (1 - Toggle); + } + + Len += Qtd->DataLen; + } + + // + // Insert the status packet for control transfer + // + if (Ep->Type == EHC_CTRL_TRANSFER) { + InsertTailList (&Qh->Qtds, &StatusQtd->QtdList); + } + + // + // OK, all the QTDs needed are created. Now, fix the NextQtd point + // + EFI_LIST_FOR_EACH (Entry, &Qh->Qtds) { + Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList); + + // + // break if it is the last entry on the list + // + if (Entry->ForwardLink == &Qh->Qtds) { + break; + } + + NextQtd = EFI_LIST_CONTAINER (Entry->ForwardLink, PEI_EHC_QTD, QtdList); + Qtd->QtdHw.NextQtd = QTD_LINK (NextQtd, FALSE); + } + + // + // Link the QTDs to the queue head + // + NextQtd = EFI_LIST_CONTAINER (Qh->Qtds.ForwardLink, PEI_EHC_QTD, QtdList); + Qh->QhHw.NextQtd = QTD_LINK (NextQtd, FALSE); + return EFI_SUCCESS; + +ON_ERROR: + EhcFreeQtds (Ehc, &Qh->Qtds); + return EFI_OUT_OF_RESOURCES; +} + +/** + Create a new URB and its associated QTD. + + @param Ehc The EHCI device. + @param DevAddr The device address. + @param EpAddr Endpoint addrress & its direction. + @param DevSpeed The device speed. + @param Toggle Initial data toggle to use. + @param MaxPacket The max packet length of the endpoint. + @param Hub The transaction translator to use. + @param Type The transaction type. + @param Request The standard USB request for control transfer. + @param Data The user data to transfer. + @param DataLen The length of data buffer. + @param Callback The function to call when data is transferred. + @param Context The context to the callback. + @param Interval The interval for interrupt transfer. + + @retval the pointer to the created URB or NULL. + +**/ +PEI_URB * +EhcCreateUrb ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINT8 Toggle, + IN UINTN MaxPacket, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub, + IN UINTN Type, + IN EFI_USB_DEVICE_REQUEST *Request, + IN VOID *Data, + IN UINTN DataLen, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context, + IN UINTN Interval + ) +{ + USB_ENDPOINT *Ep; + EFI_PHYSICAL_ADDRESS PhyAddr; + EFI_STATUS Status; + UINTN Len; + PEI_URB *Urb; + VOID *Map; + + + Map = NULL; + + Urb = Ehc->Urb; + Urb->Signature = EHC_URB_SIG; + InitializeListHead (&Urb->UrbList); + + Ep = &Urb->Ep; + Ep->DevAddr = DevAddr; + Ep->EpAddr = (UINT8) (EpAddr & 0x0F); + Ep->Direction = (((EpAddr & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut); + Ep->DevSpeed = DevSpeed; + Ep->MaxPacket = MaxPacket; + + Ep->HubAddr = 0; + Ep->HubPort = 0; + + if (DevSpeed != EFI_USB_SPEED_HIGH) { + ASSERT (Hub != NULL); + + Ep->HubAddr = Hub->TranslatorHubAddress; + Ep->HubPort = Hub->TranslatorPortNumber; + } + + Ep->Toggle = Toggle; + Ep->Type = Type; + Ep->PollRate = EhcConvertPollRate (Interval); + + Urb->Request = Request; + Urb->Data = Data; + Urb->DataLen = DataLen; + Urb->Callback = Callback; + Urb->Context = Context; + Urb->Qh = EhcCreateQh (Ehc, &Urb->Ep); + + if (Urb->Qh == NULL) { + goto ON_ERROR; + } + + // + // Map the request and user data + // + if (Request != NULL) { + Len = sizeof (EFI_USB_DEVICE_REQUEST); + PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) Request ; + if ( (Len != sizeof (EFI_USB_DEVICE_REQUEST))) { + goto ON_ERROR; + } + + Urb->RequestPhy = (VOID *) ((UINTN) PhyAddr); + Urb->RequestMap = Map; + } + + if (Data != NULL) { + Len = DataLen; + PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) Data ; + if ( (Len != DataLen)) { + goto ON_ERROR; + } + + Urb->DataPhy = (VOID *) ((UINTN) PhyAddr); + Urb->DataMap = Map; + } + + Status = EhcCreateQtds (Ehc, Urb); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return Urb; + +ON_ERROR: + EhcFreeUrb (Ehc, Urb); + return NULL; +} diff --git a/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.h b/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.h new file mode 100644 index 0000000000..3fe93fb294 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.h @@ -0,0 +1,331 @@ +/** @file +Private Header file for Usb Host Controller PEIM + +Copyright (c) 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_EHCI_URB_H_ +#define _EFI_EHCI_URB_H_ + +typedef struct _PEI_EHC_QTD PEI_EHC_QTD; +typedef struct _PEI_EHC_QH PEI_EHC_QH; +typedef struct _PEI_URB PEI_URB; + +#define EHC_CTRL_TRANSFER 0x01 +#define EHC_BULK_TRANSFER 0x02 +#define EHC_INT_TRANSFER_SYNC 0x04 +#define EHC_INT_TRANSFER_ASYNC 0x08 + +#define EHC_QTD_SIG SIGNATURE_32 ('U', 'S', 'B', 'T') +#define EHC_QH_SIG SIGNATURE_32 ('U', 'S', 'B', 'H') +#define EHC_URB_SIG SIGNATURE_32 ('U', 'S', 'B', 'R') + +// +// Hardware related bit definitions +// +#define EHC_TYPE_ITD 0x00 +#define EHC_TYPE_QH 0x02 +#define EHC_TYPE_SITD 0x04 +#define EHC_TYPE_FSTN 0x06 + +#define QH_NAK_RELOAD 3 +#define QH_HSHBW_MULTI 1 + +#define QTD_MAX_ERR 3 +#define QTD_PID_OUTPUT 0x00 +#define QTD_PID_INPUT 0x01 +#define QTD_PID_SETUP 0x02 + +#define QTD_STAT_DO_OUT 0 +#define QTD_STAT_DO_SS 0 +#define QTD_STAT_DO_PING 0x01 +#define QTD_STAT_DO_CS 0x02 +#define QTD_STAT_TRANS_ERR 0x08 +#define QTD_STAT_BABBLE_ERR 0x10 +#define QTD_STAT_BUFF_ERR 0x20 +#define QTD_STAT_HALTED 0x40 +#define QTD_STAT_ACTIVE 0x80 +#define QTD_STAT_ERR_MASK (QTD_STAT_TRANS_ERR | QTD_STAT_BABBLE_ERR | QTD_STAT_BUFF_ERR) + +#define QTD_MAX_BUFFER 4 +#define QTD_BUF_LEN 4096 +#define QTD_BUF_MASK 0x0FFF + +#define QH_MICROFRAME_0 0x01 +#define QH_MICROFRAME_1 0x02 +#define QH_MICROFRAME_2 0x04 +#define QH_MICROFRAME_3 0x08 +#define QH_MICROFRAME_4 0x10 +#define QH_MICROFRAME_5 0x20 +#define QH_MICROFRAME_6 0x40 +#define QH_MICROFRAME_7 0x80 + +#define USB_ERR_SHORT_PACKET 0x200 + +// +// Fill in the hardware link point: pass in a EHC_QH/QH_HW +// pointer to QH_LINK; A EHC_QTD/QTD_HW pointer to QTD_LINK +// +#define QH_LINK(Addr, Type, Term) \ + ((UINT32) ((EHC_LOW_32BIT (Addr) & 0xFFFFFFE0) | (Type) | ((Term) ? 1 : 0))) + +#define QTD_LINK(Addr, Term) QH_LINK((Addr), 0, (Term)) + +// +// The defination of EHCI hardware used data structure for +// little endian architecture. The QTD and QH structures +// are required to be 32 bytes aligned. Don't add members +// to the head of the associated software strucuture. +// +#pragma pack(1) +typedef struct { + UINT32 NextQtd; + UINT32 AltNext; + + UINT32 Status : 8; + UINT32 Pid : 2; + UINT32 ErrCnt : 2; + UINT32 CurPage : 3; + UINT32 Ioc : 1; + UINT32 TotalBytes : 15; + UINT32 DataToggle : 1; + + UINT32 Page[5]; + UINT32 PageHigh[5]; +} QTD_HW; + +typedef struct { + UINT32 HorizonLink; + // + // Endpoint capabilities/Characteristics DWord 1 and DWord 2 + // + UINT32 DeviceAddr : 7; + UINT32 Inactive : 1; + UINT32 EpNum : 4; + UINT32 EpSpeed : 2; + UINT32 DtCtrl : 1; + UINT32 ReclaimHead : 1; + UINT32 MaxPacketLen : 11; + UINT32 CtrlEp : 1; + UINT32 NakReload : 4; + + UINT32 SMask : 8; + UINT32 CMask : 8; + UINT32 HubAddr : 7; + UINT32 PortNum : 7; + UINT32 Multiplier : 2; + + // + // Transaction execution overlay area + // + UINT32 CurQtd; + UINT32 NextQtd; + UINT32 AltQtd; + + UINT32 Status : 8; + UINT32 Pid : 2; + UINT32 ErrCnt : 2; + UINT32 CurPage : 3; + UINT32 Ioc : 1; + UINT32 TotalBytes : 15; + UINT32 DataToggle : 1; + + UINT32 Page[5]; + UINT32 PageHigh[5]; +} QH_HW; +#pragma pack() + + +// +// Endpoint address and its capabilities +// +typedef struct _USB_ENDPOINT { + UINT8 DevAddr; + UINT8 EpAddr; // Endpoint address, no direction encoded in + EFI_USB_DATA_DIRECTION Direction; + UINT8 DevSpeed; + UINTN MaxPacket; + UINT8 HubAddr; + UINT8 HubPort; + UINT8 Toggle; // Data toggle, not used for control transfer + UINTN Type; + UINTN PollRate; // Polling interval used by EHCI +} USB_ENDPOINT; + +// +// Software QTD strcture, this is used to manage all the +// QTD generated from a URB. Don't add fields before QtdHw. +// +struct _PEI_EHC_QTD { + QTD_HW QtdHw; + UINT32 Signature; + EFI_LIST_ENTRY QtdList; // The list of QTDs to one end point + UINT8 *Data; // Buffer of the original data + UINTN DataLen; // Original amount of data in this QTD +}; + + + +// +// Software QH structure. All three different transaction types +// supported by UEFI USB, that is the control/bulk/interrupt +// transfers use the queue head and queue token strcuture. +// +// Interrupt QHs are linked to periodic frame list in the reversed +// 2^N tree. Each interrupt QH is linked to the list starting at +// frame 0. There is a dummy interrupt QH linked to each frame as +// a sentinental whose polling interval is 1. Synchronous interrupt +// transfer is linked after this dummy QH. +// +// For control/bulk transfer, only synchronous (in the sense of UEFI) +// transfer is supported. A dummy QH is linked to EHCI AsyncListAddr +// as the reclamation header. New transfer is inserted after this QH. +// +struct _PEI_EHC_QH { + QH_HW QhHw; + UINT32 Signature; + PEI_EHC_QH *NextQh; // The queue head pointed to by horizontal link + EFI_LIST_ENTRY Qtds; // The list of QTDs to this queue head + UINTN Interval; +}; + +// +// URB (Usb Request Block) contains information for all kinds of +// usb requests. +// +struct _PEI_URB { + UINT32 Signature; + EFI_LIST_ENTRY UrbList; + + // + // Transaction information + // + USB_ENDPOINT Ep; + EFI_USB_DEVICE_REQUEST *Request; // Control transfer only + VOID *RequestPhy; // Address of the mapped request + VOID *RequestMap; + VOID *Data; + UINTN DataLen; + VOID *DataPhy; // Address of the mapped user data + VOID *DataMap; + EFI_ASYNC_USB_TRANSFER_CALLBACK Callback; + VOID *Context; + + // + // Schedule data + // + PEI_EHC_QH *Qh; + + // + // Transaction result + // + UINT32 Result; + UINTN Completed; // completed data length + UINT8 DataToggle; +}; + +/** + Delete a single asynchronous interrupt transfer for + the device and endpoint. + + @param Ehc The EHCI device. + @param Data Current data not associated with a QTD. + @param DataLen The length of the data. + @param PktId Packet ID to use in the QTD. + @param Toggle Data toggle to use in the QTD. + @param MaxPacket Maximu packet length of the endpoint. + + @retval the pointer to the created QTD or NULL if failed to create one. + +**/ +PEI_EHC_QTD * +EhcCreateQtd ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT8 *Data, + IN UINTN DataLen, + IN UINT8 PktId, + IN UINT8 Toggle, + IN UINTN MaxPacket + ) +; + +/** + Allocate and initialize a EHCI queue head. + + @param Ehci The EHCI device. + @param Ep The endpoint to create queue head for. + + @retval the pointer to the created queue head or NULL if failed to create one. + +**/ +PEI_EHC_QH * +EhcCreateQh ( + IN PEI_USB2_HC_DEV *Ehci, + IN USB_ENDPOINT *Ep + ) +; + +/** + Free an allocated URB. It is possible for it to be partially inited. + + @param Ehc The EHCI device. + @param Urb The URB to free. + +**/ +VOID +EhcFreeUrb ( + IN PEI_USB2_HC_DEV *Ehc, + IN PEI_URB *Urb + ) +; + +/** + Create a new URB and its associated QTD. + + @param Ehc The EHCI device. + @param DevAddr The device address. + @param EpAddr Endpoint addrress & its direction. + @param DevSpeed The device speed. + @param Toggle Initial data toggle to use. + @param MaxPacket The max packet length of the endpoint. + @param Hub The transaction translator to use. + @param Type The transaction type. + @param Request The standard USB request for control transfer. + @param Data The user data to transfer. + @param DataLen The length of data buffer. + @param Callback The function to call when data is transferred. + @param Context The context to the callback. + @param Interval The interval for interrupt transfer. + + @retval the pointer to the created URB or NULL. + +**/ +PEI_URB * +EhcCreateUrb ( + IN PEI_USB2_HC_DEV *Ehc, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINT8 Toggle, + IN UINTN MaxPacket, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub, + IN UINTN Type, + IN EFI_USB_DEVICE_REQUEST *Request, + IN VOID *Data, + IN UINTN DataLen, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context, + IN UINTN Interval + ) +; +#endif diff --git a/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.c b/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.c new file mode 100644 index 0000000000..6b3755852b --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.c @@ -0,0 +1,493 @@ +/** @file +PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid +which is used to enable recovery function from USB Drivers. + +Copyright (c) 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "EhcPeim.h" + +/** + Allocate a block of memory to be used by the buffer pool. + + @param Ehc The EHCI device. + @param Pool The buffer pool to allocate memory for. + @param Pages How many pages to allocate. + + @return The allocated memory block or NULL if failed. + +**/ +USBHC_MEM_BLOCK * +UsbHcAllocMemBlock ( + IN PEI_USB2_HC_DEV *Ehc, + IN USBHC_MEM_POOL *Pool, + IN UINTN Pages + ) +{ + USBHC_MEM_BLOCK *Block; + VOID *BufHost; + VOID *Mapping; + EFI_PHYSICAL_ADDRESS MappedAddr; + EFI_STATUS Status; + UINTN PageNumber; + EFI_PHYSICAL_ADDRESS TempPtr; + + Mapping = NULL; + PageNumber = sizeof(USBHC_MEM_BLOCK)/PAGESIZE +1; + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + PageNumber, + &TempPtr + ); + + if (EFI_ERROR (Status)) { + return NULL; + } + ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE); + + // + // each bit in the bit array represents USBHC_MEM_UNIT + // bytes of memory in the memory block. + // + ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE); + + Block = (USBHC_MEM_BLOCK*)(UINTN)TempPtr; + Block->BufLen = EFI_PAGES_TO_SIZE (Pages); + Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8); + + PageNumber = (Block->BitsLen)/PAGESIZE +1; + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + PageNumber, + &TempPtr + ); + + if (EFI_ERROR (Status)) { + return NULL; + } + ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE); + + Block->Bits = (UINT8 *)(UINTN)TempPtr; + + + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + Pages, + &TempPtr + ); + ZeroMem ((VOID *)(UINTN)TempPtr, Pages*EFI_PAGE_SIZE); + + BufHost = (VOID *)(UINTN)TempPtr; + MappedAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) BufHost; + // + // Check whether the data structure used by the host controller + // should be restricted into the same 4G + // + if (Pool->Check4G && (Pool->Which4G != USB_HC_HIGH_32BIT (MappedAddr))) { + return NULL; + } + + Block->BufHost = BufHost; + Block->Buf = (UINT8 *) ((UINTN) MappedAddr); + Block->Mapping = Mapping; + Block->Next = NULL; + + return Block; + +} + +/** + Free the memory block from the memory pool. + + @param Pool The memory pool to free the block from. + @param Block The memory block to free. + +**/ +VOID +UsbHcFreeMemBlock ( + IN USBHC_MEM_POOL *Pool, + IN USBHC_MEM_BLOCK *Block + ) +{ + ASSERT ((Pool != NULL) && (Block != NULL)); +} + +/** + Alloc some memory from the block. + + @param Block The memory block to allocate memory from. + @param Units Number of memory units to allocate. + + @return The pointer to the allocated memory. If couldn't allocate the needed memory, + the return value is NULL. + +**/ +VOID * +UsbHcAllocMemFromBlock ( + IN USBHC_MEM_BLOCK *Block, + IN UINTN Units + ) +{ + UINTN Byte; + UINT8 Bit; + UINTN StartByte; + UINT8 StartBit; + UINTN Available; + UINTN Count; + + ASSERT ((Block != 0) && (Units != 0)); + + StartByte = 0; + StartBit = 0; + Available = 0; + + for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) { + // + // If current bit is zero, the corresponding memory unit is + // available, otherwise we need to restart our searching. + // Available counts the consective number of zero bit. + // + if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) { + Available++; + + if (Available >= Units) { + break; + } + + NEXT_BIT (Byte, Bit); + + } else { + NEXT_BIT (Byte, Bit); + + Available = 0; + StartByte = Byte; + StartBit = Bit; + } + } + + if (Available < Units) { + return NULL; + } + + // + // Mark the memory as allocated + // + Byte = StartByte; + Bit = StartBit; + + for (Count = 0; Count < Units; Count++) { + ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) USB_HC_BIT (Bit)); + NEXT_BIT (Byte, Bit); + } + + return Block->Buf + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT; +} + +/** + Insert the memory block to the pool's list of the blocks. + + @param Head The head of the memory pool's block list. + @param Block The memory block to insert. + +**/ +VOID +UsbHcInsertMemBlockToPool ( + IN USBHC_MEM_BLOCK *Head, + IN USBHC_MEM_BLOCK *Block + ) +{ + ASSERT ((Head != NULL) && (Block != NULL)); + Block->Next = Head->Next; + Head->Next = Block; +} + +/** + Is the memory block empty? + + @param Block The memory block to check. + + @retval TRUE The memory block is empty. + @retval FALSE The memory block isn't empty. + +**/ +BOOLEAN +UsbHcIsMemBlockEmpty ( + IN USBHC_MEM_BLOCK *Block + ) +{ + UINTN Index; + + + for (Index = 0; Index < Block->BitsLen; Index++) { + if (Block->Bits[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Unlink the memory block from the pool's list. + + @param Head The block list head of the memory's pool. + @param BlockToUnlink The memory block to unlink. + +**/ +VOID +UsbHcUnlinkMemBlock ( + IN USBHC_MEM_BLOCK *Head, + IN USBHC_MEM_BLOCK *BlockToUnlink + ) +{ + USBHC_MEM_BLOCK *Block; + + ASSERT ((Head != NULL) && (BlockToUnlink != NULL)); + + for (Block = Head; Block != NULL; Block = Block->Next) { + if (Block->Next == BlockToUnlink) { + Block->Next = BlockToUnlink->Next; + BlockToUnlink->Next = NULL; + break; + } + } +} + +/** + Initialize the memory management pool for the host controller. + + @param Ehc The EHCI device. + @param Check4G Whether the host controller requires allocated memory. + from one 4G address space. + @param Which4G The 4G memory area each memory allocated should be from. + + @retval EFI_SUCCESS The memory pool is initialized. + @retval EFI_OUT_OF_RESOURCE Fail to init the memory pool. + +**/ +USBHC_MEM_POOL * +UsbHcInitMemPool ( + IN PEI_USB2_HC_DEV *Ehc, + IN BOOLEAN Check4G, + IN UINT32 Which4G + ) +{ + USBHC_MEM_POOL *Pool; + UINTN PageNumber; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS TempPtr; + + PageNumber = sizeof(USBHC_MEM_POOL)/PAGESIZE +1; + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + PageNumber, + &TempPtr + ); + + if (EFI_ERROR (Status)) { + return NULL; + } + ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE); + + Pool = (USBHC_MEM_POOL *) ((UINTN) TempPtr); + + Pool->Check4G = Check4G; + Pool->Which4G = Which4G; + Pool->Head = UsbHcAllocMemBlock (Ehc, Pool, USBHC_MEM_DEFAULT_PAGES); + + if (Pool->Head == NULL) { + Pool = NULL; + } + + return Pool; +} + +/** + Release the memory management pool. + + @param Pool The USB memory pool to free. + + @retval EFI_DEVICE_ERROR Fail to free the memory pool. + @retval EFI_SUCCESS The memory pool is freed. + +**/ +EFI_STATUS +UsbHcFreeMemPool ( + IN USBHC_MEM_POOL *Pool + ) +{ + USBHC_MEM_BLOCK *Block; + + ASSERT (Pool->Head != NULL); + + // + // Unlink all the memory blocks from the pool, then free them. + // UsbHcUnlinkMemBlock can't be used to unlink and free the + // first block. + // + for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) { + UsbHcFreeMemBlock (Pool, Block); + } + + UsbHcFreeMemBlock (Pool, Pool->Head); + + return EFI_SUCCESS; +} + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Ehc The EHCI device. + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +UsbHcAllocateMem ( + IN PEI_USB2_HC_DEV *Ehc, + IN USBHC_MEM_POOL *Pool, + IN UINTN Size + ) +{ + USBHC_MEM_BLOCK *Head; + USBHC_MEM_BLOCK *Block; + USBHC_MEM_BLOCK *NewBlock; + VOID *Mem; + UINTN AllocSize; + UINTN Pages; + + Mem = NULL; + AllocSize = USBHC_MEM_ROUND (Size); + Head = Pool->Head; + ASSERT (Head != NULL); + + // + // First check whether current memory blocks can satisfy the allocation. + // + for (Block = Head; Block != NULL; Block = Block->Next) { + Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem (Mem, Size); + break; + } + } + + if (Mem != NULL) { + return Mem; + } + + // + // Create a new memory block if there is not enough memory + // in the pool. If the allocation size is larger than the + // default page number, just allocate a large enough memory + // block. Otherwise allocate default pages. + // + if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) { + Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1; + } else { + Pages = USBHC_MEM_DEFAULT_PAGES; + } + NewBlock = UsbHcAllocMemBlock (Ehc,Pool, Pages); + + if (NewBlock == NULL) { + return NULL; + } + + // + // Add the new memory block to the pool, then allocate memory from it + // + UsbHcInsertMemBlockToPool (Head, NewBlock); + Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem (Mem, Size); + } + + return Mem; +} + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + +**/ +VOID +UsbHcFreeMem ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +{ + USBHC_MEM_BLOCK *Head; + USBHC_MEM_BLOCK *Block; + UINT8 *ToFree; + UINTN AllocSize; + UINTN Byte; + UINTN Bit; + UINTN Count; + + Head = Pool->Head; + AllocSize = USBHC_MEM_ROUND (Size); + ToFree = (UINT8 *) Mem; + + for (Block = Head; Block != NULL; Block = Block->Next) { + // + // scan the memory block list for the memory block that + // completely contains the memory to free. + // + if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) { + // + // compute the start byte and bit in the bit array + // + Byte = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) / 8; + Bit = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) % 8; + + // + // reset associated bits in bit arry + // + for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) { + ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ USB_HC_BIT (Bit)); + NEXT_BIT (Byte, Bit); + } + + break; + } + } + + // + // If Block == NULL, it means that the current memory isn't + // in the host controller's pool. This is critical because + // the caller has passed in a wrong memory point + // + ASSERT (Block != NULL); + + // + // Release the current memory block if it is empty and not the head + // + if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) { + UsbHcFreeMemBlock (Pool, Block); + } + + return ; +} diff --git a/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.h b/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.h new file mode 100644 index 0000000000..586d12af96 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.h @@ -0,0 +1,77 @@ +/** @file +Private Header file for Usb Host Controller PEIM + +Copyright (c) 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_EHCI_MEM_H_ +#define _EFI_EHCI_MEM_H_ + +#include +#include + +#define USB_HC_BIT(a) ((UINTN)(1 << (a))) + +#define USB_HC_BIT_IS_SET(Data, Bit) \ + ((BOOLEAN)(((Data) & USB_HC_BIT(Bit)) == USB_HC_BIT(Bit))) + +#define USB_HC_HIGH_32BIT(Addr64) \ + ((UINT32)(RShiftU64((UINTN)(Addr64), 32) & 0XFFFFFFFF)) + +typedef struct _USBHC_MEM_BLOCK USBHC_MEM_BLOCK; + +struct _USBHC_MEM_BLOCK { + UINT8 *Bits; // Bit array to record which unit is allocated + UINTN BitsLen; + UINT8 *Buf; + UINT8 *BufHost; + UINTN BufLen; // Memory size in bytes + VOID *Mapping; + USBHC_MEM_BLOCK *Next; +}; + +// +// USBHC_MEM_POOL is used to manage the memory used by USB +// host controller. EHCI requires the control memory and transfer +// data to be on the same 4G memory. +// +typedef struct _USBHC_MEM_POOL { + BOOLEAN Check4G; + UINT32 Which4G; + USBHC_MEM_BLOCK *Head; +} USBHC_MEM_POOL; + +// +// Memory allocation unit, must be 2^n, n>4 +// +#define USBHC_MEM_UNIT 64 + +#define USBHC_MEM_UNIT_MASK (USBHC_MEM_UNIT - 1) +#define USBHC_MEM_DEFAULT_PAGES 16 + +#define USBHC_MEM_ROUND(Len) (((Len) + USBHC_MEM_UNIT_MASK) & (~USBHC_MEM_UNIT_MASK)) + +// +// Advance the byte and bit to the next bit, adjust byte accordingly. +// +#define NEXT_BIT(Byte, Bit) \ + do { \ + (Bit)++; \ + if ((Bit) > 7) { \ + (Byte)++; \ + (Bit) = 0; \ + } \ + } while (0) + + +#endif diff --git a/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.c b/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.c new file mode 100644 index 0000000000..f58b6943f2 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.c @@ -0,0 +1,3196 @@ +/** @file +PEIM to produce gPeiUsbHostControllerPpiGuid based on gPeiUsbControllerPpiGuid +which is used to enable recovery function from USB Drivers. + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UhcPeim.h" + +/** + Initializes Usb Host Controller. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS PPI successfully installed. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + +**/ +EFI_STATUS +EFIAPI +UhcPeimEntry ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + PEI_USB_CONTROLLER_PPI *ChipSetUsbControllerPpi; + EFI_STATUS Status; + UINT8 Index; + UINTN ControllerType; + UINTN BaseAddress; + UINTN MemPages; + USB_UHC_DEV *UhcDev; + EFI_PHYSICAL_ADDRESS TempPtr; + + // + // Shadow this PEIM to run from memory + // + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { + return EFI_SUCCESS; + } + + Status = PeiServicesLocatePpi ( + &gPeiUsbControllerPpiGuid, + 0, + NULL, + (VOID **) &ChipSetUsbControllerPpi + ); + // + // If failed to locate, it is a bug in dispather as depex has gPeiUsbControllerPpiGuid. + // + ASSERT_EFI_ERROR (Status); + + Index = 0; + while (TRUE) { + Status = ChipSetUsbControllerPpi->GetUsbController ( + (EFI_PEI_SERVICES **) PeiServices, + ChipSetUsbControllerPpi, + Index, + &ControllerType, + &BaseAddress + ); + // + // When status is error, meant no controller is found + // + if (EFI_ERROR (Status)) { + break; + } + + // + // This PEIM is for UHC type controller. + // + if (ControllerType != PEI_UHCI_CONTROLLER) { + Index++; + continue; + } + + MemPages = sizeof (USB_UHC_DEV) / EFI_PAGE_SIZE + 1; + + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + MemPages, + &TempPtr + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + UhcDev = (USB_UHC_DEV *) ((UINTN) TempPtr); + UhcDev->Signature = USB_UHC_DEV_SIGNATURE; + UhcDev->UsbHostControllerBaseAddress = (UINT32) BaseAddress; + + // + // Init local memory management service + // + Status = InitializeMemoryManagement (UhcDev); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Initialize Uhc's hardware + // + Status = InitializeUsbHC (UhcDev); + if (EFI_ERROR (Status)) { + return Status; + } + + UhcDev->UsbHostControllerPpi.ControlTransfer = UhcControlTransfer; + UhcDev->UsbHostControllerPpi.BulkTransfer = UhcBulkTransfer; + UhcDev->UsbHostControllerPpi.GetRootHubPortNumber = UhcGetRootHubPortNumber; + UhcDev->UsbHostControllerPpi.GetRootHubPortStatus = UhcGetRootHubPortStatus; + UhcDev->UsbHostControllerPpi.SetRootHubPortFeature = UhcSetRootHubPortFeature; + UhcDev->UsbHostControllerPpi.ClearRootHubPortFeature = UhcClearRootHubPortFeature; + + UhcDev->PpiDescriptor.Flags = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST); + UhcDev->PpiDescriptor.Guid = &gPeiUsbHostControllerPpiGuid; + UhcDev->PpiDescriptor.Ppi = &UhcDev->UsbHostControllerPpi; + + Status = PeiServicesInstallPpi (&UhcDev->PpiDescriptor); + if (EFI_ERROR (Status)) { + Index++; + continue; + } + + Index++; + } + + return EFI_SUCCESS; +} + +/** + Submits control transfer to a target USB device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB_HOST_CONTROLLER_PPI. + @param DeviceAddress The target device address. + @param DeviceSpeed Target device speed. + @param MaximumPacketLength Maximum packet size the default control transfer + endpoint is capable of sending or receiving. + @param Request USB device request to send. + @param TransferDirection Specifies the data direction for the data stage. + @param Data Data buffer to be transmitted or received from USB device. + @param DataLength The size (in bytes) of the data buffer. + @param TimeOut Indicates the maximum timeout, in millisecond. + @param TransferResult Return the result of this control transfer. + + @retval EFI_SUCCESS Transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resources. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_TIMEOUT Transfer failed due to timeout. + @retval EFI_DEVICE_ERROR Transfer failed due to host controller or device error. + +**/ +EFI_STATUS +EFIAPI +UhcControlTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 DeviceAddress, + IN UINT8 DeviceSpeed, + IN UINT8 MaximumPacketLength, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data OPTIONAL, + IN OUT UINTN *DataLength OPTIONAL, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +{ + USB_UHC_DEV *UhcDev; + UINT32 StatusReg; + UINT8 PktID; + QH_STRUCT *PtrQH; + TD_STRUCT *PtrTD; + TD_STRUCT *PtrPreTD; + TD_STRUCT *PtrSetupTD; + TD_STRUCT *PtrStatusTD; + EFI_STATUS Status; + UINT32 DataLen; + UINT8 *PtrDataSource; + UINT8 *Ptr; + UINT8 DataToggle; + + UhcDev = PEI_RECOVERY_USB_UHC_DEV_FROM_UHCI_THIS (This); + + StatusReg = UhcDev->UsbHostControllerBaseAddress + USBSTS; + + PktID = INPUT_PACKET_ID; + + if (Request == NULL || TransferResult == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // if errors exist that cause host controller halt, + // then return EFI_DEVICE_ERROR. + // + + if (!IsStatusOK (UhcDev, StatusReg)) { + ClearStatusReg (UhcDev, StatusReg); + *TransferResult = EFI_USB_ERR_SYSTEM; + return EFI_DEVICE_ERROR; + } + + ClearStatusReg (UhcDev, StatusReg); + + // + // generate Setup Stage TD + // + + PtrQH = UhcDev->ConfigQH; + + GenSetupStageTD ( + UhcDev, + DeviceAddress, + 0, + DeviceSpeed, + (UINT8 *) Request, + (UINT8) sizeof (EFI_USB_DEVICE_REQUEST), + &PtrSetupTD + ); + + // + // link setup TD structures to QH structure + // + LinkTDToQH (PtrQH, PtrSetupTD); + + PtrPreTD = PtrSetupTD; + + // + // Data Stage of Control Transfer + // + switch (TransferDirection) { + + case EfiUsbDataIn: + PktID = INPUT_PACKET_ID; + PtrDataSource = Data; + DataLen = (UINT32) *DataLength; + Ptr = PtrDataSource; + break; + + case EfiUsbDataOut: + PktID = OUTPUT_PACKET_ID; + PtrDataSource = Data; + DataLen = (UINT32) *DataLength; + Ptr = PtrDataSource; + break; + + // + // no data stage + // + case EfiUsbNoData: + if (*DataLength != 0) { + return EFI_INVALID_PARAMETER; + } + + PktID = OUTPUT_PACKET_ID; + PtrDataSource = NULL; + DataLen = 0; + Ptr = NULL; + break; + + default: + return EFI_INVALID_PARAMETER; + } + + DataToggle = 1; + + PtrTD = PtrSetupTD; + while (DataLen > 0) { + // + // create TD structures and link together + // + UINT8 PacketSize; + + // + // PacketSize is the data load size of each TD carries. + // + PacketSize = (UINT8) DataLen; + if (DataLen > MaximumPacketLength) { + PacketSize = MaximumPacketLength; + } + + GenDataTD ( + UhcDev, + DeviceAddress, + 0, + Ptr, + PacketSize, + PktID, + DataToggle, + DeviceSpeed, + &PtrTD + ); + + // + // Link two TDs in vertical depth + // + LinkTDToTD (PtrPreTD, PtrTD); + PtrPreTD = PtrTD; + + DataToggle ^= 1; + Ptr += PacketSize; + DataLen -= PacketSize; + } + + // + // PtrPreTD points to the last TD before the Setup-Stage TD. + // + PtrPreTD = PtrTD; + + // + // Status Stage of Control Transfer + // + if (PktID == OUTPUT_PACKET_ID) { + PktID = INPUT_PACKET_ID; + } else { + PktID = OUTPUT_PACKET_ID; + } + // + // create Status Stage TD structure + // + CreateStatusTD ( + UhcDev, + DeviceAddress, + 0, + PktID, + DeviceSpeed, + &PtrStatusTD + ); + + LinkTDToTD (PtrPreTD, PtrStatusTD); + + // + // Poll QH-TDs execution and get result. + // detail status is returned + // + Status = ExecuteControlTransfer ( + UhcDev, + PtrSetupTD, + DataLength, + TimeOut, + TransferResult + ); + + // + // TRUE means must search other framelistindex + // + SetQHVerticalValidorInvalid(PtrQH, FALSE); + DeleteQueuedTDs (UhcDev, PtrSetupTD); + + // + // if has errors that cause host controller halt, then return EFI_DEVICE_ERROR directly. + // + if (!IsStatusOK (UhcDev, StatusReg)) { + + ClearStatusReg (UhcDev, StatusReg); + *TransferResult |= EFI_USB_ERR_SYSTEM; + return EFI_DEVICE_ERROR; + } + + ClearStatusReg (UhcDev, StatusReg); + + return Status; +} + +/** + Submits bulk transfer to a bulk endpoint of a USB device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB_HOST_CONTROLLER_PPI. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and its direction in bit 7. + @param MaximumPacketLength Maximum packet size the endpoint is capable of + sending or receiving. + @param Data Array of pointers to the buffers of data to transmit + from or receive into. + @param DataLength The lenght of the data buffer. + @param DataToggle On input, the initial data toggle for the transfer; + On output, it is updated to to next data toggle to use of + the subsequent bulk transfer. + @param TimeOut Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + @param TransferResult A pointer to the detailed result information of the + bulk transfer. + + @retval EFI_SUCCESS The transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource. + @retval EFI_INVALID_PARAMETER Parameters are invalid. + @retval EFI_TIMEOUT The transfer failed due to timeout. + @retval EFI_DEVICE_ERROR The transfer failed due to host controller error. + +**/ +EFI_STATUS +EFIAPI +UhcBulkTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +{ + USB_UHC_DEV *UhcDev; + UINT32 StatusReg; + + UINT32 DataLen; + + QH_STRUCT *PtrQH; + TD_STRUCT *PtrFirstTD; + TD_STRUCT *PtrTD; + TD_STRUCT *PtrPreTD; + + UINT8 PktID; + UINT8 *PtrDataSource; + UINT8 *Ptr; + + BOOLEAN IsFirstTD; + + EFI_STATUS Status; + + EFI_USB_DATA_DIRECTION TransferDirection; + + BOOLEAN ShortPacketEnable; + + UINT16 CommandContent; + + UhcDev = PEI_RECOVERY_USB_UHC_DEV_FROM_UHCI_THIS (This); + + // + // Enable the maximum packet size (64bytes) + // that can be used for full speed bandwidth reclamation + // at the end of a frame. + // + CommandContent = USBReadPortW (UhcDev, UhcDev->UsbHostControllerBaseAddress + USBCMD); + if ((CommandContent & USBCMD_MAXP) != USBCMD_MAXP) { + CommandContent |= USBCMD_MAXP; + USBWritePortW (UhcDev, UhcDev->UsbHostControllerBaseAddress + USBCMD, CommandContent); + } + + StatusReg = UhcDev->UsbHostControllerBaseAddress + USBSTS; + + // + // these code lines are added here per complier's strict demand + // + PktID = INPUT_PACKET_ID; + PtrTD = NULL; + PtrFirstTD = NULL; + PtrPreTD = NULL; + DataLen = 0; + Ptr = NULL; + + ShortPacketEnable = FALSE; + + if ((DataLength == 0) || (Data == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + if (MaximumPacketLength != 8 && MaximumPacketLength != 16 + && MaximumPacketLength != 32 && MaximumPacketLength != 64) { + return EFI_INVALID_PARAMETER; + } + // + // if has errors that cause host controller halt, then return EFI_DEVICE_ERROR directly. + // + if (!IsStatusOK (UhcDev, StatusReg)) { + + ClearStatusReg (UhcDev, StatusReg); + *TransferResult = EFI_USB_ERR_SYSTEM; + return EFI_DEVICE_ERROR; + } + + ClearStatusReg (UhcDev, StatusReg); + + if ((EndPointAddress & 0x80) != 0) { + TransferDirection = EfiUsbDataIn; + } else { + TransferDirection = EfiUsbDataOut; + } + + switch (TransferDirection) { + + case EfiUsbDataIn: + ShortPacketEnable = TRUE; + PktID = INPUT_PACKET_ID; + PtrDataSource = Data; + DataLen = (UINT32) *DataLength; + Ptr = PtrDataSource; + break; + + case EfiUsbDataOut: + PktID = OUTPUT_PACKET_ID; + PtrDataSource = Data; + DataLen = (UINT32) *DataLength; + Ptr = PtrDataSource; + break; + + default: + break; + } + + PtrQH = UhcDev->BulkQH; + + IsFirstTD = TRUE; + while (DataLen > 0) { + // + // create TD structures and link together + // + UINT8 PacketSize; + + PacketSize = (UINT8) DataLen; + if (DataLen > MaximumPacketLength) { + PacketSize = MaximumPacketLength; + } + + GenDataTD ( + UhcDev, + DeviceAddress, + EndPointAddress, + Ptr, + PacketSize, + PktID, + *DataToggle, + USB_FULL_SPEED_DEVICE, + &PtrTD + ); + + // + // Enable short packet detection. + // (default action is disabling short packet detection) + // + if (ShortPacketEnable) { + EnableorDisableTDShortPacket (PtrTD, TRUE); + } + + if (IsFirstTD) { + PtrFirstTD = PtrTD; + PtrFirstTD->PtrNextTD = NULL; + IsFirstTD = FALSE; + } else { + // + // Link two TDs in vertical depth + // + LinkTDToTD (PtrPreTD, PtrTD); + } + + PtrPreTD = PtrTD; + + *DataToggle ^= 1; + Ptr += PacketSize; + DataLen -= PacketSize; + } + // + // link TD structures to QH structure + // + LinkTDToQH (PtrQH, PtrFirstTD); + + // + // Execute QH-TD and get result + // + // + // detail status is put into the Result field in the pIRP + // the Data Toggle value is also re-updated to the value + // of the last successful TD + // + Status = ExecBulkTransfer ( + UhcDev, + PtrFirstTD, + DataLength, + DataToggle, + TimeOut, + TransferResult + ); + + // + // Delete Bulk transfer TD structure + // + DeleteQueuedTDs (UhcDev, PtrFirstTD); + + // + // if has errors that cause host controller halt, then return EFI_DEVICE_ERROR directly. + // + if (!IsStatusOK (UhcDev, StatusReg)) { + + ClearStatusReg (UhcDev, StatusReg); + *TransferResult |= EFI_USB_ERR_SYSTEM; + return EFI_DEVICE_ERROR; + } + + ClearStatusReg (UhcDev, StatusReg); + + return Status; +} + +/** + Retrieves the number of root hub ports. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the + PEI_USB_HOST_CONTROLLER_PPI. + @param[out] PortNumber The pointer to the number of the root hub ports. + + @retval EFI_SUCCESS The port number was retrieved successfully. + @retval EFI_INVALID_PARAMETER PortNumber is NULL. + +**/ +EFI_STATUS +EFIAPI +UhcGetRootHubPortNumber ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + OUT UINT8 *PortNumber + ) +{ + USB_UHC_DEV *UhcDev; + UINT32 PSAddr; + UINT16 RHPortControl; + UINT32 Index; + + UhcDev = PEI_RECOVERY_USB_UHC_DEV_FROM_UHCI_THIS (This); + + if (PortNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + + *PortNumber = 0; + + for (Index = 0; Index < 2; Index++) { + PSAddr = UhcDev->UsbHostControllerBaseAddress + USBPORTSC1 + Index * 2; + RHPortControl = USBReadPortW (UhcDev, PSAddr); + // + // Port Register content is valid + // + if (RHPortControl != 0xff) { + (*PortNumber)++; + } + } + + return EFI_SUCCESS; +} + +/** + Retrieves the current status of a USB root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB_HOST_CONTROLLER_PPI. + @param PortNumber The root hub port to retrieve the state from. + @param PortStatus Variable to receive the port state. + + @retval EFI_SUCCESS The status of the USB root hub port specified. + by PortNumber was returned in PortStatus. + @retval EFI_INVALID_PARAMETER PortNumber is invalid. + +**/ +EFI_STATUS +EFIAPI +UhcGetRootHubPortStatus ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus + ) +{ + USB_UHC_DEV *UhcDev; + UINT32 PSAddr; + UINT16 RHPortStatus; + UINT8 TotalPortNumber; + + if (PortStatus == NULL) { + return EFI_INVALID_PARAMETER; + } + + UhcGetRootHubPortNumber (PeiServices, This, &TotalPortNumber); + if (PortNumber > TotalPortNumber) { + return EFI_INVALID_PARAMETER; + } + + UhcDev = PEI_RECOVERY_USB_UHC_DEV_FROM_UHCI_THIS (This); + PSAddr = UhcDev->UsbHostControllerBaseAddress + USBPORTSC1 + PortNumber * 2; + + PortStatus->PortStatus = 0; + PortStatus->PortChangeStatus = 0; + + RHPortStatus = USBReadPortW (UhcDev, PSAddr); + + // + // Current Connect Status + // + if ((RHPortStatus & USBPORTSC_CCS) != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION; + } + // + // Port Enabled/Disabled + // + if ((RHPortStatus & USBPORTSC_PED) != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_ENABLE; + } + // + // Port Suspend + // + if ((RHPortStatus & USBPORTSC_SUSP) != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND; + } + // + // Port Reset + // + if ((RHPortStatus & USBPORTSC_PR) != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_RESET; + } + // + // Low Speed Device Attached + // + if ((RHPortStatus & USBPORTSC_LSDA) != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED; + } + // + // Fill Port Status Change bits + // + // + // Connect Status Change + // + if ((RHPortStatus & USBPORTSC_CSC) != 0) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION; + } + // + // Port Enabled/Disabled Change + // + if ((RHPortStatus & USBPORTSC_PEDC) != 0) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE; + } + + return EFI_SUCCESS; +} + +/** + Sets a feature for the specified root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES + @param This The pointer of PEI_USB_HOST_CONTROLLER_PPI + @param PortNumber Root hub port to set. + @param PortFeature Feature to set. + + @retval EFI_SUCCESS The feature specified by PortFeature was set. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + @retval EFI_TIMEOUT The time out occurred. + +**/ +EFI_STATUS +EFIAPI +UhcSetRootHubPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + USB_UHC_DEV *UhcDev; + UINT32 PSAddr; + UINT32 CommandRegAddr; + UINT16 RHPortControl; + UINT8 TotalPortNumber; + + UhcGetRootHubPortNumber (PeiServices, This, &TotalPortNumber); + if (PortNumber > TotalPortNumber) { + return EFI_INVALID_PARAMETER; + } + + UhcDev = PEI_RECOVERY_USB_UHC_DEV_FROM_UHCI_THIS (This); + PSAddr = UhcDev->UsbHostControllerBaseAddress + USBPORTSC1 + PortNumber * 2; + CommandRegAddr = UhcDev->UsbHostControllerBaseAddress + USBCMD; + + RHPortControl = USBReadPortW (UhcDev, PSAddr); + + switch (PortFeature) { + + case EfiUsbPortSuspend: + if ((USBReadPortW (UhcDev, CommandRegAddr) & USBCMD_EGSM) == 0) { + // + // if global suspend is not active, can set port suspend + // + RHPortControl &= 0xfff5; + RHPortControl |= USBPORTSC_SUSP; + } + break; + + case EfiUsbPortReset: + RHPortControl &= 0xfff5; + RHPortControl |= USBPORTSC_PR; + // + // Set the reset bit + // + break; + + case EfiUsbPortPower: + break; + + case EfiUsbPortEnable: + RHPortControl &= 0xfff5; + RHPortControl |= USBPORTSC_PED; + break; + + default: + return EFI_INVALID_PARAMETER; + } + + USBWritePortW (UhcDev, PSAddr, RHPortControl); + + return EFI_SUCCESS; +} + +/** + Clears a feature for the specified root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB_HOST_CONTROLLER_PPI. + @param PortNumber Specifies the root hub port whose feature + is requested to be cleared. + @param PortFeature Indicates the feature selector associated with the + feature clear request. + + @retval EFI_SUCCESS The feature specified by PortFeature was cleared + for the USB root hub port specified by PortNumber. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + +**/ +EFI_STATUS +EFIAPI +UhcClearRootHubPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + USB_UHC_DEV *UhcDev; + UINT32 PSAddr; + UINT16 RHPortControl; + UINT8 TotalPortNumber; + + UhcGetRootHubPortNumber (PeiServices, This, &TotalPortNumber); + + if (PortNumber > TotalPortNumber) { + return EFI_INVALID_PARAMETER; + } + + UhcDev = PEI_RECOVERY_USB_UHC_DEV_FROM_UHCI_THIS (This); + PSAddr = UhcDev->UsbHostControllerBaseAddress + USBPORTSC1 + PortNumber * 2; + + RHPortControl = USBReadPortW (UhcDev, PSAddr); + + switch (PortFeature) { + // + // clear PORT_ENABLE feature means disable port. + // + case EfiUsbPortEnable: + RHPortControl &= 0xfff5; + RHPortControl &= ~USBPORTSC_PED; + break; + + // + // clear PORT_SUSPEND feature means resume the port. + // (cause a resume on the specified port if in suspend mode) + // + case EfiUsbPortSuspend: + RHPortControl &= 0xfff5; + RHPortControl &= ~USBPORTSC_SUSP; + break; + + // + // no operation + // + case EfiUsbPortPower: + break; + + // + // clear PORT_RESET means clear the reset signal. + // + case EfiUsbPortReset: + RHPortControl &= 0xfff5; + RHPortControl &= ~USBPORTSC_PR; + break; + + // + // clear connect status change + // + case EfiUsbPortConnectChange: + RHPortControl &= 0xfff5; + RHPortControl |= USBPORTSC_CSC; + break; + + // + // clear enable/disable status change + // + case EfiUsbPortEnableChange: + RHPortControl &= 0xfff5; + RHPortControl |= USBPORTSC_PEDC; + break; + + // + // root hub does not support this request + // + case EfiUsbPortSuspendChange: + break; + + // + // root hub does not support this request + // + case EfiUsbPortOverCurrentChange: + break; + + // + // root hub does not support this request + // + case EfiUsbPortResetChange: + break; + + default: + return EFI_INVALID_PARAMETER; + } + + USBWritePortW (UhcDev, PSAddr, RHPortControl); + + return EFI_SUCCESS; +} + +/** + Initialize UHCI. + + @param UhcDev UHCI Device. + + @retval EFI_SUCCESS UHCI successfully initialized. + @retval EFI_OUT_OF_RESOURCES Resource can not be allocated. + +**/ +EFI_STATUS +InitializeUsbHC ( + IN USB_UHC_DEV *UhcDev + ) +{ + EFI_STATUS Status; + UINT32 FrameListBaseAddrReg; + UINT32 CommandReg; + UINT16 Command; + + // + // Create and Initialize Frame List For the Host Controller. + // + Status = CreateFrameList (UhcDev); + if (EFI_ERROR (Status)) { + return Status; + } + + FrameListBaseAddrReg = UhcDev->UsbHostControllerBaseAddress + USBFLBASEADD; + CommandReg = UhcDev->UsbHostControllerBaseAddress + USBCMD; + + // + // Set Frame List Base Address to the specific register to inform the hardware. + // + SetFrameListBaseAddress (UhcDev, FrameListBaseAddrReg, (UINT32) (UINTN) (UhcDev->FrameListEntry)); + + Command = USBReadPortW (UhcDev, CommandReg); + Command |= USBCMD_GRESET; + USBWritePortW (UhcDev, CommandReg, Command); + + MicroSecondDelay (50 * 1000); + + + Command &= ~USBCMD_GRESET; + + USBWritePortW (UhcDev, CommandReg, Command); + + // + //UHCI spec page120 reset recovery time + // + MicroSecondDelay (20 * 1000); + + // + // Set Run/Stop bit to 1. + // + Command = USBReadPortW (UhcDev, CommandReg); + Command |= USBCMD_RS | USBCMD_MAXP; + USBWritePortW (UhcDev, CommandReg, Command); + + return EFI_SUCCESS; +} + +/** + Create Frame List Structure. + + @param UhcDev UHCI device. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +CreateFrameList ( + USB_UHC_DEV *UhcDev + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS FrameListBaseAddr; + FRAMELIST_ENTRY *FrameListPtr; + UINTN Index; + + // + // The Frame List ocupies 4K bytes, + // and must be aligned on 4-Kbyte boundaries. + // + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + 1, + &FrameListBaseAddr + ); + + if (Status != EFI_SUCCESS) { + return EFI_OUT_OF_RESOURCES; + } + + // + //Create Control QH and Bulk QH and link them into Framelist Entry + // + Status = CreateQH(UhcDev, &UhcDev->ConfigQH); + if (Status != EFI_SUCCESS) { + return EFI_OUT_OF_RESOURCES; + } + + Status = CreateQH(UhcDev, &UhcDev->BulkQH); + if (Status != EFI_SUCCESS) { + return EFI_OUT_OF_RESOURCES; + } + + // + //Set the corresponding QH pointer + // + SetQHHorizontalLinkPtr(UhcDev->ConfigQH, UhcDev->BulkQH); + SetQHHorizontalQHorTDSelect (UhcDev->ConfigQH, TRUE); + SetQHHorizontalValidorInvalid (UhcDev->ConfigQH, TRUE); + + UhcDev->FrameListEntry = (FRAMELIST_ENTRY *) ((UINTN) FrameListBaseAddr); + + FrameListPtr = UhcDev->FrameListEntry; + + for (Index = 0; Index < 1024; Index++) { + FrameListPtr->FrameListPtrTerminate = 0; + FrameListPtr->FrameListPtr = (UINT32)(UINTN)UhcDev->ConfigQH >> 4; + FrameListPtr->FrameListPtrQSelect = 1; + FrameListPtr->FrameListRsvd = 0; + FrameListPtr ++; + } + + return EFI_SUCCESS; +} + +/** + Read a 16bit width data from Uhc HC IO space register. + + @param UhcDev The UHCI device. + @param Port The IO space address of the register. + + @retval the register content read. + +**/ +UINT16 +USBReadPortW ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 Port + ) +{ + return IoRead16 (Port); +} + +/** + Write a 16bit width data into Uhc HC IO space register. + + @param UhcDev The UHCI device. + @param Port The IO space address of the register. + @param Data The data written into the register. + +**/ +VOID +USBWritePortW ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 Port, + IN UINT16 Data + ) +{ + IoWrite16 (Port, Data); +} + +/** + Write a 32bit width data into Uhc HC IO space register. + + @param UhcDev The UHCI device. + @param Port The IO space address of the register. + @param Data The data written into the register. + +**/ +VOID +USBWritePortDW ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 Port, + IN UINT32 Data + ) +{ + IoWrite32 (Port, Data); +} + +/** + Clear the content of UHCI's Status Register. + + @param UhcDev The UHCI device. + @param StatusAddr The IO space address of the register. + +**/ +VOID +ClearStatusReg ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 StatusAddr + ) +{ + // + // Clear the content of UHCI's Status Register + // + USBWritePortW (UhcDev, StatusAddr, 0x003F); +} + +/** + Check whether the host controller operates well. + + @param UhcDev The UHCI device. + @param StatusRegAddr The io address of status register. + + @retval TRUE Host controller is working. + @retval FALSE Host controller is halted or system error. + +**/ +BOOLEAN +IsStatusOK ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 StatusRegAddr + ) +{ + UINT16 StatusValue; + + StatusValue = USBReadPortW (UhcDev, StatusRegAddr); + + if ((StatusValue & (USBSTS_HCPE | USBSTS_HSE | USBSTS_HCH)) != 0) { + return FALSE; + } else { + return TRUE; + } +} + +/** + Get Current Frame Number. + + @param UhcDev The UHCI device. + @param FrameNumberAddr The address of frame list register. + + @retval The content of the frame list register. + +**/ +UINT16 +GetCurrentFrameNumber ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 FrameNumberAddr + ) +{ + // + // Gets value in the USB frame number register. + // + return (UINT16) (USBReadPortW (UhcDev, FrameNumberAddr) & 0x03FF); +} + +/** + Set Frame List Base Address. + + @param UhcDev The UHCI device. + @param FrameListRegAddr The address of frame list register. + @param Addr The address of frame list table. + +**/ +VOID +SetFrameListBaseAddress ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 FrameListRegAddr, + IN UINT32 Addr + ) +{ + // + // Sets value in the USB Frame List Base Address register. + // + USBWritePortDW (UhcDev, FrameListRegAddr, (UINT32) (Addr & 0xFFFFF000)); +} + +/** + Create QH and initialize. + + @param UhcDev The UHCI device. + @param PtrQH Place to store QH_STRUCT pointer. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +CreateQH ( + IN USB_UHC_DEV *UhcDev, + OUT QH_STRUCT **PtrQH + ) +{ + EFI_STATUS Status; + + // + // allocate align memory for QH_STRUCT + // + Status = AllocateTDorQHStruct (UhcDev, sizeof(QH_STRUCT), (void **)PtrQH); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + // + // init each field of the QH_STRUCT + // + SetQHHorizontalValidorInvalid (*PtrQH, FALSE); + SetQHVerticalValidorInvalid (*PtrQH, FALSE); + + return EFI_SUCCESS; +} + +/** + Set the horizontal link pointer in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param PtrNext Place to the next QH_STRUCT. + +**/ +VOID +SetQHHorizontalLinkPtr ( + IN QH_STRUCT *PtrQH, + IN VOID *PtrNext + ) +{ + // + // Since the QH_STRUCT is aligned on 16-byte boundaries, + // Only the highest 28bit of the address is valid + // (take 32bit address as an example). + // + PtrQH->QueueHead.QHHorizontalPtr = (UINT32) (UINTN) PtrNext >> 4; +} + +/** + Get the horizontal link pointer in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + + @retval The horizontal link pointer in QH. + +**/ +VOID * +GetQHHorizontalLinkPtr ( + IN QH_STRUCT *PtrQH + ) +{ + // + // Restore the 28bit address to 32bit address + // (take 32bit address as an example) + // + return (VOID *) (UINTN) ((PtrQH->QueueHead.QHHorizontalPtr) << 4); +} + +/** + Set a QH or TD horizontally to be connected with a specific QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param IsQH Specify QH or TD is connected. + +**/ +VOID +SetQHHorizontalQHorTDSelect ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN IsQH + ) +{ + // + // if QH is connected, the specified bit is set, + // if TD is connected, the specified bit is cleared. + // + PtrQH->QueueHead.QHHorizontalQSelect = IsQH ? 1 : 0; +} + +/** + Set the horizontal validor bit in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param IsValid Specify the horizontal linker is valid or not. + +**/ +VOID +SetQHHorizontalValidorInvalid ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN IsValid + ) +{ + // + // Valid means the horizontal link pointer is valid, + // else, it's invalid. + // + PtrQH->QueueHead.QHHorizontalTerminate = IsValid ? 0 : 1; +} + +/** + Set the vertical link pointer in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param PtrNext Place to the next QH_STRUCT. + +**/ +VOID +SetQHVerticalLinkPtr ( + IN QH_STRUCT *PtrQH, + IN VOID *PtrNext + ) +{ + // + // Since the QH_STRUCT is aligned on 16-byte boundaries, + // Only the highest 28bit of the address is valid + // (take 32bit address as an example). + // + PtrQH->QueueHead.QHVerticalPtr = (UINT32) (UINTN) PtrNext >> 4; +} + +/** + Set a QH or TD vertically to be connected with a specific QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param IsQH Specify QH or TD is connected. + +**/ +VOID +SetQHVerticalQHorTDSelect ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN IsQH + ) +{ + // + // Set the specified bit if the Vertical Link Pointer pointing to a QH, + // Clear the specified bit if the Vertical Link Pointer pointing to a TD. + // + PtrQH->QueueHead.QHVerticalQSelect = IsQH ? 1 : 0; +} + +/** + Set the vertical validor bit in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param IsValid Specify the vertical linker is valid or not. + +**/ +VOID +SetQHVerticalValidorInvalid ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN IsValid + ) +{ + // + // If TRUE, meaning the Vertical Link Pointer field is valid, + // else, the field is invalid. + // + PtrQH->QueueHead.QHVerticalTerminate = IsValid ? 0 : 1; +} + +/** + Get the vertical validor bit in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + + @retval The vertical linker is valid or not. + +**/ +BOOLEAN +GetQHHorizontalValidorInvalid ( + IN QH_STRUCT *PtrQH + ) +{ + // + // If TRUE, meaning the Horizontal Link Pointer field is valid, + // else, the field is invalid. + // + return (BOOLEAN) (!(PtrQH->QueueHead.QHHorizontalTerminate)); +} + +/** + Allocate TD or QH Struct. + + @param UhcDev The UHCI device. + @param Size The size of allocation. + @param PtrStruct Place to store TD_STRUCT pointer. + + @return EFI_SUCCESS Allocate successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + +**/ +EFI_STATUS +AllocateTDorQHStruct ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 Size, + OUT VOID **PtrStruct + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + *PtrStruct = NULL; + + Status = UhcAllocatePool ( + UhcDev, + (UINT8 **) PtrStruct, + Size + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ZeroMem (*PtrStruct, Size); + + return Status; +} + +/** + Create a TD Struct. + + @param UhcDev The UHCI device. + @param PtrTD Place to store TD_STRUCT pointer. + + @return EFI_SUCCESS Allocate successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + +**/ +EFI_STATUS +CreateTD ( + IN USB_UHC_DEV *UhcDev, + OUT TD_STRUCT **PtrTD + ) +{ + EFI_STATUS Status; + // + // create memory for TD_STRUCT, and align the memory. + // + Status = AllocateTDorQHStruct (UhcDev, sizeof(TD_STRUCT), (void **)PtrTD); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Make TD ready. + // + SetTDLinkPtrValidorInvalid (*PtrTD, FALSE); + + return EFI_SUCCESS; +} + +/** + Generate Setup Stage TD. + + @param UhcDev The UHCI device. + @param DevAddr Device address. + @param Endpoint Endpoint number. + @param DeviceSpeed Device Speed. + @param DevRequest Device reuquest. + @param RequestLen Request length. + @param PtrTD TD_STRUCT generated. + + @return EFI_SUCCESS Generate setup stage TD successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + +**/ +EFI_STATUS +GenSetupStageTD ( + IN USB_UHC_DEV *UhcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 DeviceSpeed, + IN UINT8 *DevRequest, + IN UINT8 RequestLen, + OUT TD_STRUCT **PtrTD + ) +{ + TD_STRUCT *TdStruct; + EFI_STATUS Status; + + Status = CreateTD (UhcDev, &TdStruct); + if (EFI_ERROR (Status)) { + return Status; + } + + SetTDLinkPtr (TdStruct, NULL); + + // + // Depth first fashion + // + SetTDLinkPtrDepthorBreadth (TdStruct, TRUE); + + // + // initialize as the last TD in the QH context, + // this field will be updated in the TD linkage process. + // + SetTDLinkPtrValidorInvalid (TdStruct, FALSE); + + // + // Disable Short Packet Detection by default + // + EnableorDisableTDShortPacket (TdStruct, FALSE); + + // + // Max error counter is 3, retry 3 times when error encountered. + // + SetTDControlErrorCounter (TdStruct, 3); + + // + // set device speed attribute + // (TRUE - Slow Device; FALSE - Full Speed Device) + // + switch (DeviceSpeed) { + case USB_SLOW_SPEED_DEVICE: + SetTDLoworFullSpeedDevice (TdStruct, TRUE); + break; + + case USB_FULL_SPEED_DEVICE: + SetTDLoworFullSpeedDevice (TdStruct, FALSE); + break; + } + // + // Non isochronous transfer TD + // + SetTDControlIsochronousorNot (TdStruct, FALSE); + + // + // Interrupt On Complete bit be set to zero, + // Disable IOC interrupt. + // + SetorClearTDControlIOC (TdStruct, FALSE); + + // + // Set TD Active bit + // + SetTDStatusActiveorInactive (TdStruct, TRUE); + + SetTDTokenMaxLength (TdStruct, RequestLen); + + SetTDTokenDataToggle0 (TdStruct); + + SetTDTokenEndPoint (TdStruct, Endpoint); + + SetTDTokenDeviceAddress (TdStruct, DevAddr); + + SetTDTokenPacketID (TdStruct, SETUP_PACKET_ID); + + TdStruct->PtrTDBuffer = (UINT8 *) DevRequest; + TdStruct->TDBufferLength = RequestLen; + SetTDDataBuffer (TdStruct); + + *PtrTD = TdStruct; + + return EFI_SUCCESS; +} + +/** + Generate Data Stage TD. + + @param UhcDev The UHCI device. + @param DevAddr Device address. + @param Endpoint Endpoint number. + @param PtrData Data buffer. + @param Len Data length. + @param PktID PacketID. + @param Toggle Data toggle value. + @param DeviceSpeed Device Speed. + @param PtrTD TD_STRUCT generated. + + @return EFI_SUCCESS Generate data stage TD successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + +**/ +EFI_STATUS +GenDataTD ( + IN USB_UHC_DEV *UhcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 *PtrData, + IN UINT8 Len, + IN UINT8 PktID, + IN UINT8 Toggle, + IN UINT8 DeviceSpeed, + OUT TD_STRUCT **PtrTD + ) +{ + TD_STRUCT *TdStruct; + EFI_STATUS Status; + + Status = CreateTD (UhcDev, &TdStruct); + if (EFI_ERROR (Status)) { + return Status; + } + + SetTDLinkPtr (TdStruct, NULL); + + // + // Depth first fashion + // + SetTDLinkPtrDepthorBreadth (TdStruct, TRUE); + + // + // Link pointer pointing to TD struct + // + SetTDLinkPtrQHorTDSelect (TdStruct, FALSE); + + // + // initialize as the last TD in the QH context, + // this field will be updated in the TD linkage process. + // + SetTDLinkPtrValidorInvalid (TdStruct, FALSE); + + // + // Disable short packet detect + // + EnableorDisableTDShortPacket (TdStruct, FALSE); + // + // Max error counter is 3 + // + SetTDControlErrorCounter (TdStruct, 3); + + // + // set device speed attribute + // (TRUE - Slow Device; FALSE - Full Speed Device) + // + switch (DeviceSpeed) { + case USB_SLOW_SPEED_DEVICE: + SetTDLoworFullSpeedDevice (TdStruct, TRUE); + break; + + case USB_FULL_SPEED_DEVICE: + SetTDLoworFullSpeedDevice (TdStruct, FALSE); + break; + } + // + // Non isochronous transfer TD + // + SetTDControlIsochronousorNot (TdStruct, FALSE); + + // + // Disable Interrupt On Complete + // Disable IOC interrupt. + // + SetorClearTDControlIOC (TdStruct, FALSE); + + // + // Set Active bit + // + SetTDStatusActiveorInactive (TdStruct, TRUE); + + SetTDTokenMaxLength (TdStruct, Len); + + if (Toggle != 0) { + SetTDTokenDataToggle1 (TdStruct); + } else { + SetTDTokenDataToggle0 (TdStruct); + } + + SetTDTokenEndPoint (TdStruct, Endpoint); + + SetTDTokenDeviceAddress (TdStruct, DevAddr); + + SetTDTokenPacketID (TdStruct, PktID); + + TdStruct->PtrTDBuffer = (UINT8 *) PtrData; + TdStruct->TDBufferLength = Len; + SetTDDataBuffer (TdStruct); + + *PtrTD = TdStruct; + + return EFI_SUCCESS; +} + +/** + Generate Status Stage TD. + + @param UhcDev The UHCI device. + @param DevAddr Device address. + @param Endpoint Endpoint number. + @param PktID PacketID. + @param DeviceSpeed Device Speed. + @param PtrTD TD_STRUCT generated. + + @return EFI_SUCCESS Generate status stage TD successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + +**/ +EFI_STATUS +CreateStatusTD ( + IN USB_UHC_DEV *UhcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 PktID, + IN UINT8 DeviceSpeed, + OUT TD_STRUCT **PtrTD + ) +{ + TD_STRUCT *PtrTDStruct; + EFI_STATUS Status; + + Status = CreateTD (UhcDev, &PtrTDStruct); + if (EFI_ERROR (Status)) { + return Status; + } + + SetTDLinkPtr (PtrTDStruct, NULL); + + // + // Depth first fashion + // + SetTDLinkPtrDepthorBreadth (PtrTDStruct, TRUE); + + // + // initialize as the last TD in the QH context, + // this field will be updated in the TD linkage process. + // + SetTDLinkPtrValidorInvalid (PtrTDStruct, FALSE); + + // + // Disable short packet detect + // + EnableorDisableTDShortPacket (PtrTDStruct, FALSE); + + // + // Max error counter is 3 + // + SetTDControlErrorCounter (PtrTDStruct, 3); + + // + // set device speed attribute + // (TRUE - Slow Device; FALSE - Full Speed Device) + // + switch (DeviceSpeed) { + case USB_SLOW_SPEED_DEVICE: + SetTDLoworFullSpeedDevice (PtrTDStruct, TRUE); + break; + + case USB_FULL_SPEED_DEVICE: + SetTDLoworFullSpeedDevice (PtrTDStruct, FALSE); + break; + } + // + // Non isochronous transfer TD + // + SetTDControlIsochronousorNot (PtrTDStruct, FALSE); + + // + // Disable Interrupt On Complete + // Disable IOC interrupt. + // + SetorClearTDControlIOC (PtrTDStruct, FALSE); + + // + // Set TD Active bit + // + SetTDStatusActiveorInactive (PtrTDStruct, TRUE); + + SetTDTokenMaxLength (PtrTDStruct, 0); + + SetTDTokenDataToggle1 (PtrTDStruct); + + SetTDTokenEndPoint (PtrTDStruct, Endpoint); + + SetTDTokenDeviceAddress (PtrTDStruct, DevAddr); + + SetTDTokenPacketID (PtrTDStruct, PktID); + + PtrTDStruct->PtrTDBuffer = NULL; + PtrTDStruct->TDBufferLength = 0; + SetTDDataBuffer (PtrTDStruct); + + *PtrTD = PtrTDStruct; + + return EFI_SUCCESS; +} + +/** + Set the link pointer validor bit in TD. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsValid Specify the linker pointer is valid or not. + +**/ +VOID +SetTDLinkPtrValidorInvalid ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsValid + ) +{ + // + // Valid means the link pointer is valid, + // else, it's invalid. + // + PtrTDStruct->TDData.TDLinkPtrTerminate = (IsValid ? 0 : 1); +} + +/** + Set the Link Pointer pointing to a QH or TD. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsQH Specify QH or TD is connected. + +**/ +VOID +SetTDLinkPtrQHorTDSelect ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsQH + ) +{ + // + // Indicate whether the Link Pointer pointing to a QH or TD + // + PtrTDStruct->TDData.TDLinkPtrQSelect = (IsQH ? 1 : 0); +} + +/** + Set the traverse is depth-first or breadth-first. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsDepth Specify the traverse is depth-first or breadth-first. + +**/ +VOID +SetTDLinkPtrDepthorBreadth ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsDepth + ) +{ + // + // If TRUE, indicating the host controller should process in depth first fashion, + // else, the host controller should process in breadth first fashion + // + PtrTDStruct->TDData.TDLinkPtrDepthSelect = (IsDepth ? 1 : 0); +} + +/** + Set TD Link Pointer in TD. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param PtrNext Place to the next TD_STRUCT. + +**/ +VOID +SetTDLinkPtr ( + IN TD_STRUCT *PtrTDStruct, + IN VOID *PtrNext + ) +{ + // + // Set TD Link Pointer. Since QH,TD align on 16-byte boundaries, + // only the highest 28 bits are valid. (if take 32bit address as an example) + // + PtrTDStruct->TDData.TDLinkPtr = (UINT32) (UINTN) PtrNext >> 4; +} + +/** + Get TD Link Pointer. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval Get TD Link Pointer in TD. + +**/ +VOID * +GetTDLinkPtr ( + IN TD_STRUCT *PtrTDStruct + ) +{ + // + // Get TD Link Pointer. Restore it back to 32bit + // (if take 32bit address as an example) + // + return (VOID *) (UINTN) ((PtrTDStruct->TDData.TDLinkPtr) << 4); +} + +/** + Get the information about whether the Link Pointer field pointing to + a QH or a TD. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval whether the Link Pointer field pointing to a QH or a TD. + +**/ +BOOLEAN +IsTDLinkPtrQHOrTD ( + IN TD_STRUCT *PtrTDStruct + ) +{ + // + // Get the information about whether the Link Pointer field pointing to + // a QH or a TD. + // + return (BOOLEAN) (PtrTDStruct->TDData.TDLinkPtrQSelect); +} + +/** + Enable/Disable short packet detection mechanism. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsEnable Enable or disable short packet detection mechanism. + +**/ +VOID +EnableorDisableTDShortPacket ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsEnable + ) +{ + // + // TRUE means enable short packet detection mechanism. + // + PtrTDStruct->TDData.TDStatusSPD = (IsEnable ? 1 : 0); +} + +/** + Set the max error counter in TD. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param MaxErrors The number of allowable error. + +**/ +VOID +SetTDControlErrorCounter ( + IN TD_STRUCT *PtrTDStruct, + IN UINT8 MaxErrors + ) +{ + // + // valid value of MaxErrors is 0,1,2,3 + // + if (MaxErrors > 3) { + MaxErrors = 3; + } + + PtrTDStruct->TDData.TDStatusErr = MaxErrors; +} + +/** + Set the TD is targeting a low-speed device or not. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsLowSpeedDevice Whether The device is low-speed. + +**/ +VOID +SetTDLoworFullSpeedDevice ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsLowSpeedDevice + ) +{ + // + // TRUE means the TD is targeting at a Low-speed device + // + PtrTDStruct->TDData.TDStatusLS = (IsLowSpeedDevice ? 1 : 0); +} + +/** + Set the TD is isochronous transfer type or not. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsIsochronous Whether the transaction isochronous transfer type. + +**/ +VOID +SetTDControlIsochronousorNot ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsIsochronous + ) +{ + // + // TRUE means the TD belongs to Isochronous transfer type. + // + PtrTDStruct->TDData.TDStatusIOS = (IsIsochronous ? 1 : 0); +} + +/** + Set if UCHI should issue an interrupt on completion of the frame + in which this TD is executed + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsSet Whether HC should issue an interrupt on completion. + +**/ +VOID +SetorClearTDControlIOC ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsSet + ) +{ + // + // If this bit is set, it indicates that the host controller should issue + // an interrupt on completion of the frame in which this TD is executed. + // + PtrTDStruct->TDData.TDStatusIOC = IsSet ? 1 : 0; +} + +/** + Set if the TD is active and can be executed. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsActive Whether the TD is active and can be executed. + +**/ +VOID +SetTDStatusActiveorInactive ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsActive + ) +{ + // + // If this bit is set, it indicates that the TD is active and can be + // executed. + // + if (IsActive) { + PtrTDStruct->TDData.TDStatus |= 0x80; + } else { + PtrTDStruct->TDData.TDStatus &= 0x7F; + } +} + +/** + Specifies the maximum number of data bytes allowed for the transfer. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param MaxLen The maximum number of data bytes allowed. + + @retval The allowed maximum number of data. +**/ +UINT16 +SetTDTokenMaxLength ( + IN TD_STRUCT *PtrTDStruct, + IN UINT16 MaxLen + ) +{ + // + // Specifies the maximum number of data bytes allowed for the transfer. + // the legal value extent is 0 ~ 0x500. + // + if (MaxLen > 0x500) { + MaxLen = 0x500; + } + + PtrTDStruct->TDData.TDTokenMaxLen = MaxLen - 1; + + return MaxLen; +} + +/** + Set the data toggle bit to DATA1. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + +**/ +VOID +SetTDTokenDataToggle1 ( + IN TD_STRUCT *PtrTDStruct + ) +{ + // + // Set the data toggle bit to DATA1 + // + PtrTDStruct->TDData.TDTokenDataToggle = 1; +} + +/** + Set the data toggle bit to DATA0. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + +**/ +VOID +SetTDTokenDataToggle0 ( + IN TD_STRUCT *PtrTDStruct + ) +{ + // + // Set the data toggle bit to DATA0 + // + PtrTDStruct->TDData.TDTokenDataToggle = 0; +} + +/** + Set EndPoint Number the TD is targeting at. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param EndPoint The Endport number of the target. + +**/ +VOID +SetTDTokenEndPoint ( + IN TD_STRUCT *PtrTDStruct, + IN UINTN EndPoint + ) +{ + // + // Set EndPoint Number the TD is targeting at. + // + PtrTDStruct->TDData.TDTokenEndPt = (UINT8) EndPoint; +} + +/** + Set Device Address the TD is targeting at. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param DevAddr The Device Address of the target. + +**/ +VOID +SetTDTokenDeviceAddress ( + IN TD_STRUCT *PtrTDStruct, + IN UINTN DevAddr + ) +{ + // + // Set Device Address the TD is targeting at. + // + PtrTDStruct->TDData.TDTokenDevAddr = (UINT8) DevAddr; +} + +/** + Set Packet Identification the TD is targeting at. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param PacketID The Packet Identification of the target. + +**/ +VOID +SetTDTokenPacketID ( + IN TD_STRUCT *PtrTDStruct, + IN UINT8 PacketID + ) +{ + // + // Set the Packet Identification to be used for this transaction. + // + PtrTDStruct->TDData.TDTokenPID = PacketID; +} + +/** + Set the beginning address of the data buffer that will be used + during the transaction. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + +**/ +VOID +SetTDDataBuffer ( + IN TD_STRUCT *PtrTDStruct + ) +{ + // + // Set the beginning address of the data buffer that will be used + // during the transaction. + // + PtrTDStruct->TDData.TDBufferPtr = (UINT32) (UINTN) (PtrTDStruct->PtrTDBuffer); +} + +/** + Detect whether the TD is active. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The TD is active or not. + +**/ +BOOLEAN +IsTDStatusActive ( + IN TD_STRUCT *PtrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether the TD is active. + // + TDStatus = (UINT8) (PtrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x80); +} + +/** + Detect whether the TD is stalled. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The TD is stalled or not. + +**/ +BOOLEAN +IsTDStatusStalled ( + IN TD_STRUCT *PtrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether the device/endpoint addressed by this TD is stalled. + // + TDStatus = (UINT8) (PtrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x40); +} + +/** + Detect whether Data Buffer Error is happened. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The Data Buffer Error is happened or not. + +**/ +BOOLEAN +IsTDStatusBufferError ( + IN TD_STRUCT *PtrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether Data Buffer Error is happened. + // + TDStatus = (UINT8) (PtrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x20); +} + +/** + Detect whether Babble Error is happened. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The Babble Error is happened or not. + +**/ +BOOLEAN +IsTDStatusBabbleError ( + IN TD_STRUCT *PtrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether Babble Error is happened. + // + TDStatus = (UINT8) (PtrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x10); +} + +/** + Detect whether NAK is received. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The NAK is received or not. + +**/ +BOOLEAN +IsTDStatusNAKReceived ( + IN TD_STRUCT *PtrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether NAK is received. + // + TDStatus = (UINT8) (PtrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x08); +} + +/** + Detect whether CRC/Time Out Error is encountered. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The CRC/Time Out Error is encountered or not. + +**/ +BOOLEAN +IsTDStatusCRCTimeOutError ( + IN TD_STRUCT *PtrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether CRC/Time Out Error is encountered. + // + TDStatus = (UINT8) (PtrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x04); +} + +/** + Detect whether Bitstuff Error is received. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The Bitstuff Error is received or not. + +**/ +BOOLEAN +IsTDStatusBitStuffError ( + IN TD_STRUCT *PtrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether Bitstuff Error is received. + // + TDStatus = (UINT8) (PtrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x02); +} + +/** + Retrieve the actual number of bytes that were tansferred. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The actual number of bytes that were tansferred. + +**/ +UINT16 +GetTDStatusActualLength ( + IN TD_STRUCT *PtrTDStruct + ) +{ + // + // Retrieve the actual number of bytes that were tansferred. + // the value is encoded as n-1. so return the decoded value. + // + return (UINT16) ((PtrTDStruct->TDData.TDStatusActualLength) + 1); +} + +/** + Retrieve the information of whether the Link Pointer field is valid or not. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The linker pointer field is valid or not. + +**/ +BOOLEAN +GetTDLinkPtrValidorInvalid ( + IN TD_STRUCT *PtrTDStruct + ) +{ + // + // Retrieve the information of whether the Link Pointer field + // is valid or not. + // + if ((PtrTDStruct->TDData.TDLinkPtrTerminate & BIT0) != 0) { + return FALSE; + } else { + return TRUE; + } + +} + +/** + Count TD Number from PtrFirstTD. + + @param PtrFirstTD Place to store TD_STRUCT pointer. + + @retval The queued TDs number. + +**/ +UINTN +CountTDsNumber ( + IN TD_STRUCT *PtrFirstTD + ) +{ + UINTN Number; + TD_STRUCT *Ptr; + + // + // Count the queued TDs number. + // + Number = 0; + Ptr = PtrFirstTD; + while (Ptr != 0) { + Ptr = (TD_STRUCT *) Ptr->PtrNextTD; + Number++; + } + + return Number; +} + +/** + Link TD To QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param PtrTD Place to store TD_STRUCT pointer. + +**/ +VOID +LinkTDToQH ( + IN QH_STRUCT *PtrQH, + IN TD_STRUCT *PtrTD + ) +{ + if (PtrQH == NULL || PtrTD == NULL) { + return ; + } + // + // Validate QH Vertical Ptr field + // + SetQHVerticalValidorInvalid (PtrQH, TRUE); + + // + // Vertical Ptr pointing to TD structure + // + SetQHVerticalQHorTDSelect (PtrQH, FALSE); + + SetQHVerticalLinkPtr (PtrQH, (VOID *) PtrTD); + + PtrQH->PtrDown = (VOID *) PtrTD; +} + +/** + Link TD To TD. + + @param PtrPreTD Place to store TD_STRUCT pointer. + @param PtrTD Place to store TD_STRUCT pointer. + +**/ +VOID +LinkTDToTD ( + IN TD_STRUCT *PtrPreTD, + IN TD_STRUCT *PtrTD + ) +{ + if (PtrPreTD == NULL || PtrTD == NULL) { + return ; + } + // + // Depth first fashion + // + SetTDLinkPtrDepthorBreadth (PtrPreTD, TRUE); + + // + // Link pointer pointing to TD struct + // + SetTDLinkPtrQHorTDSelect (PtrPreTD, FALSE); + + // + // Validate the link pointer valid bit + // + SetTDLinkPtrValidorInvalid (PtrPreTD, TRUE); + + SetTDLinkPtr (PtrPreTD, PtrTD); + + PtrPreTD->PtrNextTD = (VOID *) PtrTD; + + PtrTD->PtrNextTD = NULL; +} + +/** + Execute Control Transfer. + + @param UhcDev The UCHI device. + @param PtrTD A pointer to TD_STRUCT data. + @param ActualLen Actual transfer Length. + @param TimeOut TimeOut value. + @param TransferResult Transfer Result. + + @return EFI_DEVICE_ERROR The transfer failed due to transfer error. + @return EFI_TIMEOUT The transfer failed due to time out. + @return EFI_SUCCESS The transfer finished OK. + +**/ +EFI_STATUS +ExecuteControlTransfer ( + IN USB_UHC_DEV *UhcDev, + IN TD_STRUCT *PtrTD, + OUT UINTN *ActualLen, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +{ + UINTN ErrTDPos; + UINTN Delay; + + ErrTDPos = 0; + *TransferResult = EFI_USB_NOERROR; + *ActualLen = 0; + + Delay = (TimeOut * STALL_1_MILLI_SECOND / 200) + 1; + + do { + + CheckTDsResults (PtrTD, TransferResult, &ErrTDPos, ActualLen); + + // + // TD is inactive, means the control transfer is end. + // + if ((*TransferResult & EFI_USB_ERR_NOTEXECUTE) != EFI_USB_ERR_NOTEXECUTE) { + break; + } + MicroSecondDelay (200); + Delay--; + + } while (Delay != 0); + + + if (*TransferResult != EFI_USB_NOERROR) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Execute Bulk Transfer. + + @param UhcDev The UCHI device. + @param PtrTD A pointer to TD_STRUCT data. + @param ActualLen Actual transfer Length. + @param DataToggle DataToggle value. + @param TimeOut TimeOut value. + @param TransferResult Transfer Result. + + @return EFI_DEVICE_ERROR The transfer failed due to transfer error. + @return EFI_TIMEOUT The transfer failed due to time out. + @return EFI_SUCCESS The transfer finished OK. + +**/ +EFI_STATUS +ExecBulkTransfer ( + IN USB_UHC_DEV *UhcDev, + IN TD_STRUCT *PtrTD, + IN OUT UINTN *ActualLen, + IN UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +{ + UINTN ErrTDPos; + UINTN ScrollNum; + UINTN Delay; + + ErrTDPos = 0; + *TransferResult = EFI_USB_NOERROR; + *ActualLen = 0; + + Delay = (TimeOut * STALL_1_MILLI_SECOND / 200) + 1; + + do { + + CheckTDsResults (PtrTD, TransferResult, &ErrTDPos, ActualLen); + // + // TD is inactive, thus meaning bulk transfer's end. + // + if ((*TransferResult & EFI_USB_ERR_NOTEXECUTE) != EFI_USB_ERR_NOTEXECUTE) { + break; + } + MicroSecondDelay (200); + Delay--; + + } while (Delay != 0); + + // + // has error + // + if (*TransferResult != EFI_USB_NOERROR) { + // + // scroll the Data Toggle back to the last success TD + // + ScrollNum = CountTDsNumber (PtrTD) - ErrTDPos; + if ((ScrollNum % 2) != 0) { + *DataToggle ^= 1; + } + + // + // If error, wait 100ms to retry by upper layer + // + MicroSecondDelay (100 * 1000); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Delete Queued TDs. + + @param UhcDev The UCHI device. + @param PtrFirstTD Place to store TD_STRUCT pointer. + +**/ +VOID +DeleteQueuedTDs ( + IN USB_UHC_DEV *UhcDev, + IN TD_STRUCT *PtrFirstTD + ) +{ + TD_STRUCT *Tptr1; + + TD_STRUCT *Tptr2; + + Tptr1 = PtrFirstTD; + // + // Delete all the TDs in a queue. + // + while (Tptr1 != NULL) { + + Tptr2 = Tptr1; + + if (!GetTDLinkPtrValidorInvalid (Tptr2)) { + Tptr1 = NULL; + } else { + // + // has more than one TD in the queue. + // + Tptr1 = GetTDLinkPtr (Tptr2); + } + + UhcFreePool (UhcDev, (UINT8 *) Tptr2, sizeof (TD_STRUCT)); + } + + return ; +} + +/** + Check TDs Results. + + @param PtrTD A pointer to TD_STRUCT data. + @param Result The result to return. + @param ErrTDPos The Error TD position. + @param ActualTransferSize Actual transfer size. + + @retval The TD is executed successfully or not. + +**/ +BOOLEAN +CheckTDsResults ( + IN TD_STRUCT *PtrTD, + OUT UINT32 *Result, + OUT UINTN *ErrTDPos, + OUT UINTN *ActualTransferSize + ) +{ + UINTN Len; + + *Result = EFI_USB_NOERROR; + *ErrTDPos = 0; + + // + // Init to zero. + // + *ActualTransferSize = 0; + + while (PtrTD != NULL) { + + if (IsTDStatusActive (PtrTD)) { + *Result |= EFI_USB_ERR_NOTEXECUTE; + } + + if (IsTDStatusStalled (PtrTD)) { + *Result |= EFI_USB_ERR_STALL; + } + + if (IsTDStatusBufferError (PtrTD)) { + *Result |= EFI_USB_ERR_BUFFER; + } + + if (IsTDStatusBabbleError (PtrTD)) { + *Result |= EFI_USB_ERR_BABBLE; + } + + if (IsTDStatusNAKReceived (PtrTD)) { + *Result |= EFI_USB_ERR_NAK; + } + + if (IsTDStatusCRCTimeOutError (PtrTD)) { + *Result |= EFI_USB_ERR_TIMEOUT; + } + + if (IsTDStatusBitStuffError (PtrTD)) { + *Result |= EFI_USB_ERR_BITSTUFF; + } + // + // Accumulate actual transferred data length in each TD. + // + Len = GetTDStatusActualLength (PtrTD) & 0x7FF; + *ActualTransferSize += Len; + + // + // if any error encountered, stop processing the left TDs. + // + if ((*Result) != 0) { + return FALSE; + } + + PtrTD = (TD_STRUCT *) (PtrTD->PtrNextTD); + // + // Record the first Error TD's position in the queue, + // this value is zero-based. + // + (*ErrTDPos)++; + } + + return TRUE; +} + +/** + Create Memory Block. + + @param UhcDev The UCHI device. + @param MemoryHeader The Pointer to allocated memory block. + @param MemoryBlockSizeInPages The page size of memory block to be allocated. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +CreateMemoryBlock ( + IN USB_UHC_DEV *UhcDev, + OUT MEMORY_MANAGE_HEADER **MemoryHeader, + IN UINTN MemoryBlockSizeInPages + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS TempPtr; + UINTN MemPages; + UINT8 *Ptr; + + // + // Memory Block uses MemoryBlockSizeInPages pages, + // memory management header and bit array use 1 page + // + MemPages = MemoryBlockSizeInPages + 1; + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + MemPages, + &TempPtr + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Ptr = (UINT8 *) ((UINTN) TempPtr); + + ZeroMem (Ptr, MemPages * EFI_PAGE_SIZE); + + *MemoryHeader = (MEMORY_MANAGE_HEADER *) Ptr; + // + // adjust Ptr pointer to the next empty memory + // + Ptr += sizeof (MEMORY_MANAGE_HEADER); + // + // Set Bit Array initial address + // + (*MemoryHeader)->BitArrayPtr = Ptr; + + (*MemoryHeader)->Next = NULL; + + // + // Memory block initial address + // + Ptr = (UINT8 *) ((UINTN) TempPtr); + Ptr += EFI_PAGE_SIZE; + (*MemoryHeader)->MemoryBlockPtr = Ptr; + // + // set Memory block size + // + (*MemoryHeader)->MemoryBlockSizeInBytes = MemoryBlockSizeInPages * EFI_PAGE_SIZE; + // + // each bit in Bit Array will manage 32byte memory in memory block + // + (*MemoryHeader)->BitArraySizeInBytes = ((*MemoryHeader)->MemoryBlockSizeInBytes / 32) / 8; + + return EFI_SUCCESS; +} + +/** + Initialize UHCI memory management. + + @param UhcDev The UCHI device. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +InitializeMemoryManagement ( + IN USB_UHC_DEV *UhcDev + ) +{ + MEMORY_MANAGE_HEADER *MemoryHeader; + EFI_STATUS Status; + UINTN MemPages; + + MemPages = NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES; + Status = CreateMemoryBlock (UhcDev, &MemoryHeader, MemPages); + if (EFI_ERROR (Status)) { + return Status; + } + + UhcDev->Header1 = MemoryHeader; + + return EFI_SUCCESS; +} + +/** + Initialize UHCI memory management. + + @param UhcDev The UCHI device. + @param Pool Buffer pointer to store the buffer pointer. + @param AllocSize The size of the pool to be allocated. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +UhcAllocatePool ( + IN USB_UHC_DEV *UhcDev, + OUT UINT8 **Pool, + IN UINTN AllocSize + ) +{ + MEMORY_MANAGE_HEADER *MemoryHeader; + MEMORY_MANAGE_HEADER *TempHeaderPtr; + MEMORY_MANAGE_HEADER *NewMemoryHeader; + UINTN RealAllocSize; + UINTN MemoryBlockSizeInPages; + EFI_STATUS Status; + + *Pool = NULL; + + MemoryHeader = UhcDev->Header1; + + // + // allocate unit is 32 byte (align on 32 byte) + // + if ((AllocSize & 0x1F) != 0) { + RealAllocSize = (AllocSize / 32 + 1) * 32; + } else { + RealAllocSize = AllocSize; + } + + Status = EFI_NOT_FOUND; + for (TempHeaderPtr = MemoryHeader; TempHeaderPtr != NULL; TempHeaderPtr = TempHeaderPtr->Next) { + + Status = AllocMemInMemoryBlock ( + TempHeaderPtr, + (VOID **) Pool, + RealAllocSize / 32 + ); + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + } + // + // There is no enough memory, + // Create a new Memory Block + // + // + // if pool size is larger than NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES, + // just allocate a large enough memory block. + // + if (RealAllocSize > (NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES * EFI_PAGE_SIZE)) { + MemoryBlockSizeInPages = RealAllocSize / EFI_PAGE_SIZE + 1; + } else { + MemoryBlockSizeInPages = NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES; + } + + Status = CreateMemoryBlock (UhcDev, &NewMemoryHeader, MemoryBlockSizeInPages); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Link the new Memory Block to the Memory Header list + // + InsertMemoryHeaderToList (MemoryHeader, NewMemoryHeader); + + Status = AllocMemInMemoryBlock ( + NewMemoryHeader, + (VOID **) Pool, + RealAllocSize / 32 + ); + return Status; +} + +/** + Alloc Memory In MemoryBlock. + + @param MemoryHeader The pointer to memory manage header. + @param Pool Buffer pointer to store the buffer pointer. + @param NumberOfMemoryUnit The size of the pool to be allocated. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +AllocMemInMemoryBlock ( + IN MEMORY_MANAGE_HEADER *MemoryHeader, + OUT VOID **Pool, + IN UINTN NumberOfMemoryUnit + ) +{ + UINTN TempBytePos; + UINTN FoundBytePos; + UINT8 Index; + UINT8 FoundBitPos; + UINT8 ByteValue; + UINT8 BitValue; + UINTN NumberOfZeros; + UINTN Count; + + FoundBytePos = 0; + FoundBitPos = 0; + + ByteValue = MemoryHeader->BitArrayPtr[0]; + NumberOfZeros = 0; + Index = 0; + for (TempBytePos = 0; TempBytePos < MemoryHeader->BitArraySizeInBytes;) { + // + // Pop out BitValue from a byte in TempBytePos. + // + BitValue = (UINT8)(ByteValue & 0x1); + + if (BitValue == 0) { + // + // Found a free bit, the NumberOfZeros only record the number of those consecutive zeros + // + NumberOfZeros++; + // + // Found enough consecutive free space, break the loop + // + if (NumberOfZeros >= NumberOfMemoryUnit) { + break; + } + } else { + // + // Encountering a '1', meant the bit is ocupied. + // + if (NumberOfZeros >= NumberOfMemoryUnit) { + // + // Found enough consecutive free space,break the loop + // + break; + } else { + // + // the NumberOfZeros only record the number of those consecutive zeros, + // so reset the NumberOfZeros to 0 when encountering '1' before finding + // enough consecutive '0's + // + NumberOfZeros = 0; + // + // reset the (FoundBytePos,FoundBitPos) to the position of '1' + // + FoundBytePos = TempBytePos; + FoundBitPos = Index; + } + } + // + // right shift the byte + // + ByteValue /= 2; + + // + // step forward a bit + // + Index++; + if (Index == 8) { + // + // step forward a byte, getting the byte value, + // and reset the bit pos. + // + TempBytePos += 1; + ByteValue = MemoryHeader->BitArrayPtr[TempBytePos]; + Index = 0; + } + } + + if (NumberOfZeros < NumberOfMemoryUnit) { + return EFI_NOT_FOUND; + } + // + // Found enough free space. + // + // + // The values recorded in (FoundBytePos,FoundBitPos) have two conditions: + // 1)(FoundBytePos,FoundBitPos) record the position + // of the last '1' before the consecutive '0's, it must + // be adjusted to the start position of the consecutive '0's. + // 2)the start address of the consecutive '0's is just the start of + // the bitarray. so no need to adjust the values of (FoundBytePos,FoundBitPos). + // + if ((MemoryHeader->BitArrayPtr[0] & BIT0) != 0) { + FoundBitPos += 1; + } + // + // Have the (FoundBytePos,FoundBitPos) make sense. + // + if (FoundBitPos > 7) { + FoundBytePos += 1; + FoundBitPos -= 8; + } + // + // Set the memory as allocated + // + for (TempBytePos = FoundBytePos, Index = FoundBitPos, Count = 0; Count < NumberOfMemoryUnit; Count++) { + + MemoryHeader->BitArrayPtr[TempBytePos] = (UINT8) (MemoryHeader->BitArrayPtr[TempBytePos] | (1 << Index)); + Index++; + if (Index == 8) { + TempBytePos += 1; + Index = 0; + } + } + + *Pool = MemoryHeader->MemoryBlockPtr + (FoundBytePos * 8 + FoundBitPos) * 32; + + return EFI_SUCCESS; +} + +/** + Uhci Free Pool. + + @param UhcDev The UHCI device. + @param Pool A pointer to store the buffer address. + @param AllocSize The size of the pool to be freed. + +**/ +VOID +UhcFreePool ( + IN USB_UHC_DEV *UhcDev, + IN UINT8 *Pool, + IN UINTN AllocSize + ) +{ + MEMORY_MANAGE_HEADER *MemoryHeader; + MEMORY_MANAGE_HEADER *TempHeaderPtr; + UINTN StartBytePos; + UINTN Index; + UINT8 StartBitPos; + UINT8 Index2; + UINTN Count; + UINTN RealAllocSize; + + MemoryHeader = UhcDev->Header1; + + // + // allocate unit is 32 byte (align on 32 byte) + // + if ((AllocSize & 0x1F) != 0) { + RealAllocSize = (AllocSize / 32 + 1) * 32; + } else { + RealAllocSize = AllocSize; + } + + for (TempHeaderPtr = MemoryHeader; TempHeaderPtr != NULL; + TempHeaderPtr = TempHeaderPtr->Next) { + + if ((Pool >= TempHeaderPtr->MemoryBlockPtr) && + ((Pool + RealAllocSize) <= (TempHeaderPtr->MemoryBlockPtr + + TempHeaderPtr->MemoryBlockSizeInBytes))) { + + // + // Pool is in the Memory Block area, + // find the start byte and bit in the bit array + // + StartBytePos = ((Pool - TempHeaderPtr->MemoryBlockPtr) / 32) / 8; + StartBitPos = (UINT8) (((Pool - TempHeaderPtr->MemoryBlockPtr) / 32) % 8); + + // + // reset associated bits in bit arry + // + for (Index = StartBytePos, Index2 = StartBitPos, Count = 0; Count < (RealAllocSize / 32); Count++) { + + TempHeaderPtr->BitArrayPtr[Index] = (UINT8) (TempHeaderPtr->BitArrayPtr[Index] ^ (1 << Index2)); + Index2++; + if (Index2 == 8) { + Index += 1; + Index2 = 0; + } + } + // + // break the loop + // + break; + } + } + +} + +/** + Insert a new memory header into list. + + @param MemoryHeader A pointer to the memory header list. + @param NewMemoryHeader A new memory header to be inserted into the list. + +**/ +VOID +InsertMemoryHeaderToList ( + IN MEMORY_MANAGE_HEADER *MemoryHeader, + IN MEMORY_MANAGE_HEADER *NewMemoryHeader + ) +{ + MEMORY_MANAGE_HEADER *TempHeaderPtr; + + for (TempHeaderPtr = MemoryHeader; TempHeaderPtr != NULL; TempHeaderPtr = TempHeaderPtr->Next) { + if (TempHeaderPtr->Next == NULL) { + TempHeaderPtr->Next = NewMemoryHeader; + break; + } + } +} + +/** + Judge the memory block in the memory header is empty or not. + + @param MemoryHeaderPtr A pointer to the memory header list. + + @retval Whether the memory block in the memory header is empty or not. + +**/ +BOOLEAN +IsMemoryBlockEmptied ( + IN MEMORY_MANAGE_HEADER *MemoryHeaderPtr + ) +{ + UINTN Index; + + for (Index = 0; Index < MemoryHeaderPtr->BitArraySizeInBytes; Index++) { + if (MemoryHeaderPtr->BitArrayPtr[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + remove a memory header from list. + + @param FirstMemoryHeader A pointer to the memory header list. + @param FreeMemoryHeader A memory header to be removed into the list. + +**/ +VOID +DelinkMemoryBlock ( + IN MEMORY_MANAGE_HEADER *FirstMemoryHeader, + IN MEMORY_MANAGE_HEADER *FreeMemoryHeader + ) +{ + MEMORY_MANAGE_HEADER *TempHeaderPtr; + + if ((FirstMemoryHeader == NULL) || (FreeMemoryHeader == NULL)) { + return ; + } + + for (TempHeaderPtr = FirstMemoryHeader; TempHeaderPtr != NULL; TempHeaderPtr = TempHeaderPtr->Next) { + + if (TempHeaderPtr->Next == FreeMemoryHeader) { + // + // Link the before and after + // + TempHeaderPtr->Next = FreeMemoryHeader->Next; + break; + } + } +} diff --git a/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.h b/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.h new file mode 100644 index 0000000000..dc697ce105 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.h @@ -0,0 +1,1332 @@ +/** @file +Private Header file for Usb Host Controller PEIM + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _RECOVERY_UHC_H_ +#define _RECOVERY_UHC_H_ + + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define USB_SLOW_SPEED_DEVICE 0x01 +#define USB_FULL_SPEED_DEVICE 0x02 + +// +// One memory block uses 16 page +// +#define NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES 16 + +#define USBCMD 0 /* Command Register Offset 00-01h */ +#define USBCMD_RS BIT0 /* Run/Stop */ +#define USBCMD_HCRESET BIT1 /* Host reset */ +#define USBCMD_GRESET BIT2 /* Global reset */ +#define USBCMD_EGSM BIT3 /* Global Suspend Mode */ +#define USBCMD_FGR BIT4 /* Force Global Resume */ +#define USBCMD_SWDBG BIT5 /* SW Debug mode */ +#define USBCMD_CF BIT6 /* Config Flag (sw only) */ +#define USBCMD_MAXP BIT7 /* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS 2 /* Status Register Offset 02-03h */ +#define USBSTS_USBINT BIT0 /* Interrupt due to IOC */ +#define USBSTS_ERROR BIT1 /* Interrupt due to error */ +#define USBSTS_RD BIT2 /* Resume Detect */ +#define USBSTS_HSE BIT3 /* Host System Error - basically PCI problems */ +#define USBSTS_HCPE BIT4 /* Host Controller Process Error - the scripts were buggy */ +#define USBSTS_HCH BIT5 /* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR 4 /* Interrupt Enable Register 04-05h */ +#define USBINTR_TIMEOUT BIT0 /* Timeout/CRC error enable */ +#define USBINTR_RESUME BIT1 /* Resume interrupt enable */ +#define USBINTR_IOC BIT2 /* Interrupt On Complete enable */ +#define USBINTR_SP BIT3 /* Short packet interrupt enable */ + +/* Frame Number Register Offset 06-08h */ +#define USBFRNUM 6 + +/* Frame List Base Address Register Offset 08-0Bh */ +#define USBFLBASEADD 8 + +/* Start of Frame Modify Register Offset 0Ch */ +#define USBSOF 0x0c + +/* USB port status and control registers */ +#define USBPORTSC1 0x10 /*Port 1 offset 10-11h */ +#define USBPORTSC2 0x12 /*Port 2 offset 12-13h */ + +#define USBPORTSC_CCS BIT0 /* Current Connect Status ("device present") */ +#define USBPORTSC_CSC BIT1 /* Connect Status Change */ +#define USBPORTSC_PED BIT2 /* Port Enable / Disable */ +#define USBPORTSC_PEDC BIT3 /* Port Enable / Disable Change */ +#define USBPORTSC_LSL BIT4 /* Line Status Low bit*/ +#define USBPORTSC_LSH BIT5 /* Line Status High bit*/ +#define USBPORTSC_RD BIT6 /* Resume Detect */ +#define USBPORTSC_LSDA BIT8 /* Low Speed Device Attached */ +#define USBPORTSC_PR BIT9 /* Port Reset */ +#define USBPORTSC_SUSP BIT12 /* Suspend */ + +#define SETUP_PACKET_ID 0x2D +#define INPUT_PACKET_ID 0x69 +#define OUTPUT_PACKET_ID 0xE1 +#define ERROR_PACKET_ID 0x55 + +#define STALL_1_MILLI_SECOND 1000 + + +#pragma pack(1) + +typedef struct { + UINT32 FrameListPtrTerminate : 1; + UINT32 FrameListPtrQSelect : 1; + UINT32 FrameListRsvd : 2; + UINT32 FrameListPtr : 28; +} FRAMELIST_ENTRY; + +typedef struct { + UINT32 QHHorizontalTerminate : 1; + UINT32 QHHorizontalQSelect : 1; + UINT32 QHHorizontalRsvd : 2; + UINT32 QHHorizontalPtr : 28; + UINT32 QHVerticalTerminate : 1; + UINT32 QHVerticalQSelect : 1; + UINT32 QHVerticalRsvd : 2; + UINT32 QHVerticalPtr : 28; +} QUEUE_HEAD; + +typedef struct { + QUEUE_HEAD QueueHead; + UINT32 Reserved1; + UINT32 Reserved2; + VOID *PtrNext; + VOID *PtrDown; + VOID *Reserved3; + UINT32 Reserved4; +} QH_STRUCT; + +typedef struct { + UINT32 TDLinkPtrTerminate : 1; + UINT32 TDLinkPtrQSelect : 1; + UINT32 TDLinkPtrDepthSelect : 1; + UINT32 TDLinkPtrRsvd : 1; + UINT32 TDLinkPtr : 28; + UINT32 TDStatusActualLength : 11; + UINT32 TDStatusRsvd : 5; + UINT32 TDStatus : 8; + UINT32 TDStatusIOC : 1; + UINT32 TDStatusIOS : 1; + UINT32 TDStatusLS : 1; + UINT32 TDStatusErr : 2; + UINT32 TDStatusSPD : 1; + UINT32 TDStatusRsvd2 : 2; + UINT32 TDTokenPID : 8; + UINT32 TDTokenDevAddr : 7; + UINT32 TDTokenEndPt : 4; + UINT32 TDTokenDataToggle : 1; + UINT32 TDTokenRsvd : 1; + UINT32 TDTokenMaxLen : 11; + UINT32 TDBufferPtr; +} TD; + +typedef struct { + TD TDData; + UINT8 *PtrTDBuffer; + VOID *PtrNextTD; + VOID *PtrNextQH; + UINT16 TDBufferLength; + UINT16 Reserved; +} TD_STRUCT; + +#pragma pack() + +typedef struct _MEMORY_MANAGE_HEADER MEMORY_MANAGE_HEADER; + +struct _MEMORY_MANAGE_HEADER { + UINT8 *BitArrayPtr; + UINTN BitArraySizeInBytes; + UINT8 *MemoryBlockPtr; + UINTN MemoryBlockSizeInBytes; + MEMORY_MANAGE_HEADER *Next; +}; + +#define USB_UHC_DEV_SIGNATURE SIGNATURE_32 ('p', 'u', 'h', 'c') +typedef struct { + UINTN Signature; + PEI_USB_HOST_CONTROLLER_PPI UsbHostControllerPpi; + EFI_PEI_PPI_DESCRIPTOR PpiDescriptor; + + UINT32 UsbHostControllerBaseAddress; + FRAMELIST_ENTRY *FrameListEntry; + QH_STRUCT *ConfigQH; + QH_STRUCT *BulkQH; + // + // Header1 used for QH,TD memory blocks management + // + MEMORY_MANAGE_HEADER *Header1; + +} USB_UHC_DEV; + +#define PEI_RECOVERY_USB_UHC_DEV_FROM_UHCI_THIS(a) CR (a, USB_UHC_DEV, UsbHostControllerPpi, USB_UHC_DEV_SIGNATURE) + +/** + Submits control transfer to a target USB device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB_HOST_CONTROLLER_PPI. + @param DeviceAddress The target device address. + @param DeviceSpeed Target device speed. + @param MaximumPacketLength Maximum packet size the default control transfer + endpoint is capable of sending or receiving. + @param Request USB device request to send. + @param TransferDirection Specifies the data direction for the data stage. + @param Data Data buffer to be transmitted or received from USB device. + @param DataLength The size (in bytes) of the data buffer. + @param TimeOut Indicates the maximum timeout, in millisecond. + @param TransferResult Return the result of this control transfer. + + @retval EFI_SUCCESS Transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resources. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_TIMEOUT Transfer failed due to timeout. + @retval EFI_DEVICE_ERROR Transfer failed due to host controller or device error. + +**/ +EFI_STATUS +EFIAPI +UhcControlTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI * This, + IN UINT8 DeviceAddress, + IN UINT8 DeviceSpeed, + IN UINT8 MaximumPacketLength, + IN EFI_USB_DEVICE_REQUEST * Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data OPTIONAL, + IN OUT UINTN *DataLength OPTIONAL, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ); + +/** + Submits bulk transfer to a bulk endpoint of a USB device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB_HOST_CONTROLLER_PPI. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and its direction in bit 7. + @param MaximumPacketLength Maximum packet size the endpoint is capable of + sending or receiving. + @param Data Array of pointers to the buffers of data to transmit + from or receive into. + @param DataLength The lenght of the data buffer. + @param DataToggle On input, the initial data toggle for the transfer; + On output, it is updated to to next data toggle to use of + the subsequent bulk transfer. + @param TimeOut Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + @param TransferResult A pointer to the detailed result information of the + bulk transfer. + + @retval EFI_SUCCESS The transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource. + @retval EFI_INVALID_PARAMETER Parameters are invalid. + @retval EFI_TIMEOUT The transfer failed due to timeout. + @retval EFI_DEVICE_ERROR The transfer failed due to host controller error. + +**/ +EFI_STATUS +EFIAPI +UhcBulkTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ); + +/** + Retrieves the number of root hub ports. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the + PEI_USB_HOST_CONTROLLER_PPI. + @param[out] PortNumber The pointer to the number of the root hub ports. + + @retval EFI_SUCCESS The port number was retrieved successfully. + @retval EFI_INVALID_PARAMETER PortNumber is NULL. + +**/ +EFI_STATUS +EFIAPI +UhcGetRootHubPortNumber ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + OUT UINT8 *PortNumber + ); + +/** + Retrieves the current status of a USB root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB_HOST_CONTROLLER_PPI. + @param PortNumber The root hub port to retrieve the state from. + @param PortStatus Variable to receive the port state. + + @retval EFI_SUCCESS The status of the USB root hub port specified. + by PortNumber was returned in PortStatus. + @retval EFI_INVALID_PARAMETER PortNumber is invalid. + +**/ +EFI_STATUS +EFIAPI +UhcGetRootHubPortStatus ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus + ); + +/** + Sets a feature for the specified root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES + @param This The pointer of PEI_USB_HOST_CONTROLLER_PPI + @param PortNumber Root hub port to set. + @param PortFeature Feature to set. + + @retval EFI_SUCCESS The feature specified by PortFeature was set. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + @retval EFI_TIMEOUT The time out occurred. + +**/ +EFI_STATUS +EFIAPI +UhcSetRootHubPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ); + +/** + Clears a feature for the specified root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB_HOST_CONTROLLER_PPI. + @param PortNumber Specifies the root hub port whose feature + is requested to be cleared. + @param PortFeature Indicates the feature selector associated with the + feature clear request. + + @retval EFI_SUCCESS The feature specified by PortFeature was cleared + for the USB root hub port specified by PortNumber. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + +**/ +EFI_STATUS +EFIAPI +UhcClearRootHubPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ); + +/** + Initialize UHCI. + + @param UhcDev UHCI Device. + + @retval EFI_SUCCESS UHCI successfully initialized. + @retval EFI_OUT_OF_RESOURCES Resource can not be allocated. + +**/ +EFI_STATUS +InitializeUsbHC ( + IN USB_UHC_DEV *UhcDev + ); + +/** + Create Frame List Structure. + + @param UhcDev UHCI device. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +CreateFrameList ( + USB_UHC_DEV *UhcDev + ); + +/** + Read a 16bit width data from Uhc HC IO space register. + + @param UhcDev The UHCI device. + @param Port The IO space address of the register. + + @retval the register content read. + +**/ +UINT16 +USBReadPortW ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 Port + ); + +/** + Write a 16bit width data into Uhc HC IO space register. + + @param UhcDev The UHCI device. + @param Port The IO space address of the register. + @param Data The data written into the register. + +**/ +VOID +USBWritePortW ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 Port, + IN UINT16 Data + ); + +/** + Write a 32bit width data into Uhc HC IO space register. + + @param UhcDev The UHCI device. + @param Port The IO space address of the register. + @param Data The data written into the register. + +**/ +VOID +USBWritePortDW ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 Port, + IN UINT32 Data + ); + +/** + Clear the content of UHCI's Status Register. + + @param UhcDev The UHCI device. + @param StatusAddr The IO space address of the register. + +**/ +VOID +ClearStatusReg ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 StatusAddr + ); + +/** + Check whether the host controller operates well. + + @param UhcDev The UHCI device. + @param StatusRegAddr The io address of status register. + + @retval TRUE Host controller is working. + @retval FALSE Host controller is halted or system error. + +**/ +BOOLEAN +IsStatusOK ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 StatusRegAddr + ); + +/** + Get Current Frame Number. + + @param UhcDev The UHCI device. + @param FrameNumberAddr The address of frame list register. + + @retval The content of the frame list register. + +**/ +UINT16 +GetCurrentFrameNumber ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 FrameNumberAddr + ); + +/** + Set Frame List Base Address. + + @param UhcDev The UHCI device. + @param FrameListRegAddr The address of frame list register. + @param Addr The address of frame list table. + +**/ +VOID +SetFrameListBaseAddress ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 FrameListRegAddr, + IN UINT32 Addr + ); + +/** + Create QH and initialize. + + @param UhcDev The UHCI device. + @param PtrQH Place to store QH_STRUCT pointer. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +CreateQH ( + IN USB_UHC_DEV *UhcDev, + OUT QH_STRUCT **PtrQH + ); + +/** + Set the horizontal link pointer in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param PtrNext Place to the next QH_STRUCT. + +**/ +VOID +SetQHHorizontalLinkPtr ( + IN QH_STRUCT *PtrQH, + IN VOID *PtrNext + ); + +/** + Get the horizontal link pointer in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + + @retval The horizontal link pointer in QH. + +**/ +VOID * +GetQHHorizontalLinkPtr ( + IN QH_STRUCT *PtrQH + ); + +/** + Set a QH or TD horizontally to be connected with a specific QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param IsQH Specify QH or TD is connected. + +**/ +VOID +SetQHHorizontalQHorTDSelect ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN IsQH + ); + +/** + Set the horizontal validor bit in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param IsValid Specify the horizontal linker is valid or not. + +**/ +VOID +SetQHHorizontalValidorInvalid ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN IsValid + ); + +/** + Set the vertical link pointer in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param PtrNext Place to the next QH_STRUCT. + +**/ +VOID +SetQHVerticalLinkPtr ( + IN QH_STRUCT *PtrQH, + IN VOID *PtrNext + ); + +/** + Set a QH or TD vertically to be connected with a specific QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param IsQH Specify QH or TD is connected. + +**/ +VOID +SetQHVerticalQHorTDSelect ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN IsQH + ); + +/** + Set the vertical validor bit in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param IsValid Specify the vertical linker is valid or not. + +**/ +VOID +SetQHVerticalValidorInvalid ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN IsValid + ); + +/** + Get the vertical validor bit in QH. + + @param PtrQH Place to store QH_STRUCT pointer. + + @retval The vertical linker is valid or not. + +**/ +BOOLEAN +GetQHHorizontalValidorInvalid ( + IN QH_STRUCT *PtrQH + ); + +/** + Allocate TD or QH Struct. + + @param UhcDev The UHCI device. + @param Size The size of allocation. + @param PtrStruct Place to store TD_STRUCT pointer. + + @return EFI_SUCCESS Allocate successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + +**/ +EFI_STATUS +AllocateTDorQHStruct ( + IN USB_UHC_DEV *UhcDev, + IN UINT32 Size, + OUT VOID **PtrStruct + ); + +/** + Create a TD Struct. + + @param UhcDev The UHCI device. + @param PtrTD Place to store TD_STRUCT pointer. + + @return EFI_SUCCESS Allocate successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + +**/ +EFI_STATUS +CreateTD ( + IN USB_UHC_DEV *UhcDev, + OUT TD_STRUCT **PtrTD + ); + +/** + Generate Setup Stage TD. + + @param UhcDev The UHCI device. + @param DevAddr Device address. + @param Endpoint Endpoint number. + @param DeviceSpeed Device Speed. + @param DevRequest Device reuquest. + @param RequestLen Request length. + @param PtrTD TD_STRUCT generated. + + @return EFI_SUCCESS Generate setup stage TD successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + +**/ +EFI_STATUS +GenSetupStageTD ( + IN USB_UHC_DEV *UhcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 DeviceSpeed, + IN UINT8 *DevRequest, + IN UINT8 RequestLen, + OUT TD_STRUCT **PtrTD + ); + +/** + Generate Data Stage TD. + + @param UhcDev The UHCI device. + @param DevAddr Device address. + @param Endpoint Endpoint number. + @param PtrData Data buffer. + @param Len Data length. + @param PktID PacketID. + @param Toggle Data toggle value. + @param DeviceSpeed Device Speed. + @param PtrTD TD_STRUCT generated. + + @return EFI_SUCCESS Generate data stage TD successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + +**/ +EFI_STATUS +GenDataTD ( + IN USB_UHC_DEV *UhcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 *PtrData, + IN UINT8 Len, + IN UINT8 PktID, + IN UINT8 Toggle, + IN UINT8 DeviceSpeed, + OUT TD_STRUCT **PtrTD + ); + +/** + Generate Status Stage TD. + + @param UhcDev The UHCI device. + @param DevAddr Device address. + @param Endpoint Endpoint number. + @param PktID PacketID. + @param DeviceSpeed Device Speed. + @param PtrTD TD_STRUCT generated. + + @return EFI_SUCCESS Generate status stage TD successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + +**/ +EFI_STATUS +CreateStatusTD ( + IN USB_UHC_DEV *UhcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 PktID, + IN UINT8 DeviceSpeed, + OUT TD_STRUCT **PtrTD + ); + +/** + Set the link pointer validor bit in TD. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsValid Specify the linker pointer is valid or not. + +**/ +VOID +SetTDLinkPtrValidorInvalid ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsValid + ); + +/** + Set the Link Pointer pointing to a QH or TD. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsQH Specify QH or TD is connected. + +**/ +VOID +SetTDLinkPtrQHorTDSelect ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsQH + ); + +/** + Set the traverse is depth-first or breadth-first. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsDepth Specify the traverse is depth-first or breadth-first. + +**/ +VOID +SetTDLinkPtrDepthorBreadth ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsDepth + ); + +/** + Set TD Link Pointer in TD. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param PtrNext Place to the next TD_STRUCT. + +**/ +VOID +SetTDLinkPtr ( + IN TD_STRUCT *PtrTDStruct, + IN VOID *PtrNext + ); + +/** + Get TD Link Pointer. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval Get TD Link Pointer in TD. + +**/ +VOID* +GetTDLinkPtr ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Get the information about whether the Link Pointer field pointing to + a QH or a TD. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval whether the Link Pointer field pointing to a QH or a TD. + +**/ +BOOLEAN +IsTDLinkPtrQHOrTD ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Enable/Disable short packet detection mechanism. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsEnable Enable or disable short packet detection mechanism. + +**/ +VOID +EnableorDisableTDShortPacket ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsEnable + ); + +/** + Set the max error counter in TD. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param MaxErrors The number of allowable error. + +**/ +VOID +SetTDControlErrorCounter ( + IN TD_STRUCT *PtrTDStruct, + IN UINT8 MaxErrors + ); + +/** + Set the TD is targeting a low-speed device or not. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsLowSpeedDevice Whether The device is low-speed. + +**/ +VOID +SetTDLoworFullSpeedDevice ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsLowSpeedDevice + ); + +/** + Set the TD is isochronous transfer type or not. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsIsochronous Whether the transaction isochronous transfer type. + +**/ +VOID +SetTDControlIsochronousorNot ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsIsochronous + ); + +/** + Set if UCHI should issue an interrupt on completion of the frame + in which this TD is executed + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsSet Whether HC should issue an interrupt on completion. + +**/ +VOID +SetorClearTDControlIOC ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsSet + ); + +/** + Set if the TD is active and can be executed. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param IsActive Whether the TD is active and can be executed. + +**/ +VOID +SetTDStatusActiveorInactive ( + IN TD_STRUCT *PtrTDStruct, + IN BOOLEAN IsActive + ); + +/** + Specifies the maximum number of data bytes allowed for the transfer. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param MaxLen The maximum number of data bytes allowed. + + @retval The allowed maximum number of data. +**/ +UINT16 +SetTDTokenMaxLength ( + IN TD_STRUCT *PtrTDStruct, + IN UINT16 MaxLen + ); + +/** + Set the data toggle bit to DATA1. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + +**/ +VOID +SetTDTokenDataToggle1 ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Set the data toggle bit to DATA0. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + +**/ +VOID +SetTDTokenDataToggle0 ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Set EndPoint Number the TD is targeting at. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param EndPoint The Endport number of the target. + +**/ +VOID +SetTDTokenEndPoint ( + IN TD_STRUCT *PtrTDStruct, + IN UINTN EndPoint + ); + +/** + Set Device Address the TD is targeting at. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param DevAddr The Device Address of the target. + +**/ +VOID +SetTDTokenDeviceAddress ( + IN TD_STRUCT *PtrTDStruct, + IN UINTN DevAddr + ); + +/** + Set Packet Identification the TD is targeting at. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + @param PacketID The Packet Identification of the target. + +**/ +VOID +SetTDTokenPacketID ( + IN TD_STRUCT *PtrTDStruct, + IN UINT8 PacketID + ); + +/** + Set the beginning address of the data buffer that will be used + during the transaction. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + +**/ +VOID +SetTDDataBuffer ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Detect whether the TD is active. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The TD is active or not. + +**/ +BOOLEAN +IsTDStatusActive ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Detect whether the TD is stalled. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The TD is stalled or not. + +**/ +BOOLEAN +IsTDStatusStalled ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Detect whether Data Buffer Error is happened. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The Data Buffer Error is happened or not. + +**/ +BOOLEAN +IsTDStatusBufferError ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Detect whether Babble Error is happened. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The Babble Error is happened or not. + +**/ +BOOLEAN +IsTDStatusBabbleError ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Detect whether NAK is received. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The NAK is received or not. + +**/ +BOOLEAN +IsTDStatusNAKReceived ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Detect whether CRC/Time Out Error is encountered. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The CRC/Time Out Error is encountered or not. + +**/ +BOOLEAN +IsTDStatusCRCTimeOutError ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Detect whether Bitstuff Error is received. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The Bitstuff Error is received or not. + +**/ +BOOLEAN +IsTDStatusBitStuffError ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Retrieve the actual number of bytes that were tansferred. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The actual number of bytes that were tansferred. + +**/ +UINT16 +GetTDStatusActualLength ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Retrieve the information of whether the Link Pointer field is valid or not. + + @param PtrTDStruct Place to store TD_STRUCT pointer. + + @retval The linker pointer field is valid or not. + +**/ +BOOLEAN +GetTDLinkPtrValidorInvalid ( + IN TD_STRUCT *PtrTDStruct + ); + +/** + Count TD Number from PtrFirstTD. + + @param PtrFirstTD Place to store TD_STRUCT pointer. + + @retval The queued TDs number. + +**/ +UINTN +CountTDsNumber ( + IN TD_STRUCT *PtrFirstTD + ); + +/** + Link TD To QH. + + @param PtrQH Place to store QH_STRUCT pointer. + @param PtrTD Place to store TD_STRUCT pointer. + +**/ +VOID +LinkTDToQH ( + IN QH_STRUCT *PtrQH, + IN TD_STRUCT *PtrTD + ); + +/** + Link TD To TD. + + @param PtrPreTD Place to store TD_STRUCT pointer. + @param PtrTD Place to store TD_STRUCT pointer. + +**/ +VOID +LinkTDToTD ( + IN TD_STRUCT *PtrPreTD, + IN TD_STRUCT *PtrTD + ); + +/** + Execute Control Transfer. + + @param UhcDev The UCHI device. + @param PtrTD A pointer to TD_STRUCT data. + @param ActualLen Actual transfer Length. + @param TimeOut TimeOut value. + @param TransferResult Transfer Result. + + @return EFI_DEVICE_ERROR The transfer failed due to transfer error. + @return EFI_TIMEOUT The transfer failed due to time out. + @return EFI_SUCCESS The transfer finished OK. + +**/ +EFI_STATUS +ExecuteControlTransfer ( + IN USB_UHC_DEV *UhcDev, + IN TD_STRUCT *PtrTD, + OUT UINTN *ActualLen, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ); + +/** + Execute Bulk Transfer. + + @param UhcDev The UCHI device. + @param PtrTD A pointer to TD_STRUCT data. + @param ActualLen Actual transfer Length. + @param DataToggle DataToggle value. + @param TimeOut TimeOut value. + @param TransferResult Transfer Result. + + @return EFI_DEVICE_ERROR The transfer failed due to transfer error. + @return EFI_TIMEOUT The transfer failed due to time out. + @return EFI_SUCCESS The transfer finished OK. + +**/ +EFI_STATUS +ExecBulkTransfer ( + IN USB_UHC_DEV *UhcDev, + IN TD_STRUCT *PtrTD, + IN OUT UINTN *ActualLen, + IN UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ); + +/** + Delete Queued TDs. + + @param UhcDev The UCHI device. + @param PtrFirstTD Place to store TD_STRUCT pointer. + +**/ +VOID +DeleteQueuedTDs ( + IN USB_UHC_DEV *UhcDev, + IN TD_STRUCT *PtrFirstTD + ); + +/** + Check TDs Results. + + @param PtrTD A pointer to TD_STRUCT data. + @param Result The result to return. + @param ErrTDPos The Error TD position. + @param ActualTransferSize Actual transfer size. + + @retval The TD is executed successfully or not. + +**/ +BOOLEAN +CheckTDsResults ( + IN TD_STRUCT *PtrTD, + OUT UINT32 *Result, + OUT UINTN *ErrTDPos, + OUT UINTN *ActualTransferSize + ); + +/** + Create Memory Block. + + @param UhcDev The UCHI device. + @param MemoryHeader The Pointer to allocated memory block. + @param MemoryBlockSizeInPages The page size of memory block to be allocated. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +CreateMemoryBlock ( + IN USB_UHC_DEV *UhcDev, + OUT MEMORY_MANAGE_HEADER **MemoryHeader, + IN UINTN MemoryBlockSizeInPages + ); + +/** + Initialize UHCI memory management. + + @param UhcDev The UCHI device. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +InitializeMemoryManagement ( + IN USB_UHC_DEV *UhcDev + ); + +/** + Initialize UHCI memory management. + + @param UhcDev The UCHI device. + @param Pool Buffer pointer to store the buffer pointer. + @param AllocSize The size of the pool to be allocated. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +UhcAllocatePool ( + IN USB_UHC_DEV *UhcDev, + OUT UINT8 **Pool, + IN UINTN AllocSize + ); + +/** + Alloc Memory In MemoryBlock. + + @param MemoryHeader The pointer to memory manage header. + @param Pool Buffer pointer to store the buffer pointer. + @param NumberOfMemoryUnit The size of the pool to be allocated. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +AllocMemInMemoryBlock ( + IN MEMORY_MANAGE_HEADER *MemoryHeader, + OUT VOID **Pool, + IN UINTN NumberOfMemoryUnit + ); + +/** + Uhci Free Pool. + + @param UhcDev The UHCI device. + @param Pool A pointer to store the buffer address. + @param AllocSize The size of the pool to be freed. + +**/ +VOID +UhcFreePool ( + IN USB_UHC_DEV *UhcDev, + IN UINT8 *Pool, + IN UINTN AllocSize + ); + +/** + Insert a new memory header into list. + + @param MemoryHeader A pointer to the memory header list. + @param NewMemoryHeader A new memory header to be inserted into the list. + +**/ +VOID +InsertMemoryHeaderToList ( + IN MEMORY_MANAGE_HEADER *MemoryHeader, + IN MEMORY_MANAGE_HEADER *NewMemoryHeader + ); + +/** + Judge the memory block in the memory header is empty or not. + + @param MemoryHeaderPtr A pointer to the memory header list. + + @retval Whether the memory block in the memory header is empty or not. + +**/ +BOOLEAN +IsMemoryBlockEmptied ( + IN MEMORY_MANAGE_HEADER *MemoryHeaderPtr + ); + +/** + remove a memory header from list. + + @param FirstMemoryHeader A pointer to the memory header list. + @param FreeMemoryHeader A memory header to be removed into the list. + +**/ +VOID +DelinkMemoryBlock ( + IN MEMORY_MANAGE_HEADER *FirstMemoryHeader, + IN MEMORY_MANAGE_HEADER *FreeMemoryHeader + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/UhciPei/UhciPei.inf b/MdeModulePkg/Bus/Pci/UhciPei/UhciPei.inf new file mode 100644 index 0000000000..cf91ebcac6 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciPei/UhciPei.inf @@ -0,0 +1,62 @@ +## @file +# Component description file for UhcPeim PEIM to produce gPeiUsbHostControllerPpiGuid based on gPeiUsbControllerPpiGuid +# which is used to enable recovery function from USB Drivers. + +# +# Usb Host Controller PEIM to support recovery from USB device. +# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions +# of the BSD License which accompanies this distribution. The +# full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UhciPei + FILE_GUID = C463CEAC-FC57-4f36-88B7-356C750C3BCA + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = UhcPeimEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + UhcPeim.c + UhcPeim.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + IoLib + TimerLib + BaseMemoryLib + PeiServicesLib + PeimEntryPoint + DebugLib + + +[Ppis] + gPeiUsbHostControllerPpiGuid # PPI ALWAYS_PRODUCED + gPeiUsbControllerPpiGuid # PPI ALWAYS_CONSUMED + + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid AND gPeiUsbControllerPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid + + -- cgit v1.2.3