From 4b1bf81c20d5523554a83f6604df7930396f7c21 Mon Sep 17 00:00:00 2001 From: jljusten Date: Mon, 27 Jun 2011 23:30:55 +0000 Subject: [PATCH] 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 + MdeModulePkg/Bus/Usb/UsbBotPei/BotPeim.c | 401 +++ MdeModulePkg/Bus/Usb/UsbBotPei/BotPeim.h | 224 ++ MdeModulePkg/Bus/Usb/UsbBotPei/PeiAtapi.c | 630 ++++ MdeModulePkg/Bus/Usb/UsbBotPei/PeiUsbLib.c | 331 ++ MdeModulePkg/Bus/Usb/UsbBotPei/PeiUsbLib.h | 248 ++ MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPei.inf | 65 + MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPeim.c | 720 ++++ MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPeim.h | 233 ++ MdeModulePkg/Bus/Usb/UsbBotPei/UsbPeim.h | 179 + MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c | 537 +++ MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.h | 279 ++ MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.c | 333 ++ MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.h | 250 ++ MdeModulePkg/Bus/Usb/UsbBusPei/UsbBusPei.inf | 66 + MdeModulePkg/Bus/Usb/UsbBusPei/UsbIoPeim.c | 325 ++ MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c | 1041 ++++++ MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.h | 203 ++ MdeModulePkg/Include/Ppi/Usb2HostController.h | 263 ++ MdeModulePkg/Include/Ppi/UsbController.h | 88 + MdeModulePkg/Include/Ppi/UsbHostController.h | 251 ++ MdeModulePkg/Include/Ppi/UsbIo.h | 190 + MdeModulePkg/MdeModulePkg.dec | 13 + MdeModulePkg/MdeModulePkg.dsc | 4 + 36 files changed, 15879 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 create mode 100644 MdeModulePkg/Bus/Usb/UsbBotPei/BotPeim.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBotPei/BotPeim.h create mode 100644 MdeModulePkg/Bus/Usb/UsbBotPei/PeiAtapi.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBotPei/PeiUsbLib.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBotPei/PeiUsbLib.h create mode 100644 MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPei.inf create mode 100644 MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPeim.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPeim.h create mode 100644 MdeModulePkg/Bus/Usb/UsbBotPei/UsbPeim.h create mode 100644 MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.h create mode 100644 MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.h create mode 100644 MdeModulePkg/Bus/Usb/UsbBusPei/UsbBusPei.inf create mode 100644 MdeModulePkg/Bus/Usb/UsbBusPei/UsbIoPeim.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.h create mode 100644 MdeModulePkg/Include/Ppi/Usb2HostController.h create mode 100644 MdeModulePkg/Include/Ppi/UsbController.h create mode 100644 MdeModulePkg/Include/Ppi/UsbHostController.h create mode 100644 MdeModulePkg/Include/Ppi/UsbIo.h 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 + + diff --git a/MdeModulePkg/Bus/Usb/UsbBotPei/BotPeim.c b/MdeModulePkg/Bus/Usb/UsbBotPei/BotPeim.c new file mode 100644 index 0000000000..55c4a537fa --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBotPei/BotPeim.c @@ -0,0 +1,401 @@ +/** @file +BOT Transportation implementation. + +Copyright (c) 2006, 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 "UsbBotPeim.h" +#include "BotPeim.h" +#include "PeiUsbLib.h" + +/** + Reset the given usb device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDev The instance to PEI_BOT_DEVICE. + + @retval EFI_INVALID_PARAMETER Can not get usb io ppi. + @retval EFI_SUCCESS Failed to reset the given usb device. + +**/ +EFI_STATUS +BotRecoveryReset ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDev + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + UINT32 Timeout; + PEI_USB_IO_PPI *UsbIoPpi; + UINT8 EndpointAddr; + EFI_STATUS Status; + + UsbIoPpi = PeiBotDev->UsbIoPpi; + + if (UsbIoPpi == NULL) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST)); + + DevReq.RequestType = 0x21; + DevReq.Request = 0xFF; + DevReq.Value = 0; + DevReq.Index = 0; + DevReq.Length = 0; + + Timeout = 3000; + + Status = UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbNoData, + Timeout, + NULL, + 0 + ); + + // + // clear bulk in endpoint stall feature + // + EndpointAddr = (PeiBotDev->BulkInEndpoint)->EndpointAddress; + PeiUsbClearEndpointHalt (PeiServices, UsbIoPpi, EndpointAddr); + + // + // clear bulk out endpoint stall feature + // + EndpointAddr = (PeiBotDev->BulkOutEndpoint)->EndpointAddress; + PeiUsbClearEndpointHalt (PeiServices, UsbIoPpi, EndpointAddr); + + return Status; +} + +/** + Send the command to the device using Bulk-Out endpoint. + + This function sends the command to the device using Bulk-Out endpoint. + BOT transfer is composed of three phases: Command, Data, and Status. + This is the Command phase. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDev The instance to PEI_BOT_DEVICE. + @param Command The command to transfer to device. + @param CommandSize The length of the command. + @param DataTransferLength The expected length of the data. + @param Direction The direction of the data. + @param Timeout Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + + @retval EFI_DEVICE_ERROR Successful to send the command to device. + @retval EFI_SUCCESS Failed to send the command to device. + +**/ +EFI_STATUS +BotCommandPhase ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDev, + IN VOID *Command, + IN UINT8 CommandSize, + IN UINT32 DataTransferLength, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINT16 Timeout + ) +{ + CBW Cbw; + EFI_STATUS Status; + PEI_USB_IO_PPI *UsbIoPpi; + UINTN DataSize; + + UsbIoPpi = PeiBotDev->UsbIoPpi; + + ZeroMem (&Cbw, sizeof (CBW)); + + // + // Fill the command block, detailed see BOT spec + // + Cbw.Signature = CBWSIG; + Cbw.Tag = 0x01; + Cbw.DataTransferLength = DataTransferLength; + Cbw.Flags = (UINT8) ((Direction == EfiUsbDataIn) ? 0x80 : 0); + Cbw.Lun = 0; + Cbw.CmdLen = CommandSize; + + CopyMem (Cbw.CmdBlock, Command, CommandSize); + + DataSize = sizeof (CBW); + + Status = UsbIoPpi->UsbBulkTransfer ( + PeiServices, + UsbIoPpi, + (PeiBotDev->BulkOutEndpoint)->EndpointAddress, + (UINT8 *) &Cbw, + &DataSize, + Timeout + ); + if (EFI_ERROR (Status)) { + // + // Command phase fail, we need to recovery reset this device + // + BotRecoveryReset (PeiServices, PeiBotDev); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Transfer the data between the device and host. + + This function transfers the data between the device and host. + BOT transfer is composed of three phases: Command, Data, and Status. + This is the Data phase. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDev The instance to PEI_BOT_DEVICE. + @param DataSize The length of the data. + @param DataBuffer The pointer to the data. + @param Direction The direction of the data. + @param Timeout Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + + @retval EFI_DEVICE_ERROR Successful to send the data to device. + @retval EFI_SUCCESS Failed to send the data to device. + +**/ +EFI_STATUS +BotDataPhase ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDev, + IN UINT32 *DataSize, + IN OUT VOID *DataBuffer, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINT16 Timeout + ) +{ + EFI_STATUS Status; + PEI_USB_IO_PPI *UsbIoPpi; + UINT8 EndpointAddr; + UINTN Remain; + UINTN Increment; + UINT32 MaxPacketLen; + UINT8 *BufferPtr; + UINTN TransferredSize; + + UsbIoPpi = PeiBotDev->UsbIoPpi; + + Remain = *DataSize; + BufferPtr = (UINT8 *) DataBuffer; + TransferredSize = 0; + + // + // retrieve the the max packet length of the given endpoint + // + if (Direction == EfiUsbDataIn) { + MaxPacketLen = (PeiBotDev->BulkInEndpoint)->MaxPacketSize; + EndpointAddr = (PeiBotDev->BulkInEndpoint)->EndpointAddress; + } else { + MaxPacketLen = (PeiBotDev->BulkOutEndpoint)->MaxPacketSize; + EndpointAddr = (PeiBotDev->BulkOutEndpoint)->EndpointAddress; + } + + while (Remain > 0) { + // + // Using 15 packets to avoid Bitstuff error + // + if (Remain > 16 * MaxPacketLen) { + Increment = 16 * MaxPacketLen; + } else { + Increment = Remain; + } + + Status = UsbIoPpi->UsbBulkTransfer ( + PeiServices, + UsbIoPpi, + EndpointAddr, + BufferPtr, + &Increment, + Timeout + ); + + TransferredSize += Increment; + + if (EFI_ERROR (Status)) { + PeiUsbClearEndpointHalt (PeiServices, UsbIoPpi, EndpointAddr); + return Status; + } + + BufferPtr += Increment; + Remain -= Increment; + } + + *DataSize = (UINT32) TransferredSize; + + return EFI_SUCCESS; +} + +/** + Get the command execution status from device. + + This function gets the command execution status from device. + BOT transfer is composed of three phases: Command, Data, and Status. + This is the Status phase. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDev The instance to PEI_BOT_DEVICE. + @param TransferStatus The status of the transaction. + @param Timeout Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + + @retval EFI_DEVICE_ERROR Successful to get the status of device. + @retval EFI_SUCCESS Failed to get the status of device. + +**/ +EFI_STATUS +BotStatusPhase ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDev, + OUT UINT8 *TransferStatus, + IN UINT16 Timeout + ) +{ + CSW Csw; + EFI_STATUS Status; + PEI_USB_IO_PPI *UsbIoPpi; + UINT8 EndpointAddr; + UINTN DataSize; + + UsbIoPpi = PeiBotDev->UsbIoPpi; + + ZeroMem (&Csw, sizeof (CSW)); + + EndpointAddr = (PeiBotDev->BulkInEndpoint)->EndpointAddress; + + DataSize = sizeof (CSW); + + // + // Get the status field from bulk transfer + // + Status = UsbIoPpi->UsbBulkTransfer ( + PeiServices, + UsbIoPpi, + EndpointAddr, + &Csw, + &DataSize, + Timeout + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Csw.Signature == CSWSIG) { + *TransferStatus = Csw.Status; + } else { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Send ATAPI command using BOT protocol. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDev The instance to PEI_BOT_DEVICE. + @param Command The command to be sent to ATAPI device. + @param CommandSize The length of the data to be sent. + @param DataBuffer The pointer to the data. + @param BufferLength The length of the data. + @param Direction The direction of the data. + @param TimeOutInMilliSeconds Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + + @retval EFI_DEVICE_ERROR Successful to get the status of device. + @retval EFI_SUCCESS Failed to get the status of device. + +**/ +EFI_STATUS +PeiAtapiCommand ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDev, + IN VOID *Command, + IN UINT8 CommandSize, + IN VOID *DataBuffer, + IN UINT32 BufferLength, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINT16 TimeOutInMilliSeconds + ) +{ + EFI_STATUS Status; + EFI_STATUS BotDataStatus; + UINT8 TransferStatus; + UINT32 BufferSize; + + BotDataStatus = EFI_SUCCESS; + // + // First send ATAPI command through Bot + // + Status = BotCommandPhase ( + PeiServices, + PeiBotDev, + Command, + CommandSize, + BufferLength, + Direction, + TimeOutInMilliSeconds + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // Send/Get Data if there is a Data Stage + // + switch (Direction) { + case EfiUsbDataIn: + case EfiUsbDataOut: + BufferSize = BufferLength; + + BotDataStatus = BotDataPhase ( + PeiServices, + PeiBotDev, + &BufferSize, + DataBuffer, + Direction, + TimeOutInMilliSeconds + ); + break; + + case EfiUsbNoData: + break; + } + // + // Status Phase + // + Status = BotStatusPhase ( + PeiServices, + PeiBotDev, + &TransferStatus, + TimeOutInMilliSeconds + ); + if (EFI_ERROR (Status)) { + BotRecoveryReset (PeiServices, PeiBotDev); + return EFI_DEVICE_ERROR; + } + + if (TransferStatus == 0x01) { + return EFI_DEVICE_ERROR; + } + + return BotDataStatus; +} diff --git a/MdeModulePkg/Bus/Usb/UsbBotPei/BotPeim.h b/MdeModulePkg/Bus/Usb/UsbBotPei/BotPeim.h new file mode 100644 index 0000000000..07235c397b --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBotPei/BotPeim.h @@ -0,0 +1,224 @@ +/** @file +BOT Transportation implementation. + +Copyright (c) 2006, 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 _PEI_BOT_PEIM_H_ +#define _PEI_BOT_PEIM_H_ + + +#include + +#include +#include +#include + +//#include +#include +#include +#include + +#include + +#pragma pack(1) +// +// Bulk Only device protocol +// +typedef struct { + UINT32 Signature; + UINT32 Tag; + UINT32 DataTransferLength; + UINT8 Flags; + UINT8 Lun; + UINT8 CmdLen; + UINT8 CmdBlock[16]; +} CBW; + +typedef struct { + UINT32 Signature; + UINT32 Tag; + UINT32 DataResidue; + UINT8 Status; +} CSW; + +#pragma pack() +// +// Status code, see Usb Bot device spec +// +#define CSWSIG 0x53425355 +#define CBWSIG 0x43425355 + +/** + Sends out ATAPI Inquiry Packet Command to the specified device. This command will + return INQUIRY data of the device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + + @retval EFI_SUCCESS Inquiry command completes successfully. + @retval EFI_DEVICE_ERROR Inquiry command failed. + +**/ +EFI_STATUS +PeiUsbInquiry ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice + ); + +/** + Sends out ATAPI Test Unit Ready Packet Command to the specified device + to find out whether device is accessible. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + + @retval EFI_SUCCESS TestUnit command executed successfully. + @retval EFI_DEVICE_ERROR Device cannot be executed TestUnit command successfully. + +**/ +EFI_STATUS +PeiUsbTestUnitReady ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice + ); + +/** + Sends out ATAPI Request Sense Packet Command to the specified device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + @param SenseCounts Length of sense buffer. + @param SenseKeyBuffer Pointer to sense buffer. + + @retval EFI_SUCCESS Command executed successfully. + @retval EFI_DEVICE_ERROR Some device errors happen. + +**/ +EFI_STATUS +PeiUsbRequestSense ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice, + OUT UINTN *SenseCounts, + IN UINT8 *SenseKeyBuffer + ); + +/** + Sends out ATAPI Read Capacity Packet Command to the specified device. + This command will return the information regarding the capacity of the + media in the device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + + @retval EFI_SUCCESS Command executed successfully. + @retval EFI_DEVICE_ERROR Some device errors happen. + +**/ +EFI_STATUS +PeiUsbReadCapacity ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice + ); + +/** + Sends out ATAPI Read Format Capacity Data Command to the specified device. + This command will return the information regarding the capacity of the + media in the device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + + @retval EFI_SUCCESS Command executed successfully. + @retval EFI_DEVICE_ERROR Some device errors happen. + +**/ +EFI_STATUS +PeiUsbReadFormattedCapacity ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice + ); + +/** + Execute Read(10) ATAPI command on a specific SCSI target. + + Executes the ATAPI Read(10) command on the ATAPI target specified by PeiBotDevice. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + @param Buffer The pointer to data buffer. + @param Lba The start logic block address of reading. + @param NumberOfBlocks The block number of reading. + + @retval EFI_SUCCESS Command executed successfully. + @retval EFI_DEVICE_ERROR Some device errors happen. + +**/ +EFI_STATUS +PeiUsbRead10 ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice, + IN VOID *Buffer, + IN EFI_PEI_LBA Lba, + IN UINTN NumberOfBlocks + ); + +/** + Check if there is media according to sense data. + + @param SenseData Pointer to sense data. + @param SenseCounts Count of sense data. + + @retval TRUE No media + @retval FALSE Media exists + +**/ +BOOLEAN +IsNoMedia ( + IN ATAPI_REQUEST_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ); + +/** + Check if there is media error according to sense data. + + @param SenseData Pointer to sense data. + @param SenseCounts Count of sense data. + + @retval TRUE Media error + @retval FALSE No media error + +**/ +BOOLEAN +IsMediaError ( + IN ATAPI_REQUEST_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ); + +/** + Check if media is changed according to sense data. + + @param SenseData Pointer to sense data. + @param SenseCounts Count of sense data. + + @retval TRUE There is media change event. + @retval FALSE media is NOT changed. + +**/ +BOOLEAN +IsMediaChange ( + IN ATAPI_REQUEST_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ); + +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbBotPei/PeiAtapi.c b/MdeModulePkg/Bus/Usb/UsbBotPei/PeiAtapi.c new file mode 100644 index 0000000000..28574d07e1 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBotPei/PeiAtapi.c @@ -0,0 +1,630 @@ +/** @file +Pei USB ATATPI command implementations. + +Copyright (c) 1999 - 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 "UsbBotPeim.h" +#include "BotPeim.h" + +#define MAXSENSEKEY 5 + +/** + Sends out ATAPI Inquiry Packet Command to the specified device. This command will + return INQUIRY data of the device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + + @retval EFI_SUCCESS Inquiry command completes successfully. + @retval EFI_DEVICE_ERROR Inquiry command failed. + +**/ +EFI_STATUS +PeiUsbInquiry ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice + ) +{ + ATAPI_PACKET_COMMAND Packet; + EFI_STATUS Status; + ATAPI_INQUIRY_DATA Idata; + + // + // fill command packet + // + ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); + ZeroMem (&Idata, sizeof (ATAPI_INQUIRY_DATA)); + + Packet.Inquiry.opcode = ATA_CMD_INQUIRY; + Packet.Inquiry.page_code = 0; + Packet.Inquiry.allocation_length = 36; + + // + // Send scsi INQUIRY command packet. + // According to SCSI Primary Commands-2 spec, host only needs to + // retrieve the first 36 bytes for standard INQUIRY data. + // + Status = PeiAtapiCommand ( + PeiServices, + PeiBotDevice, + &Packet, + (UINT8) sizeof (ATAPI_PACKET_COMMAND), + &Idata, + 36, + EfiUsbDataIn, + 2000 + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + if ((Idata.peripheral_type & 0x1f) == 0x05) { + PeiBotDevice->DeviceType = USBCDROM; + PeiBotDevice->Media.BlockSize = 0x800; + } else { + PeiBotDevice->DeviceType = USBFLOPPY; + PeiBotDevice->Media.BlockSize = 0x200; + } + + return EFI_SUCCESS; +} + +/** + Sends out ATAPI Test Unit Ready Packet Command to the specified device + to find out whether device is accessible. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + + @retval EFI_SUCCESS TestUnit command executed successfully. + @retval EFI_DEVICE_ERROR Device cannot be executed TestUnit command successfully. + +**/ +EFI_STATUS +PeiUsbTestUnitReady ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice + ) +{ + ATAPI_PACKET_COMMAND Packet; + EFI_STATUS Status; + + // + // fill command packet + // + ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); + Packet.TestUnitReady.opcode = ATA_CMD_TEST_UNIT_READY; + + // + // send command packet + // + Status = PeiAtapiCommand ( + PeiServices, + PeiBotDevice, + &Packet, + (UINT8) sizeof (ATAPI_PACKET_COMMAND), + NULL, + 0, + EfiUsbNoData, + 2000 + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Sends out ATAPI Request Sense Packet Command to the specified device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + @param SenseCounts Length of sense buffer. + @param SenseKeyBuffer Pointer to sense buffer. + + @retval EFI_SUCCESS Command executed successfully. + @retval EFI_DEVICE_ERROR Some device errors happen. + +**/ +EFI_STATUS +PeiUsbRequestSense ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice, + OUT UINTN *SenseCounts, + IN UINT8 *SenseKeyBuffer + ) +{ + EFI_STATUS Status; + ATAPI_PACKET_COMMAND Packet; + UINT8 *Ptr; + BOOLEAN SenseReq; + ATAPI_REQUEST_SENSE_DATA *Sense; + + *SenseCounts = 0; + + // + // fill command packet for Request Sense Packet Command + // + ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); + Packet.RequestSence.opcode = ATA_CMD_REQUEST_SENSE; + Packet.RequestSence.allocation_length = (UINT8) sizeof (ATAPI_REQUEST_SENSE_DATA); + + Ptr = SenseKeyBuffer; + + SenseReq = TRUE; + + // + // request sense data from device continuously + // until no sense data exists in the device. + // + while (SenseReq) { + Sense = (ATAPI_REQUEST_SENSE_DATA *) Ptr; + + // + // send out Request Sense Packet Command and get one Sense + // data form device. + // + Status = PeiAtapiCommand ( + PeiServices, + PeiBotDevice, + &Packet, + (UINT8) sizeof (ATAPI_PACKET_COMMAND), + (VOID *) Ptr, + sizeof (ATAPI_REQUEST_SENSE_DATA), + EfiUsbDataIn, + 2000 + ); + + // + // failed to get Sense data + // + if (EFI_ERROR (Status)) { + if (*SenseCounts == 0) { + return EFI_DEVICE_ERROR; + } else { + return EFI_SUCCESS; + } + } + + if (Sense->sense_key != ATA_SK_NO_SENSE) { + + Ptr += sizeof (ATAPI_REQUEST_SENSE_DATA); + // + // Ptr is byte based pointer + // + (*SenseCounts)++; + + if (*SenseCounts == MAXSENSEKEY) { + break; + } + + } else { + // + // when no sense key, skip out the loop + // + SenseReq = FALSE; + } + } + + return EFI_SUCCESS; +} + +/** + Sends out ATAPI Read Capacity Packet Command to the specified device. + This command will return the information regarding the capacity of the + media in the device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + + @retval EFI_SUCCESS Command executed successfully. + @retval EFI_DEVICE_ERROR Some device errors happen. + +**/ +EFI_STATUS +PeiUsbReadCapacity ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice + ) +{ + EFI_STATUS Status; + ATAPI_PACKET_COMMAND Packet; + ATAPI_READ_CAPACITY_DATA Data; + + ZeroMem (&Data, sizeof (ATAPI_READ_CAPACITY_DATA)); + ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); + + Packet.Inquiry.opcode = ATA_CMD_READ_CAPACITY; + + // + // send command packet + // + Status = PeiAtapiCommand ( + PeiServices, + PeiBotDevice, + &Packet, + (UINT8) sizeof (ATAPI_PACKET_COMMAND), + (VOID *) &Data, + sizeof (ATAPI_READ_CAPACITY_DATA), + EfiUsbDataIn, + 2000 + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + PeiBotDevice->Media.LastBlock = (Data.LastLba3 << 24) | (Data.LastLba2 << 16) | (Data.LastLba1 << 8) | Data.LastLba0; + + PeiBotDevice->Media.MediaPresent = TRUE; + + return EFI_SUCCESS; +} + +/** + Sends out ATAPI Read Format Capacity Data Command to the specified device. + This command will return the information regarding the capacity of the + media in the device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + + @retval EFI_SUCCESS Command executed successfully. + @retval EFI_DEVICE_ERROR Some device errors happen. + +**/ +EFI_STATUS +PeiUsbReadFormattedCapacity ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice + ) +{ + EFI_STATUS Status; + ATAPI_PACKET_COMMAND Packet; + ATAPI_READ_FORMAT_CAPACITY_DATA FormatData; + + ZeroMem (&FormatData, sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA)); + ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); + + Packet.ReadFormatCapacity.opcode = ATA_CMD_READ_FORMAT_CAPACITY; + Packet.ReadFormatCapacity.allocation_length_lo = 12; + + // + // send command packet + // + Status = PeiAtapiCommand ( + PeiServices, + PeiBotDevice, + &Packet, + (UINT8) sizeof (ATAPI_PACKET_COMMAND), + (VOID *) &FormatData, + sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA), + EfiUsbDataIn, + 2000 + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + if (FormatData.DesCode == 3) { + // + // Media is not present + // + PeiBotDevice->Media.MediaPresent = FALSE; + PeiBotDevice->Media.LastBlock = 0; + + } else { + + PeiBotDevice->Media.LastBlock = (FormatData.LastLba3 << 24) | (FormatData.LastLba2 << 16) | (FormatData.LastLba1 << 8) | FormatData.LastLba0; + + PeiBotDevice->Media.LastBlock--; + + PeiBotDevice->Media.MediaPresent = TRUE; + } + + return EFI_SUCCESS; +} + +/** + Execute Read(10) ATAPI command on a specific SCSI target. + + Executes the ATAPI Read(10) command on the ATAPI target specified by PeiBotDevice. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. + @param Buffer The pointer to data buffer. + @param Lba The start logic block address of reading. + @param NumberOfBlocks The block number of reading. + + @retval EFI_SUCCESS Command executed successfully. + @retval EFI_DEVICE_ERROR Some device errors happen. + +**/ +EFI_STATUS +PeiUsbRead10 ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDevice, + IN VOID *Buffer, + IN EFI_PEI_LBA Lba, + IN UINTN NumberOfBlocks + ) +{ + ATAPI_PACKET_COMMAND Packet; + ATAPI_READ10_CMD *Read10Packet; + UINT16 MaxBlock; + UINT16 BlocksRemaining; + UINT16 SectorCount; + UINT32 Lba32; + UINT32 BlockSize; + UINT32 ByteCount; + VOID *PtrBuffer; + EFI_STATUS Status; + UINT16 TimeOut; + + // + // prepare command packet for the Inquiry Packet Command. + // + ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); + Read10Packet = &Packet.Read10; + Lba32 = (UINT32) Lba; + PtrBuffer = Buffer; + + BlockSize = (UINT32) PeiBotDevice->Media.BlockSize; + + MaxBlock = (UINT16) (65535 / BlockSize); + BlocksRemaining = (UINT16) NumberOfBlocks; + + Status = EFI_SUCCESS; + while (BlocksRemaining > 0) { + + if (BlocksRemaining <= MaxBlock) { + + SectorCount = BlocksRemaining; + + } else { + + SectorCount = MaxBlock; + } + // + // fill the Packet data structure + // + Read10Packet->opcode = ATA_CMD_READ_10; + + // + // Lba0 ~ Lba3 specify the start logical block address of the data transfer. + // Lba0 is MSB, Lba3 is LSB + // + Read10Packet->Lba3 = (UINT8) (Lba32 & 0xff); + Read10Packet->Lba2 = (UINT8) (Lba32 >> 8); + Read10Packet->Lba1 = (UINT8) (Lba32 >> 16); + Read10Packet->Lba0 = (UINT8) (Lba32 >> 24); + + // + // TranLen0 ~ TranLen1 specify the transfer length in block unit. + // TranLen0 is MSB, TranLen is LSB + // + Read10Packet->TranLen1 = (UINT8) (SectorCount & 0xff); + Read10Packet->TranLen0 = (UINT8) (SectorCount >> 8); + + ByteCount = SectorCount * BlockSize; + + TimeOut = (UINT16) (SectorCount * 2000); + + // + // send command packet + // + Status = PeiAtapiCommand ( + PeiServices, + PeiBotDevice, + &Packet, + (UINT8) sizeof (ATAPI_PACKET_COMMAND), + (VOID *) PtrBuffer, + ByteCount, + EfiUsbDataIn, + TimeOut + ); + + if (Status != EFI_SUCCESS) { + return Status; + } + + Lba32 += SectorCount; + PtrBuffer = (UINT8 *) PtrBuffer + SectorCount * BlockSize; + BlocksRemaining = (UINT16) (BlocksRemaining - SectorCount); + } + + return Status; +} + +/** + Check if there is media according to sense data. + + @param SenseData Pointer to sense data. + @param SenseCounts Count of sense data. + + @retval TRUE No media + @retval FALSE Media exists + +**/ +BOOLEAN +IsNoMedia ( + IN ATAPI_REQUEST_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ) +{ + ATAPI_REQUEST_SENSE_DATA *SensePtr; + UINTN Index; + BOOLEAN NoMedia; + + NoMedia = FALSE; + SensePtr = SenseData; + + for (Index = 0; Index < SenseCounts; Index++) { + + switch (SensePtr->sense_key) { + + case ATA_SK_NOT_READY: + switch (SensePtr->addnl_sense_code) { + // + // if no media, fill IdeDev parameter with specific info. + // + case ATA_ASC_NO_MEDIA: + NoMedia = TRUE; + break; + + default: + break; + } + break; + + default: + break; + } + + SensePtr++; + } + + return NoMedia; +} + +/** + Check if there is media error according to sense data. + + @param SenseData Pointer to sense data. + @param SenseCounts Count of sense data. + + @retval TRUE Media error + @retval FALSE No media error + +**/ +BOOLEAN +IsMediaError ( + IN ATAPI_REQUEST_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ) +{ + ATAPI_REQUEST_SENSE_DATA *SensePtr; + UINTN Index; + BOOLEAN Error; + + SensePtr = SenseData; + Error = FALSE; + + for (Index = 0; Index < SenseCounts; Index++) { + + switch (SensePtr->sense_key) { + // + // Medium error case + // + case ATA_SK_MEDIUM_ERROR: + switch (SensePtr->addnl_sense_code) { + case ATA_ASC_MEDIA_ERR1: + // + // fall through + // + case ATA_ASC_MEDIA_ERR2: + // + // fall through + // + case ATA_ASC_MEDIA_ERR3: + // + // fall through + // + case ATA_ASC_MEDIA_ERR4: + Error = TRUE; + break; + + default: + break; + } + + break; + + // + // Medium upside-down case + // + case ATA_SK_NOT_READY: + switch (SensePtr->addnl_sense_code) { + case ATA_ASC_MEDIA_UPSIDE_DOWN: + Error = TRUE; + break; + + default: + break; + } + break; + + default: + break; + } + + SensePtr++; + } + + return Error; +} + +/** + Check if media is changed according to sense data. + + @param SenseData Pointer to sense data. + @param SenseCounts Count of sense data. + + @retval TRUE There is media change event. + @retval FALSE media is NOT changed. + +**/ +BOOLEAN +IsMediaChange ( + IN ATAPI_REQUEST_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ) +{ + ATAPI_REQUEST_SENSE_DATA *SensePtr; + UINTN Index; + BOOLEAN MediaChange; + + MediaChange = FALSE; + + SensePtr = SenseData; + + for (Index = 0; Index < SenseCounts; Index++) { + // + // catch media change sense key and addition sense data + // + switch (SensePtr->sense_key) { + case ATA_SK_UNIT_ATTENTION: + switch (SensePtr->addnl_sense_code) { + case ATA_ASC_MEDIA_CHANGE: + MediaChange = TRUE; + break; + + default: + break; + } + break; + + default: + break; + } + + SensePtr++; + } + + return MediaChange; +} diff --git a/MdeModulePkg/Bus/Usb/UsbBotPei/PeiUsbLib.c b/MdeModulePkg/Bus/Usb/UsbBotPei/PeiUsbLib.c new file mode 100644 index 0000000000..2a8fde07ff --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBotPei/PeiUsbLib.c @@ -0,0 +1,331 @@ +/** @file +Common Libarary for PEI USB. + +Copyright (c) 2006, 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 "UsbPeim.h" +#include "PeiUsbLib.h" + +/** + Get a given usb descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Value Request Value. + @param Index Request Index. + @param DescriptorLength Request descriptor Length. + @param Descriptor Request descriptor. + + + @retval EFI_SUCCESS Usb descriptor is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the usb descriptor due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbGetDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT16 Value, + IN UINT16 Index, + IN UINT16 DescriptorLength, + OUT VOID *Descriptor + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + + ASSERT (UsbIoPpi != NULL); + + DevReq.RequestType = USB_DEV_GET_DESCRIPTOR_REQ_TYPE; + DevReq.Request = USB_DEV_GET_DESCRIPTOR; + DevReq.Value = Value; + DevReq.Index = Index; + DevReq.Length = DescriptorLength; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbDataIn, + PcdGet32 (PcdUsbTransferTimeoutValue), + Descriptor, + DescriptorLength + ); +} + +/** + Set a usb device with a specified address. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param AddressValue The address to assign. + + @retval EFI_SUCCESS Usb device address is set successfully. + @retval EFI_DEVICE_ERROR Cannot set the usb address due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbSetDeviceAddress ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT16 AddressValue + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + + ASSERT (UsbIoPpi != NULL); + + DevReq.RequestType = USB_DEV_SET_ADDRESS_REQ_TYPE; + DevReq.Request = USB_DEV_SET_ADDRESS; + DevReq.Value = AddressValue; + DevReq.Index = 0; + DevReq.Length = 0; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbNoData, + PcdGet32 (PcdUsbTransferTimeoutValue), + NULL, + 0 + ); +} + +/** + Clear a given usb feature. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Recipient The recipient of ClearFeature Request, should be one of Device/Interface/Endpoint. + @param Value Request Value. + @param Target Request Index. + + @retval EFI_SUCCESS Usb feature is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the usb feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbClearDeviceFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN EFI_USB_RECIPIENT Recipient, + IN UINT16 Value, + IN UINT16 Target + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + + ASSERT (UsbIoPpi != NULL); + + switch (Recipient) { + case EfiUsbDevice: + DevReq.RequestType = USB_DEV_CLEAR_FEATURE_REQ_TYPE_D; + break; + + case EfiUsbInterface: + DevReq.RequestType = USB_DEV_CLEAR_FEATURE_REQ_TYPE_I; + break; + + case EfiUsbEndpoint: + DevReq.RequestType = USB_DEV_CLEAR_FEATURE_REQ_TYPE_E; + break; + } + + DevReq.Request = USB_DEV_CLEAR_FEATURE; + DevReq.Value = Value; + DevReq.Index = Target; + DevReq.Length = 0; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbNoData, + PcdGet32 (PcdUsbTransferTimeoutValue), + NULL, + 0 + ); +} + +/** + Configure a usb device to Configuration 1. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + + @retval EFI_SUCCESS Usb device is set to use Configuration 1 successfully. + @retval EFI_DEVICE_ERROR Cannot set the usb device due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbSetConfiguration ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST)); + + DevReq.RequestType = USB_DEV_SET_CONFIGURATION_REQ_TYPE; + DevReq.Request = USB_DEV_SET_CONFIGURATION; + DevReq.Value = 1; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbNoData, + PcdGet32 (PcdUsbTransferTimeoutValue), + NULL, + 0 + ); +} + +/** + Clear Endpoint Halt. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param EndpointAddress The endpoint address. + + @retval EFI_SUCCESS Endpoint halt is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the endpoint halt status due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbClearEndpointHalt ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 EndpointAddress + ) +{ + EFI_STATUS Status; + PEI_USB_DEVICE *PeiUsbDev; + EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDescriptor; + UINT8 EndpointIndex; + + EndpointIndex = 0; + PeiUsbDev = PEI_USB_DEVICE_FROM_THIS (UsbIoPpi); + + while (EndpointIndex < MAX_ENDPOINT) { + Status = UsbIoPpi->UsbGetEndpointDescriptor (PeiServices, UsbIoPpi, EndpointIndex, &EndpointDescriptor); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + if (EndpointDescriptor->EndpointAddress == EndpointAddress) { + break; + } + + EndpointIndex++; + } + + if (EndpointIndex == MAX_ENDPOINT) { + return EFI_INVALID_PARAMETER; + } + + Status = PeiUsbClearDeviceFeature ( + PeiServices, + UsbIoPpi, + EfiUsbEndpoint, + EfiUsbEndpointHalt, + EndpointAddress + ); + + // + // set data toggle to zero. + // + if ((PeiUsbDev->DataToggle & (1 << EndpointIndex)) != 0) { + PeiUsbDev->DataToggle = (UINT8) (PeiUsbDev->DataToggle ^ (1 << EndpointIndex)); + } + + return Status; +} + +/** + Judge if the port is connected with a usb device or not. + + @param PortStatus The usb port status gotten. + + @retval TRUE A usb device is connected with the port. + @retval FALSE No usb device is connected with the port. + +**/ +BOOLEAN +IsPortConnect ( + IN UINT16 PortStatus + ) +{ + // + // return the bit 0 value of PortStatus + // + if ((PortStatus & USB_PORT_STAT_CONNECTION) != 0) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Judge if the port is connected with a low-speed usb device or not. + + @param PortStatus The usb port status gotten. + + @retval TRUE A low-speed usb device is connected with the port. + @retval FALSE No low-speed usb device is connected with the port. + +**/ +BOOLEAN +IsPortLowSpeedDeviceAttached ( + IN UINT16 PortStatus + ) +{ + // + // return the bit 9 value of PortStatus + // + if ((PortStatus & USB_PORT_STAT_LOW_SPEED) != 0) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Judge if the port is in "connection change" status or not. + + @param PortChangeStatus The usb port change status gotten. + + @retval TRUE The port is in "connection change" status. + @retval FALSE The port is NOT in "connection change" status. + +**/ +BOOLEAN +IsPortConnectChange ( + IN UINT16 PortChangeStatus + ) +{ + // + // return the bit 0 value of PortChangeStatus + // + if ((PortChangeStatus & USB_PORT_STAT_C_CONNECTION) != 0) { + return TRUE; + } else { + return FALSE; + } +} diff --git a/MdeModulePkg/Bus/Usb/UsbBotPei/PeiUsbLib.h b/MdeModulePkg/Bus/Usb/UsbBotPei/PeiUsbLib.h new file mode 100644 index 0000000000..eafccfdbf8 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBotPei/PeiUsbLib.h @@ -0,0 +1,248 @@ +/** @file +Common Libarary for PEI USB. + +Copyright (c) 1999 - 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 _PEI_USB_LIB_H_ +#define _PEI_USB_LIB_H_ +// +// Standard device request and request type +// By [Spec-USB20/Chapter-9.4] +// +#define USB_DEV_GET_STATUS 0x00 +#define USB_DEV_GET_STATUS_REQ_TYPE_D 0x80 // Receiver : Device +#define USB_DEV_GET_STATUS_REQ_TYPE_I 0x81 // Receiver : Interface +#define USB_DEV_GET_STATUS_REQ_TYPE_E 0x82 // Receiver : Endpoint + +#define USB_DEV_CLEAR_FEATURE 0x01 +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_D 0x00 // Receiver : Device +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_I 0x01 // Receiver : Interface +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_E 0x02 // Receiver : Endpoint + +#define USB_DEV_SET_FEATURE 0x03 +#define USB_DEV_SET_FEATURE_REQ_TYPE_D 0x00 // Receiver : Device +#define USB_DEV_SET_FEATURE_REQ_TYPE_I 0x01 // Receiver : Interface +#define USB_DEV_SET_FEATURE_REQ_TYPE_E 0x02 // Receiver : Endpoint + +#define USB_DEV_SET_ADDRESS 0x05 +#define USB_DEV_SET_ADDRESS_REQ_TYPE 0x00 + +#define USB_DEV_GET_DESCRIPTOR 0x06 +#define USB_DEV_GET_DESCRIPTOR_REQ_TYPE 0x80 + +#define USB_DEV_SET_DESCRIPTOR 0x07 +#define USB_DEV_SET_DESCRIPTOR_REQ_TYPE 0x00 + +#define USB_DEV_GET_CONFIGURATION 0x08 +#define USB_DEV_GET_CONFIGURATION_REQ_TYPE 0x80 + +#define USB_DEV_SET_CONFIGURATION 0x09 +#define USB_DEV_SET_CONFIGURATION_REQ_TYPE 0x00 + +#define USB_DEV_GET_INTERFACE 0x0A +#define USB_DEV_GET_INTERFACE_REQ_TYPE 0x81 + +#define USB_DEV_SET_INTERFACE 0x0B +#define USB_DEV_SET_INTERFACE_REQ_TYPE 0x01 + +#define USB_DEV_SYNCH_FRAME 0x0C +#define USB_DEV_SYNCH_FRAME_REQ_TYPE 0x82 + +// +// USB Descriptor types +// +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_HUB 0x29 +#define USB_DT_HID 0x21 + +// +// USB request type +// +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +// +// USB request targer device +// +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +typedef enum { + EfiUsbEndpointHalt, + EfiUsbDeviceRemoteWakeup +} EFI_USB_STANDARD_FEATURE_SELECTOR; + +// +// Usb Data recipient type +// +typedef enum { + EfiUsbDevice, + EfiUsbInterface, + EfiUsbEndpoint +} EFI_USB_RECIPIENT; + +/** + Get a given usb descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Value Request Value. + @param Index Request Index. + @param DescriptorLength Request descriptor Length. + @param Descriptor Request descriptor. + + + @retval EFI_SUCCESS Usb descriptor is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the usb descriptor due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbGetDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT16 Value, + IN UINT16 Index, + IN UINT16 DescriptorLength, + OUT VOID *Descriptor + ); + +/** + Set a usb device with a specified address. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param AddressValue The address to assign. + + @retval EFI_SUCCESS Usb device address is set successfully. + @retval EFI_DEVICE_ERROR Cannot set the usb address due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbSetDeviceAddress ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT16 AddressValue + ); + +/** + Clear a given usb feature. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Recipient The recipient of ClearFeature Request, should be one of Device/Interface/Endpoint. + @param Value Request Value. + @param Target Request Index. + + @retval EFI_SUCCESS Usb feature is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the usb feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbClearDeviceFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN EFI_USB_RECIPIENT Recipient, + IN UINT16 Value, + IN UINT16 Target + ); + +/** + Configure a usb device to Configuration 1. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + + @retval EFI_SUCCESS Usb device is set to use Configuration 1 successfully. + @retval EFI_DEVICE_ERROR Cannot set the usb device due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbSetConfiguration ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi + ); + +/** + Clear Endpoint Halt. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param EndpointAddress The endpoint address. + + @retval EFI_SUCCESS Endpoint halt is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the endpoint halt status due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbClearEndpointHalt ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 EndpointAddress + ); + +/** + Judge if the port is connected with a usb device or not. + + @param PortStatus The usb port status gotten. + + @retval TRUE A usb device is connected with the port. + @retval FALSE No usb device is connected with the port. + +**/ +BOOLEAN +IsPortConnect ( + IN UINT16 PortStatus + ); + +/** + Judge if the port is connected with a low-speed usb device or not. + + @param PortStatus The usb port status gotten. + + @retval TRUE A low-speed usb device is connected with the port. + @retval FALSE No low-speed usb device is connected with the port. + +**/ +BOOLEAN +IsPortLowSpeedDeviceAttached ( + IN UINT16 PortStatus + ); + +/** + Judge if the port is in "connection change" status or not. + + @param PortChangeStatus The usb port change status gotten. + + @retval TRUE The port is in "connection change" status. + @retval FALSE The port is NOT in "connection change" status. + +**/ +BOOLEAN +IsPortConnectChange ( + IN UINT16 PortChangeStatus + ); +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPei.inf b/MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPei.inf new file mode 100644 index 0000000000..a2442c112e --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPei.inf @@ -0,0 +1,65 @@ +## @file +# Component description file for UsbBotPei module. +# +# Usb mass storage device Peim driver 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 = UsbBotPei + FILE_GUID = 8401A046-6F70-4505-8471-7015B40355E3 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = PeimInitializeUsbBot + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + PeiUsbLib.c + PeiAtapi.c + BotPeim.c + UsbBotPeim.c + UsbPeim.h + UsbBotPeim.h + PeiUsbLib.h + BotPeim.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseMemoryLib + PeiServicesLib + PeimEntryPoint + DebugLib + PcdLib + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUsbTransferTimeoutValue + +[Ppis] + gEfiPeiVirtualBlockIoPpiGuid # PPI ALWAYS_PRODUCED + gPeiUsbIoPpiGuid # PPI ALWAYS_CONSUMED + + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid AND gPeiUsbIoPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid + diff --git a/MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPeim.c b/MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPeim.c new file mode 100644 index 0000000000..b04bd42cff --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPeim.c @@ -0,0 +1,720 @@ +/** @file + +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 "UsbBotPeim.h" +#include "BotPeim.h" + +// +// Global function +// +EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList = { + EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, + &gPeiUsbIoPpiGuid, + NotifyOnUsbIoPpi +}; + +EFI_PEI_RECOVERY_BLOCK_IO_PPI mRecoveryBlkIoPpi = { + BotGetNumberOfBlockDevices, + BotGetMediaInfo, + BotReadBlocks +}; + +EFI_PEI_PPI_DESCRIPTOR mPpiList = { + EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, + &gEfiPeiVirtualBlockIoPpiGuid, + NULL +}; + +/** + Detect whether the removable media is present and whether it has changed. + + @param[in] PeiServices General-purpose services that are available to every + PEIM. + @param[in] PeiBotDev Indicates the PEI_BOT_DEVICE instance. + + @retval EFI_SUCCESS The media status is successfully checked. + @retval Other Failed to detect media. + +**/ +EFI_STATUS +PeiBotDetectMedia ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDev + ); + +/** + Initializes the Usb Bot. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS Usb bot driver is successfully initialized. + @retval EFI_OUT_OF_RESOURCES Can't initialize the driver. + +**/ +EFI_STATUS +EFIAPI +PeimInitializeUsbBot ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + UINTN UsbIoPpiInstance; + EFI_PEI_PPI_DESCRIPTOR *TempPpiDescriptor; + PEI_USB_IO_PPI *UsbIoPpi; + + // + // Shadow this PEIM to run from memory + // + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { + return EFI_SUCCESS; + } + + // + // locate all usb io PPIs + // + for (UsbIoPpiInstance = 0; UsbIoPpiInstance < PEI_FAT_MAX_USB_IO_PPI; UsbIoPpiInstance++) { + + Status = PeiServicesLocatePpi ( + &gPeiUsbIoPpiGuid, + UsbIoPpiInstance, + &TempPpiDescriptor, + (VOID **) &UsbIoPpi + ); + if (EFI_ERROR (Status)) { + break; + } + } + // + // Register a notify function + // + return PeiServicesNotifyPpi (&mNotifyList); +} + +/** + UsbIo installation notification function. + + This function finds out all the current USB IO PPIs in the system and add them + into private data. + + @param PeiServices Indirect reference to the PEI Services Table. + @param NotifyDesc Address of the notification descriptor data structure. + @param InvokePpi Address of the PPI that was invoked. + + @retval EFI_SUCCESS The function completes successfully. + +**/ +EFI_STATUS +EFIAPI +NotifyOnUsbIoPpi ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, + IN VOID *InvokePpi + ) +{ + PEI_USB_IO_PPI *UsbIoPpi; + + UsbIoPpi = (PEI_USB_IO_PPI *) InvokePpi; + + InitUsbBot (PeiServices, UsbIoPpi); + + return EFI_SUCCESS; +} + +/** + Initialize the usb bot device. + + @param[in] PeiServices General-purpose services that are available to every + PEIM. + @param[in] UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + + @retval EFI_SUCCESS The usb bot device is initialized successfully. + @retval Other Failed to initialize media. + +**/ +EFI_STATUS +InitUsbBot ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi + ) +{ + PEI_BOT_DEVICE *PeiBotDevice; + EFI_STATUS Status; + EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDesc; + UINTN MemPages; + EFI_PHYSICAL_ADDRESS AllocateAddress; + EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDesc; + UINT8 Index; + + // + // Check its interface + // + Status = UsbIoPpi->UsbGetInterfaceDescriptor ( + PeiServices, + UsbIoPpi, + &InterfaceDesc + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check if it is the BOT device we support + // + if ((InterfaceDesc->InterfaceClass != 0x08) || (InterfaceDesc->InterfaceProtocol != 0x50)) { + + return EFI_NOT_FOUND; + } + + MemPages = sizeof (PEI_BOT_DEVICE) / EFI_PAGE_SIZE + 1; + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + MemPages, + &AllocateAddress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + PeiBotDevice = (PEI_BOT_DEVICE *) ((UINTN) AllocateAddress); + + PeiBotDevice->Signature = PEI_BOT_DEVICE_SIGNATURE; + PeiBotDevice->UsbIoPpi = UsbIoPpi; + PeiBotDevice->AllocateAddress = (UINTN) AllocateAddress; + PeiBotDevice->BotInterface = InterfaceDesc; + + // + // Default value + // + PeiBotDevice->Media.DeviceType = UsbMassStorage; + PeiBotDevice->Media.BlockSize = 0x200; + + // + // Check its Bulk-in/Bulk-out endpoint + // + for (Index = 0; Index < 2; Index++) { + Status = UsbIoPpi->UsbGetEndpointDescriptor ( + PeiServices, + UsbIoPpi, + Index, + &EndpointDesc + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if ((EndpointDesc->EndpointAddress & 0x80) != 0) { + PeiBotDevice->BulkInEndpoint = EndpointDesc; + } else { + PeiBotDevice->BulkOutEndpoint = EndpointDesc; + } + } + + CopyMem ( + &(PeiBotDevice->BlkIoPpi), + &mRecoveryBlkIoPpi, + sizeof (EFI_PEI_RECOVERY_BLOCK_IO_PPI) + ); + CopyMem ( + &(PeiBotDevice->BlkIoPpiList), + &mPpiList, + sizeof (EFI_PEI_PPI_DESCRIPTOR) + ); + PeiBotDevice->BlkIoPpiList.Ppi = &PeiBotDevice->BlkIoPpi; + + Status = PeiUsbInquiry (PeiServices, PeiBotDevice); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + 1, + &AllocateAddress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + PeiBotDevice->SensePtr = (ATAPI_REQUEST_SENSE_DATA *) ((UINTN) AllocateAddress); + + Status = PeiServicesInstallPpi (&PeiBotDevice->BlkIoPpiList); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Gets the count of block I/O devices that one specific block driver detects. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. To the PEI ATAPI driver, it returns the number + of all the detected ATAPI devices it detects during the enumeration process. + To the PEI legacy floppy driver, it returns the number of all the legacy + devices it finds during its enumeration process. If no device is detected, + then the function will return zero. + + @param[in] PeiServices General-purpose services that are available + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discovered. + + @retval EFI_SUCCESS Operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +BotGetNumberOfBlockDevices ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + OUT UINTN *NumberBlockDevices + ) +{ + // + // For Usb devices, this value should be always 1 + // + *NumberBlockDevices = 1; + return EFI_SUCCESS; +} + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the + device index that was assigned during the enumeration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block media. + The caller is responsible for the ownership of this + data structure. + + @retval EFI_SUCCESS Media information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +BotGetMediaInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo + ) +{ + PEI_BOT_DEVICE *PeiBotDev; + EFI_STATUS Status; + + PeiBotDev = PEI_BOT_DEVICE_FROM_THIS (This); + + // + // First test unit ready + // + PeiUsbTestUnitReady ( + PeiServices, + PeiBotDev + ); + + Status = PeiBotDetectMedia ( + PeiServices, + PeiBotDev + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + CopyMem ( + MediaInfo, + &(PeiBotDev->Media), + sizeof (EFI_PEI_BLOCK_IO_MEDIA) + ); + + return EFI_SUCCESS; +} + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +BotReadBlocks ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + PEI_BOT_DEVICE *PeiBotDev; + EFI_STATUS Status; + UINTN BlockSize; + UINTN NumberOfBlocks; + + Status = EFI_SUCCESS; + PeiBotDev = PEI_BOT_DEVICE_FROM_THIS (This); + + // + // Check parameters + // + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + if (!PeiBotDev->Media.MediaPresent) { + return EFI_NO_MEDIA; + } + + BlockSize = PeiBotDev->Media.BlockSize; + + if (BufferSize % BlockSize != 0) { + Status = EFI_BAD_BUFFER_SIZE; + } + + if (StartLBA > PeiBotDev->Media.LastBlock) { + Status = EFI_INVALID_PARAMETER; + } + + NumberOfBlocks = BufferSize / (PeiBotDev->Media.BlockSize); + + if (Status == EFI_SUCCESS) { + + Status = PeiUsbTestUnitReady ( + PeiServices, + PeiBotDev + ); + if (Status == EFI_SUCCESS) { + Status = PeiUsbRead10 ( + PeiServices, + PeiBotDev, + Buffer, + StartLBA, + 1 + ); + } + } else { + // + // To generate sense data for DetectMedia use. + // + PeiUsbTestUnitReady ( + PeiServices, + PeiBotDev + ); + } + + if (EFI_ERROR (Status)) { + // + // if any error encountered, detect what happened to the media and + // update the media info accordingly. + // + Status = PeiBotDetectMedia ( + PeiServices, + PeiBotDev + ); + if (Status != EFI_SUCCESS) { + return EFI_DEVICE_ERROR; + } + + NumberOfBlocks = BufferSize / PeiBotDev->Media.BlockSize; + + if (!(PeiBotDev->Media.MediaPresent)) { + return EFI_NO_MEDIA; + } + + if (BufferSize % (PeiBotDev->Media.BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (StartLBA > PeiBotDev->Media.LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if ((StartLBA + NumberOfBlocks - 1) > PeiBotDev->Media.LastBlock) { + return EFI_INVALID_PARAMETER; + } + + Status = PeiUsbRead10 ( + PeiServices, + PeiBotDev, + Buffer, + StartLBA, + NumberOfBlocks + ); + + switch (Status) { + + case EFI_SUCCESS: + return EFI_SUCCESS; + + default: + return EFI_DEVICE_ERROR; + } + } else { + StartLBA += 1; + NumberOfBlocks -= 1; + Buffer = (UINT8 *) Buffer + PeiBotDev->Media.BlockSize; + + if (NumberOfBlocks == 0) { + return EFI_SUCCESS; + } + + Status = PeiUsbRead10 ( + PeiServices, + PeiBotDev, + Buffer, + StartLBA, + NumberOfBlocks + ); + switch (Status) { + + case EFI_SUCCESS: + return EFI_SUCCESS; + + default: + return EFI_DEVICE_ERROR; + + } + } +} + +/** + Detect whether the removable media is present and whether it has changed. + + @param[in] PeiServices General-purpose services that are available to every + PEIM. + @param[in] PeiBotDev Indicates the PEI_BOT_DEVICE instance. + + @retval EFI_SUCCESS The media status is successfully checked. + @retval Other Failed to detect media. + +**/ +EFI_STATUS +PeiBotDetectMedia ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDev + ) +{ + EFI_STATUS Status; + EFI_STATUS FloppyStatus; + UINTN SenseCounts; + BOOLEAN NeedReadCapacity; + EFI_PHYSICAL_ADDRESS AllocateAddress; + ATAPI_REQUEST_SENSE_DATA *SensePtr; + UINTN Retry; + + // + // if there is no media present,or media not changed, + // the request sense command will detect faster than read capacity command. + // read capacity command can be bypassed, thus improve performance. + // + SenseCounts = 0; + NeedReadCapacity = TRUE; + + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + 1, + &AllocateAddress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + SensePtr = PeiBotDev->SensePtr; + ZeroMem (SensePtr, EFI_PAGE_SIZE); + + Status = PeiUsbRequestSense ( + PeiServices, + PeiBotDev, + &SenseCounts, + (UINT8 *) SensePtr + ); + + if (Status == EFI_SUCCESS) { + // + // No Media + // + if (IsNoMedia (SensePtr, SenseCounts)) { + NeedReadCapacity = FALSE; + PeiBotDev->Media.MediaPresent = FALSE; + PeiBotDev->Media.LastBlock = 0; + } else { + // + // Media Changed + // + if (IsMediaChange (SensePtr, SenseCounts)) { + PeiBotDev->Media.MediaPresent = TRUE; + } + // + // Media Error + // + if (IsMediaError (SensePtr, SenseCounts)) { + // + // if media error encountered, make it look like no media present. + // + PeiBotDev->Media.MediaPresent = FALSE; + PeiBotDev->Media.LastBlock = 0; + } + + } + + } + + if (NeedReadCapacity) { + // + // Retry at most 4 times to detect media info + // + for (Retry = 0; Retry < 4; Retry++) { + switch (PeiBotDev->DeviceType) { + case USBCDROM: + Status = PeiUsbReadCapacity ( + PeiServices, + PeiBotDev + ); + break; + + case USBFLOPPY2: + Status = PeiUsbReadFormattedCapacity ( + PeiServices, + PeiBotDev + ); + if (EFI_ERROR(Status)|| + !PeiBotDev->Media.MediaPresent) { + // + // retry the ReadCapacity command + // + PeiBotDev->DeviceType = USBFLOPPY; + Status = EFI_DEVICE_ERROR; + } + break; + + case USBFLOPPY: + Status = PeiUsbReadCapacity ( + PeiServices, + PeiBotDev + ); + // + // retry the ReadFormatCapacity command + // + PeiBotDev->DeviceType = USBFLOPPY2; + break; + + default: + return EFI_INVALID_PARAMETER; + } + + SenseCounts = 0; + ZeroMem (SensePtr, EFI_PAGE_SIZE); + + if (Status == EFI_SUCCESS) { + break; + } + + FloppyStatus = PeiUsbRequestSense ( + PeiServices, + PeiBotDev, + &SenseCounts, + (UINT8 *) SensePtr + ); + + // + // If Request Sense data failed,retry. + // + if (EFI_ERROR (FloppyStatus)) { + continue; + } + // + // No Media + // + if (IsNoMedia (SensePtr, SenseCounts)) { + PeiBotDev->Media.MediaPresent = FALSE; + PeiBotDev->Media.LastBlock = 0; + break; + } + + if (IsMediaError (SensePtr, SenseCounts)) { + // + // if media error encountered, make it look like no media present. + // + PeiBotDev->Media.MediaPresent = FALSE; + PeiBotDev->Media.LastBlock = 0; + break; + } + } + // + // ENDFOR + // + // tell whether the readcapacity process is successful or not + // ("Status" variable record the latest status returned + // by ReadCapacity ) + // + if (Status != EFI_SUCCESS) { + return EFI_DEVICE_ERROR; + } + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPeim.h b/MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPeim.h new file mode 100644 index 0000000000..26d08dfd56 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPeim.h @@ -0,0 +1,233 @@ +/** @file +Usb BOT Peim definition. + +Copyright (c) 2006, 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 _PEI_USB_BOT_PEIM_H_ +#define _PEI_USB_BOT_PEIM_H_ + +#include + +#include +#include +#include + +#include + +#include +#include + +#define PEI_FAT_MAX_USB_IO_PPI 127 + +/** + Gets the count of block I/O devices that one specific block driver detects. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. To the PEI ATAPI driver, it returns the number + of all the detected ATAPI devices it detects during the enumeration process. + To the PEI legacy floppy driver, it returns the number of all the legacy + devices it finds during its enumeration process. If no device is detected, + then the function will return zero. + + @param[in] PeiServices General-purpose services that are available + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discovered. + + @retval EFI_SUCCESS Operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +BotGetNumberOfBlockDevices ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + OUT UINTN *NumberBlockDevices + ); + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the + device index that was assigned during the enumeration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block media. + The caller is responsible for the ownership of this + data structure. + + @retval EFI_SUCCESS Media information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +BotGetMediaInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo + ); + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +BotReadBlocks ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + UsbIo installation notification function. + + This function finds out all the current USB IO PPIs in the system and add them + into private data. + + @param PeiServices Indirect reference to the PEI Services Table. + @param NotifyDesc Address of the notification descriptor data structure. + @param InvokePpi Address of the PPI that was invoked. + + @retval EFI_SUCCESS The function completes successfully. + +**/ +EFI_STATUS +EFIAPI +NotifyOnUsbIoPpi ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, + IN VOID *InvokePpi + ); + +/** + Initialize the usb bot device. + + @param[in] PeiServices General-purpose services that are available to every + PEIM. + @param[in] UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + + @retval EFI_SUCCESS The usb bot device is initialized successfully. + @retval Other Failed to initialize media. + +**/ +EFI_STATUS +InitUsbBot ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi + ); + +#define USBCDROM 1 // let the device type value equal to USBCDROM, which is defined by PI spec. + // Therefore the CdExpressPei module can do recovery on UsbCdrom. +#define USBFLOPPY 2 // for those that use ReadCapacity(0x25) command to retrieve media capacity +#define USBFLOPPY2 3 // for those that use ReadFormatCapacity(0x23) command to retrieve media capacity + +// +// Bot device structure +// +#define PEI_BOT_DEVICE_SIGNATURE SIGNATURE_32 ('U', 'B', 'O', 'T') +typedef struct { + UINTN Signature; + EFI_PEI_RECOVERY_BLOCK_IO_PPI BlkIoPpi; + EFI_PEI_PPI_DESCRIPTOR BlkIoPpiList; + EFI_PEI_BLOCK_IO_MEDIA Media; + PEI_USB_IO_PPI *UsbIoPpi; + EFI_USB_INTERFACE_DESCRIPTOR *BotInterface; + EFI_USB_ENDPOINT_DESCRIPTOR *BulkInEndpoint; + EFI_USB_ENDPOINT_DESCRIPTOR *BulkOutEndpoint; + UINTN AllocateAddress; + UINTN DeviceType; + ATAPI_REQUEST_SENSE_DATA *SensePtr; +} PEI_BOT_DEVICE; + +#define PEI_BOT_DEVICE_FROM_THIS(a) CR (a, PEI_BOT_DEVICE, BlkIoPpi, PEI_BOT_DEVICE_SIGNATURE) + +/** + Send ATAPI command using BOT protocol. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param PeiBotDev The instance to PEI_BOT_DEVICE. + @param Command The command to be sent to ATAPI device. + @param CommandSize The length of the data to be sent. + @param DataBuffer The pointer to the data. + @param BufferLength The length of the data. + @param Direction The direction of the data. + @param TimeOutInMilliSeconds Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + + @retval EFI_DEVICE_ERROR Successful to get the status of device. + @retval EFI_SUCCESS Failed to get the status of device. + +**/ +EFI_STATUS +PeiAtapiCommand ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_BOT_DEVICE *PeiBotDev, + IN VOID *Command, + IN UINT8 CommandSize, + IN VOID *DataBuffer, + IN UINT32 BufferLength, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINT16 TimeOutInMilliSeconds + ); + +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbBotPei/UsbPeim.h b/MdeModulePkg/Bus/Usb/UsbBotPei/UsbPeim.h new file mode 100644 index 0000000000..4f3f57f0bc --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBotPei/UsbPeim.h @@ -0,0 +1,179 @@ +/** @file +Usb Peim definition. + +Copyright (c) 2006, 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 _PEI_USB_PEIM_H_ +#define _PEI_USB_PEIM_H_ + + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#define MAX_ROOT_PORT 2 +#define MAX_ENDPOINT 16 + +#define USB_SLOW_SPEED_DEVICE 0x01 +#define USB_FULL_SPEED_DEVICE 0x02 + +#define PEI_USB_DEVICE_SIGNATURE SIGNATURE_32 ('U', 's', 'b', 'D') +typedef struct { + UINTN Signature; + PEI_USB_IO_PPI UsbIoPpi; + EFI_PEI_PPI_DESCRIPTOR UsbIoPpiList; + UINT8 DeviceAddress; + UINT8 MaxPacketSize0; + UINT8 DeviceSpeed; + UINT8 DataToggle; + UINT8 IsHub; + UINT8 DownStreamPortNo; + UINT8 Reserved[2]; // Padding for IPF + UINTN AllocateAddress; + PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi; + UINT8 ConfigurationData[1024]; + EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc; + EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDesc; + EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDesc[MAX_ENDPOINT]; +} PEI_USB_DEVICE; + +#define PEI_USB_DEVICE_FROM_THIS(a) CR (a, PEI_USB_DEVICE, UsbIoPpi, PEI_USB_DEVICE_SIGNATURE) + + +/** + Submits control transfer to a target USB device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB_IO_PPI. + @param Request USB device request to send. + @param Direction Specifies the data direction for the data stage. + @param Timeout Indicates the maximum timeout, in millisecond. + @param Data Data buffer to be transmitted or received from USB device. + @param DataLength The size (in bytes) of the data buffer. + + @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 +PeiUsbControlTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINT32 Timeout, + IN OUT VOID *Data, OPTIONAL + IN UINTN DataLength OPTIONAL + ); + +/** + 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_IO_PPI. + @param DeviceEndpoint Endpoint number and its direction in bit 7. + @param Data A pointer to the buffer of data to transmit + from or receive into. + @param DataLength The lenght of the data buffer. + @param Timeout Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + + @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 +PeiUsbBulkTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout + ); + +/** + Get the usb interface descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param This Indicates the PEI_USB_IO_PPI instance. + @param InterfaceDescriptor Request interface descriptor. + + + @retval EFI_SUCCESS Usb interface descriptor is obtained successfully. + +**/ +EFI_STATUS +EFIAPI +PeiUsbGetInterfaceDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + OUT EFI_USB_INTERFACE_DESCRIPTOR **InterfaceDescriptor + ); + +/** + Get the usb endpoint descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param This Indicates the PEI_USB_IO_PPI instance. + @param EndpointIndex The valid index of the specified endpoint. + @param EndpointDescriptor Request endpoint descriptor. + + @retval EFI_SUCCESS Usb endpoint descriptor is obtained successfully. + @retval EFI_NOT_FOUND Usb endpoint descriptor is NOT found. + +**/ +EFI_STATUS +EFIAPI +PeiUsbGetEndpointDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN UINT8 EndpointIndex, + OUT EFI_USB_ENDPOINT_DESCRIPTOR **EndpointDescriptor + ); + +/** + Reset the port and re-configure the usb device. + + @param PeiServices General-purpose services that are available to every PEIM. + @param This Indicates the PEI_USB_IO_PPI instance. + + @retval EFI_SUCCESS Usb device is reset and configured successfully. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +EFIAPI +PeiUsbPortReset ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This + ); + +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c b/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c new file mode 100644 index 0000000000..5b7ebfad90 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c @@ -0,0 +1,537 @@ +/** @file +Usb Hub Request Support In PEI Phase + +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 "UsbPeim.h" +#include "HubPeim.h" +#include "PeiUsbLib.h" + +/** + Get a given hub port status. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Port Usb hub port number (starting from 1). + @param PortStatus Current Hub port status and change status. + + @retval EFI_SUCCESS Port status is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the port status due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubGetPortStatus ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 Port, + OUT UINT32 *PortStatus + ) +{ + EFI_USB_DEVICE_REQUEST DeviceRequest; + + ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST)); + + // + // Fill Device request packet + // + DeviceRequest.RequestType = USB_HUB_GET_PORT_STATUS_REQ_TYPE; + DeviceRequest.Request = USB_HUB_GET_PORT_STATUS; + DeviceRequest.Index = Port; + DeviceRequest.Length = (UINT16) sizeof (UINT32); + + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DeviceRequest, + EfiUsbDataIn, + PcdGet32 (PcdUsbTransferTimeoutValue), + PortStatus, + sizeof (UINT32) + ); + +} + +/** + Set specified feature to a given hub port. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Port Usb hub port number (starting from 1). + @param Value New feature value. + + @retval EFI_SUCCESS Port feature is set successfully. + @retval EFI_DEVICE_ERROR Cannot set the port feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubSetPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 Port, + IN UINT8 Value + ) +{ + EFI_USB_DEVICE_REQUEST DeviceRequest; + + ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST)); + + // + // Fill Device request packet + // + DeviceRequest.RequestType = USB_HUB_SET_PORT_FEATURE_REQ_TYPE; + DeviceRequest.Request = USB_HUB_SET_PORT_FEATURE; + DeviceRequest.Value = Value; + DeviceRequest.Index = Port; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DeviceRequest, + EfiUsbNoData, + PcdGet32 (PcdUsbTransferTimeoutValue), + NULL, + 0 + ); +} + +/** + Clear specified feature on a given hub port. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Port Usb hub port number (starting from 1). + @param Value Feature value that will be cleared from the hub port. + + @retval EFI_SUCCESS Port feature is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the port feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubClearPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 Port, + IN UINT8 Value + ) +{ + EFI_USB_DEVICE_REQUEST DeviceRequest; + + ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST)); + + // + // Fill Device request packet + // + DeviceRequest.RequestType = USB_HUB_CLEAR_FEATURE_PORT_REQ_TYPE; + DeviceRequest.Request = USB_HUB_CLEAR_FEATURE_PORT; + DeviceRequest.Value = Value; + DeviceRequest.Index = Port; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DeviceRequest, + EfiUsbNoData, + PcdGet32 (PcdUsbTransferTimeoutValue), + NULL, + 0 + ); +} + +/** + Get a given hub status. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param HubStatus Current Hub status and change status. + + @retval EFI_SUCCESS Hub status is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the hub status due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubGetHubStatus ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + OUT UINT32 *HubStatus + ) +{ + EFI_USB_DEVICE_REQUEST DeviceRequest; + + ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST)); + + // + // Fill Device request packet + // + DeviceRequest.RequestType = USB_HUB_GET_HUB_STATUS_REQ_TYPE; + DeviceRequest.Request = USB_HUB_GET_HUB_STATUS; + DeviceRequest.Length = (UINT16) sizeof (UINT32); + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DeviceRequest, + EfiUsbDataIn, + PcdGet32 (PcdUsbTransferTimeoutValue), + HubStatus, + sizeof (UINT32) + ); +} + +/** + Set specified feature to a given hub. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Value New feature value. + + @retval EFI_SUCCESS Port feature is set successfully. + @retval EFI_DEVICE_ERROR Cannot set the port feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubSetHubFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 Value + ) +{ + EFI_USB_DEVICE_REQUEST DeviceRequest; + + ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST)); + + // + // Fill Device request packet + // + DeviceRequest.RequestType = USB_HUB_SET_HUB_FEATURE_REQ_TYPE; + DeviceRequest.Request = USB_HUB_SET_HUB_FEATURE; + DeviceRequest.Value = Value; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DeviceRequest, + EfiUsbNoData, + PcdGet32 (PcdUsbTransferTimeoutValue), + NULL, + 0 + ); +} + +/** + Clear specified feature on a given hub. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Value Feature value that will be cleared from the hub port. + + @retval EFI_SUCCESS Hub feature is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the hub feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubClearHubFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 Value + ) +{ + EFI_USB_DEVICE_REQUEST DeviceRequest; + + ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST)); + + // + // Fill Device request packet + // + DeviceRequest.RequestType = USB_HUB_CLEAR_FEATURE_REQ_TYPE; + DeviceRequest.Request = USB_HUB_CLEAR_FEATURE; + DeviceRequest.Value = Value; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DeviceRequest, + EfiUsbNoData, + PcdGet32 (PcdUsbTransferTimeoutValue), + NULL, + 0 + ); +} + +/** + Get a given hub descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param DescriptorSize The length of Hub Descriptor buffer. + @param HubDescriptor Caller allocated buffer to store the hub descriptor if + successfully returned. + + @retval EFI_SUCCESS Hub descriptor is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the hub descriptor due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiGetHubDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINTN DescriptorSize, + OUT EFI_USB_HUB_DESCRIPTOR *HubDescriptor + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST)); + + // + // Fill Device request packet + // + DevReq.RequestType = USB_RT_HUB | 0x80; + DevReq.Request = USB_HUB_GET_DESCRIPTOR; + DevReq.Value = USB_DT_HUB << 8; + DevReq.Length = (UINT16)DescriptorSize; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbDataIn, + PcdGet32 (PcdUsbTransferTimeoutValue), + HubDescriptor, + (UINT16)DescriptorSize + ); +} + +/** + Configure a given hub. + + @param PeiServices General-purpose services that are available to every PEIM. + @param PeiUsbDevice Indicating the hub controller device that will be configured + + @retval EFI_SUCCESS Hub configuration is done successfully. + @retval EFI_DEVICE_ERROR Cannot configure the hub due to a hardware error. + +**/ +EFI_STATUS +PeiDoHubConfig ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_DEVICE *PeiUsbDevice + ) +{ + EFI_USB_HUB_DESCRIPTOR HubDescriptor; + EFI_STATUS Status; + EFI_USB_HUB_STATUS HubStatus; + UINTN Index; + UINT32 PortStatus; + PEI_USB_IO_PPI *UsbIoPpi; + + ZeroMem (&HubDescriptor, sizeof (HubDescriptor)); + UsbIoPpi = &PeiUsbDevice->UsbIoPpi; + + // + // First get the hub descriptor length + // + Status = PeiGetHubDescriptor ( + PeiServices, + UsbIoPpi, + 2, + &HubDescriptor + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // First get the whole descriptor, then + // get the number of hub ports + // + Status = PeiGetHubDescriptor ( + PeiServices, + UsbIoPpi, + HubDescriptor.Length, + &HubDescriptor + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + PeiUsbDevice->DownStreamPortNo = HubDescriptor.NbrPorts; + + Status = PeiHubGetHubStatus ( + PeiServices, + UsbIoPpi, + (UINT32 *) &HubStatus + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // Get all hub ports status + // + for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) { + + Status = PeiHubGetPortStatus ( + PeiServices, + UsbIoPpi, + (UINT8) (Index + 1), + &PortStatus + ); + if (EFI_ERROR (Status)) { + continue; + } + } + // + // Power all the hub ports + // + for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) { + Status = PeiHubSetPortFeature ( + PeiServices, + UsbIoPpi, + (UINT8) (Index + 1), + EfiUsbPortPower + ); + if (EFI_ERROR (Status)) { + continue; + } + } + // + // Clear Hub Status Change + // + Status = PeiHubGetHubStatus ( + PeiServices, + UsbIoPpi, + (UINT32 *) &HubStatus + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } else { + // + // Hub power supply change happens + // + if ((HubStatus.HubChangeStatus & HUB_CHANGE_LOCAL_POWER) != 0) { + PeiHubClearHubFeature ( + PeiServices, + UsbIoPpi, + C_HUB_LOCAL_POWER + ); + } + // + // Hub change overcurrent happens + // + if ((HubStatus.HubChangeStatus & HUB_CHANGE_OVERCURRENT) != 0) { + PeiHubClearHubFeature ( + PeiServices, + UsbIoPpi, + C_HUB_OVER_CURRENT + ); + } + } + + return EFI_SUCCESS; +} + +/** + Send reset signal over the given root hub port. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param PortNum Usb hub port number (starting from 1). + +**/ +VOID +PeiResetHubPort ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 PortNum + ) +{ + UINT8 Try; + EFI_USB_PORT_STATUS HubPortStatus; + + + MicroSecondDelay (100 * 1000); + + // + // reset root port + // + PeiHubSetPortFeature ( + PeiServices, + UsbIoPpi, + PortNum, + EfiUsbPortReset + ); + + Try = 10; + do { + PeiHubGetPortStatus ( + PeiServices, + UsbIoPpi, + PortNum, + (UINT32 *) &HubPortStatus + ); + + MicroSecondDelay (2 * 1000); + Try -= 1; + } while ((HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) == 0 && Try > 0); + + // + // clear reset root port + // + PeiHubClearPortFeature ( + PeiServices, + UsbIoPpi, + PortNum, + EfiUsbPortReset + ); + + MicroSecondDelay (1 * 1000); + + PeiHubClearPortFeature ( + PeiServices, + UsbIoPpi, + PortNum, + EfiUsbPortConnectChange + ); + + // + // Set port enable + // + PeiHubSetPortFeature ( + PeiServices, + UsbIoPpi, + PortNum, + EfiUsbPortEnable + ); + + // + // Clear any change status + // + + PeiHubClearPortFeature ( + PeiServices, + UsbIoPpi, + PortNum, + EfiUsbPortEnableChange + ); + + MicroSecondDelay (10 * 1000); + + return; +} diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.h b/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.h new file mode 100644 index 0000000000..273a26c1ae --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.h @@ -0,0 +1,279 @@ +/** @file +Constants definitions for Usb Hub Peim + +Copyright (c) 2006, 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 _PEI_HUB_PEIM_H_ +#define _PEI_HUB_PEIM_H_ + + +// +// Hub feature numbers +// +#define C_HUB_LOCAL_POWER 0 +#define C_HUB_OVER_CURRENT 1 + +// +// Hub class code & sub class code +// +#define CLASS_CODE_HUB 0x09 +#define SUB_CLASS_CODE_HUB 0 + +// +// Hub Status & Hub Change bit masks +// +#define HUB_STATUS_LOCAL_POWER 0x0001 +#define HUB_STATUS_OVERCURRENT 0x0002 + +#define HUB_CHANGE_LOCAL_POWER 0x0001 +#define HUB_CHANGE_OVERCURRENT 0x0002 + +// +// Hub Characteristics +// +#define HUB_CHAR_LPSM 0x0003 +#define HUB_CHAR_COMPOUND 0x0004 +#define HUB_CHAR_OCPM 0x0018 + +// +// Standard hub request and request type +// By [Spec-USB20/Chapter-11.24] +// +#define USB_HUB_CLEAR_FEATURE 0x01 +#define USB_HUB_CLEAR_FEATURE_REQ_TYPE 0x20 + +#define USB_HUB_CLEAR_FEATURE_PORT 0x01 +#define USB_HUB_CLEAR_FEATURE_PORT_REQ_TYPE 0x23 + +#define USB_HUB_GET_BUS_STATE 0x02 +#define USB_HUB_GET_BUS_STATE_REQ_TYPE 0xA3 + +#define USB_HUB_GET_DESCRIPTOR 0x06 +#define USB_HUB_GET_DESCRIPTOR_REQ_TYPE 0xA0 + +#define USB_HUB_GET_HUB_STATUS 0x00 +#define USB_HUB_GET_HUB_STATUS_REQ_TYPE 0xA0 + +#define USB_HUB_GET_PORT_STATUS 0x00 +#define USB_HUB_GET_PORT_STATUS_REQ_TYPE 0xA3 + +#define USB_HUB_SET_DESCRIPTOR 0x07 +#define USB_HUB_SET_DESCRIPTOR_REQ_TYPE 0x20 + +#define USB_HUB_SET_HUB_FEATURE 0x03 +#define USB_HUB_SET_HUB_FEATURE_REQ_TYPE 0x20 + +#define USB_HUB_SET_PORT_FEATURE 0x03 +#define USB_HUB_SET_PORT_FEATURE_REQ_TYPE 0x23 + +#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE) +#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER) + +#define MAXBYTES 8 +#pragma pack(1) +// +// Hub descriptor, the last two fields are of variable lenght. +// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT8 NbrPorts; + UINT8 HubCharacteristics[2]; + UINT8 PwrOn2PwrGood; + UINT8 HubContrCurrent; + UINT8 Filler[MAXBYTES]; +} EFI_USB_HUB_DESCRIPTOR; + +typedef struct { + UINT16 HubStatus; + UINT16 HubChangeStatus; +} EFI_USB_HUB_STATUS; + +#pragma pack() +/** + Get a given hub port status. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Port Usb hub port number (starting from 1). + @param PortStatus Current Hub port status and change status. + + @retval EFI_SUCCESS Port status is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the port status due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubGetPortStatus ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 Port, + OUT UINT32 *PortStatus + ); + +/** + Set specified feature to a given hub port. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Port Usb hub port number (starting from 1). + @param Value New feature value. + + @retval EFI_SUCCESS Port feature is set successfully. + @retval EFI_DEVICE_ERROR Cannot set the port feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubSetPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 Port, + IN UINT8 Value + ); + +/** + Set specified feature to a given hub. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Value New feature value. + + @retval EFI_SUCCESS Port feature is set successfully. + @retval EFI_DEVICE_ERROR Cannot set the port feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubSetHubFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 Value + ); + +/** + Get a given hub status. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param HubStatus Current Hub status and change status. + + @retval EFI_SUCCESS Hub status is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the hub status due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubGetHubStatus ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + OUT UINT32 *HubStatus + ); + +/** + Clear specified feature on a given hub port. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Port Usb hub port number (starting from 1). + @param Value Feature value that will be cleared from the hub port. + + @retval EFI_SUCCESS Port feature is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the port feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubClearPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 Port, + IN UINT8 Value + ); + +/** + Clear specified feature on a given hub. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Value Feature value that will be cleared from the hub port. + + @retval EFI_SUCCESS Hub feature is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the hub feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubClearHubFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 Value + ); + +/** + Get a given hub descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param DescriptorSize The length of Hub Descriptor buffer. + @param HubDescriptor Caller allocated buffer to store the hub descriptor if + successfully returned. + + @retval EFI_SUCCESS Hub descriptor is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the hub descriptor due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiGetHubDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINTN DescriptorSize, + OUT EFI_USB_HUB_DESCRIPTOR *HubDescriptor + ); + +/** + Configure a given hub. + + @param PeiServices General-purpose services that are available to every PEIM. + @param PeiUsbDevice Indicating the hub controller device that will be configured + + @retval EFI_SUCCESS Hub configuration is done successfully. + @retval EFI_DEVICE_ERROR Cannot configure the hub due to a hardware error. + +**/ +EFI_STATUS +PeiDoHubConfig ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_DEVICE *PeiUsbDevice + ); + +/** + Send reset signal over the given root hub port. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param PortNum Usb hub port number (starting from 1). + +**/ +VOID +PeiResetHubPort ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 PortNum + ); + +#endif + + diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.c b/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.c new file mode 100644 index 0000000000..2ac8d7bae3 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.c @@ -0,0 +1,333 @@ +/** @file +Common Libarary for PEI USB + +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 "UsbPeim.h" +#include "PeiUsbLib.h" + +/** + Get a given usb descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Value Request Value. + @param Index Request Index. + @param DescriptorLength Request descriptor Length. + @param Descriptor Request descriptor. + + + @retval EFI_SUCCESS Usb descriptor is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the usb descriptor due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbGetDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT16 Value, + IN UINT16 Index, + IN UINT16 DescriptorLength, + OUT VOID *Descriptor + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + + ASSERT (UsbIoPpi != NULL); + + DevReq.RequestType = USB_DEV_GET_DESCRIPTOR_REQ_TYPE; + DevReq.Request = USB_DEV_GET_DESCRIPTOR; + DevReq.Value = Value; + DevReq.Index = Index; + DevReq.Length = DescriptorLength; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbDataIn, + PcdGet32 (PcdUsbTransferTimeoutValue), + Descriptor, + DescriptorLength + ); +} + +/** + Set a usb device with a specified address. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param AddressValue The address to assign. + + @retval EFI_SUCCESS Usb device address is set successfully. + @retval EFI_DEVICE_ERROR Cannot set the usb address due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbSetDeviceAddress ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT16 AddressValue + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + + ASSERT (UsbIoPpi != NULL); + + DevReq.RequestType = USB_DEV_SET_ADDRESS_REQ_TYPE; + DevReq.Request = USB_DEV_SET_ADDRESS; + DevReq.Value = AddressValue; + DevReq.Index = 0; + DevReq.Length = 0; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbNoData, + PcdGet32 (PcdUsbTransferTimeoutValue), + NULL, + 0 + ); +} + +/** + Clear a given usb feature. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Recipient The recipient of ClearFeature Request, should be one of Device/Interface/Endpoint. + @param Value Request Value. + @param Target Request Index. + + @retval EFI_SUCCESS Usb feature is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the usb feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbClearDeviceFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN EFI_USB_RECIPIENT Recipient, + IN UINT16 Value, + IN UINT16 Target + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + + ASSERT (UsbIoPpi != NULL); + + switch (Recipient) { + case EfiUsbDevice: + DevReq.RequestType = USB_DEV_CLEAR_FEATURE_REQ_TYPE_D; + break; + + case EfiUsbInterface: + DevReq.RequestType = USB_DEV_CLEAR_FEATURE_REQ_TYPE_I; + break; + + case EfiUsbEndpoint: + DevReq.RequestType = USB_DEV_CLEAR_FEATURE_REQ_TYPE_E; + break; + } + + DevReq.Request = USB_DEV_CLEAR_FEATURE; + DevReq.Value = Value; + DevReq.Index = Target; + DevReq.Length = 0; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbNoData, + PcdGet32 (PcdUsbTransferTimeoutValue), + NULL, + 0 + ); +} + +/** + Configure a usb device to Configuration 1. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + + @retval EFI_SUCCESS Usb device is set to use Configuration 1 successfully. + @retval EFI_DEVICE_ERROR Cannot set the usb device due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbSetConfiguration ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST)); + + DevReq.RequestType = USB_DEV_SET_CONFIGURATION_REQ_TYPE; + DevReq.Request = USB_DEV_SET_CONFIGURATION; + DevReq.Value = 1; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbNoData, + PcdGet32 (PcdUsbTransferTimeoutValue), + NULL, + 0 + ); +} + +/** + Clear Endpoint Halt. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param EndpointAddress The endpoint address. + + @retval EFI_SUCCESS Endpoint halt is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the endpoint halt status due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbClearEndpointHalt ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 EndpointAddress + ) +{ + EFI_STATUS Status; + PEI_USB_DEVICE *PeiUsbDev; + EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDescriptor; + UINT8 EndpointIndex; + + EndpointIndex = 0; + PeiUsbDev = PEI_USB_DEVICE_FROM_THIS (UsbIoPpi); + + while (EndpointIndex < MAX_ENDPOINT) { + Status = UsbIoPpi->UsbGetEndpointDescriptor (PeiServices, UsbIoPpi, EndpointIndex, &EndpointDescriptor); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + if (EndpointDescriptor->EndpointAddress == EndpointAddress) { + break; + } + + EndpointIndex++; + } + + if (EndpointIndex == MAX_ENDPOINT) { + return EFI_INVALID_PARAMETER; + } + + Status = PeiUsbClearDeviceFeature ( + PeiServices, + UsbIoPpi, + EfiUsbEndpoint, + EfiUsbEndpointHalt, + EndpointAddress + ); + + // + // set data toggle to zero. + // + if ((PeiUsbDev->DataToggle & (1 << EndpointIndex)) != 0) { + PeiUsbDev->DataToggle = (UINT8) (PeiUsbDev->DataToggle ^ (1 << EndpointIndex)); + } + + return Status; +} + +/** + Judge if the port is connected with a usb device or not. + + @param PortStatus The usb port status gotten. + + @retval TRUE A usb device is connected with the port. + @retval FALSE No usb device is connected with the port. + +**/ +BOOLEAN +IsPortConnect ( + IN UINT16 PortStatus + ) +{ + // + // return the bit 0 value of PortStatus + // + if ((PortStatus & USB_PORT_STAT_CONNECTION) != 0) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Judge if the port is connected with a low-speed usb device or not. + + @param PortStatus The usb port status gotten. + + @retval TRUE A low-speed usb device is connected with the port. + @retval FALSE No low-speed usb device is connected with the port. + +**/ +UINTN +IsPortLowSpeedDeviceAttached ( + IN UINT16 PortStatus + ) +{ + // + // return the bit 9 value of PortStatus + // + if ((PortStatus & USB_PORT_STAT_LOW_SPEED) != 0) { + return EFI_USB_SPEED_LOW; + } else if ((PortStatus & USB_PORT_STAT_HIGH_SPEED) != 0){ + return EFI_USB_SPEED_HIGH; + } else { + return EFI_USB_SPEED_FULL; + } +} + +/** + Judge if the port is in "connection change" status or not. + + @param PortChangeStatus The usb port change status gotten. + + @retval TRUE The port is in "connection change" status. + @retval FALSE The port is NOT in "connection change" status. + +**/ +BOOLEAN +IsPortConnectChange ( + IN UINT16 PortChangeStatus + ) +{ + // + // return the bit 0 value of PortChangeStatus + // + if ((PortChangeStatus & USB_PORT_STAT_C_CONNECTION) != 0) { + return TRUE; + } else { + return FALSE; + } +} diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.h b/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.h new file mode 100644 index 0000000000..dd4ce1befc --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.h @@ -0,0 +1,250 @@ +/** @file +Common Libarary for PEI USB + +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 _PEI_USB_LIB_H_ +#define _PEI_USB_LIB_H_ + + +// +// Standard device request and request type +// By [Spec-USB20/Chapter-9.4] +// +#define USB_DEV_GET_STATUS 0x00 +#define USB_DEV_GET_STATUS_REQ_TYPE_D 0x80 // Receiver : Device +#define USB_DEV_GET_STATUS_REQ_TYPE_I 0x81 // Receiver : Interface +#define USB_DEV_GET_STATUS_REQ_TYPE_E 0x82 // Receiver : Endpoint + +#define USB_DEV_CLEAR_FEATURE 0x01 +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_D 0x00 // Receiver : Device +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_I 0x01 // Receiver : Interface +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_E 0x02 // Receiver : Endpoint + +#define USB_DEV_SET_FEATURE 0x03 +#define USB_DEV_SET_FEATURE_REQ_TYPE_D 0x00 // Receiver : Device +#define USB_DEV_SET_FEATURE_REQ_TYPE_I 0x01 // Receiver : Interface +#define USB_DEV_SET_FEATURE_REQ_TYPE_E 0x02 // Receiver : Endpoint + +#define USB_DEV_SET_ADDRESS 0x05 +#define USB_DEV_SET_ADDRESS_REQ_TYPE 0x00 + +#define USB_DEV_GET_DESCRIPTOR 0x06 +#define USB_DEV_GET_DESCRIPTOR_REQ_TYPE 0x80 + +#define USB_DEV_SET_DESCRIPTOR 0x07 +#define USB_DEV_SET_DESCRIPTOR_REQ_TYPE 0x00 + +#define USB_DEV_GET_CONFIGURATION 0x08 +#define USB_DEV_GET_CONFIGURATION_REQ_TYPE 0x80 + +#define USB_DEV_SET_CONFIGURATION 0x09 +#define USB_DEV_SET_CONFIGURATION_REQ_TYPE 0x00 + +#define USB_DEV_GET_INTERFACE 0x0A +#define USB_DEV_GET_INTERFACE_REQ_TYPE 0x81 + +#define USB_DEV_SET_INTERFACE 0x0B +#define USB_DEV_SET_INTERFACE_REQ_TYPE 0x01 + +#define USB_DEV_SYNCH_FRAME 0x0C +#define USB_DEV_SYNCH_FRAME_REQ_TYPE 0x82 + +// +// USB Descriptor types +// +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_HUB 0x29 +#define USB_DT_HID 0x21 + +// +// USB request type +// +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +// +// USB request targer device +// +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +typedef enum { + EfiUsbEndpointHalt, + EfiUsbDeviceRemoteWakeup +} EFI_USB_STANDARD_FEATURE_SELECTOR; + +// +// Usb Data recipient type +// +typedef enum { + EfiUsbDevice, + EfiUsbInterface, + EfiUsbEndpoint +} EFI_USB_RECIPIENT; + +/** + Get a given usb descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Value Request Value. + @param Index Request Index. + @param DescriptorLength Request descriptor Length. + @param Descriptor Request descriptor. + + + @retval EFI_SUCCESS Usb descriptor is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the usb descriptor due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbGetDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT16 Value, + IN UINT16 Index, + IN UINT16 DescriptorLength, + OUT VOID *Descriptor + ); + +/** + Set a usb device with a specified address. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param AddressValue The address to assign. + + @retval EFI_SUCCESS Usb device address is set successfully. + @retval EFI_DEVICE_ERROR Cannot set the usb address due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbSetDeviceAddress ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT16 AddressValue + ); + +/** + Clear a given usb feature. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param Recipient The recipient of ClearFeature Request, should be one of Device/Interface/Endpoint. + @param Value Request Value. + @param Target Request Index. + + @retval EFI_SUCCESS Usb feature is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the usb feature due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbClearDeviceFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN EFI_USB_RECIPIENT Recipient, + IN UINT16 Value, + IN UINT16 Target + ); + +/** + Configure a usb device to Configuration 1. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + + @retval EFI_SUCCESS Usb device is set to use Configuration 1 successfully. + @retval EFI_DEVICE_ERROR Cannot set the usb device due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbSetConfiguration ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi + ); + +/** + Clear Endpoint Halt. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param EndpointAddress The endpoint address. + + @retval EFI_SUCCESS Endpoint halt is cleared successfully. + @retval EFI_DEVICE_ERROR Cannot clear the endpoint halt status due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbClearEndpointHalt ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + IN UINT8 EndpointAddress + ); + +/** + Judge if the port is connected with a usb device or not. + + @param PortStatus The usb port status gotten. + + @retval TRUE A usb device is connected with the port. + @retval FALSE No usb device is connected with the port. + +**/ +BOOLEAN +IsPortConnect ( + IN UINT16 PortStatus + ); + +/** + Judge if the port is connected with a low-speed usb device or not. + + @param PortStatus The usb port status gotten. + + @retval TRUE A low-speed usb device is connected with the port. + @retval FALSE No low-speed usb device is connected with the port. + +**/ +UINTN +IsPortLowSpeedDeviceAttached ( + IN UINT16 PortStatus + ); + +/** + Judge if the port is in "connection change" status or not. + + @param PortChangeStatus The usb port change status gotten. + + @retval TRUE The port is in "connection change" status. + @retval FALSE The port is NOT in "connection change" status. + +**/ +BOOLEAN +IsPortConnectChange ( + IN UINT16 PortChangeStatus + ); +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/UsbBusPei.inf b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbBusPei.inf new file mode 100644 index 0000000000..e4a2cb449a --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbBusPei.inf @@ -0,0 +1,66 @@ +## @file +# Component description file for UsbPeim module. +# +# Usb Bus Peim driver 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 = UsbBusPei + FILE_GUID = 8401A045-6F70-4505-8471-7015B40355E3 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = PeimInitializeUsb + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + PeiUsbLib.c + HubPeim.c + UsbIoPeim.c + UsbPeim.c + UsbPeim.h + PeiUsbLib.h + HubPeim.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + TimerLib + BaseMemoryLib + PeiServicesLib + PeimEntryPoint + DebugLib + PcdLib + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUsbTransferTimeoutValue + +[Ppis] + gPeiUsbIoPpiGuid # PPI ALWAYS_PRODUCED + gPeiUsbHostControllerPpiGuid # PPI ALWAYS_CONSUMED + gPeiUsb2HostControllerPpiGuid # PPI ALWAYS_CONSUMED + + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid AND gPeiUsb2HostControllerPpiGuid OR gPeiUsbHostControllerPpiGuid + diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/UsbIoPeim.c b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbIoPeim.c new file mode 100644 index 0000000000..897b22896a --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbIoPeim.c @@ -0,0 +1,325 @@ +/** @file +The module is used to implement Usb Io PPI interfaces. + +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 "UsbPeim.h" +#include "PeiUsbLib.h" + +/** + Submits control transfer to a target USB device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB_IO_PPI. + @param Request USB device request to send. + @param Direction Specifies the data direction for the data stage. + @param Timeout Indicates the maximum timeout, in millisecond. + @param Data Data buffer to be transmitted or received from USB device. + @param DataLength The size (in bytes) of the data buffer. + + @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 +PeiUsbControlTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINT32 Timeout, + IN OUT VOID *Data, OPTIONAL + IN UINTN DataLength OPTIONAL + ) +{ + EFI_STATUS Status; + PEI_USB_DEVICE *PeiUsbDev; + UINT32 TransferResult; + + PeiUsbDev = PEI_USB_DEVICE_FROM_THIS (This); + + if (PeiUsbDev->Usb2HcPpi != NULL) { + Status = PeiUsbDev->Usb2HcPpi->ControlTransfer ( + PeiServices, + PeiUsbDev->Usb2HcPpi, + PeiUsbDev->DeviceAddress, + PeiUsbDev->DeviceSpeed, + PeiUsbDev->MaxPacketSize0, + Request, + Direction, + Data, + &DataLength, + Timeout, + &(PeiUsbDev->Translator), + &TransferResult + ); + } else { + Status = PeiUsbDev->UsbHcPpi->ControlTransfer ( + PeiServices, + PeiUsbDev->UsbHcPpi, + PeiUsbDev->DeviceAddress, + PeiUsbDev->DeviceSpeed, + PeiUsbDev->MaxPacketSize0, + Request, + Direction, + Data, + &DataLength, + Timeout, + &TransferResult + ); + } + 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_IO_PPI. + @param DeviceEndpoint Endpoint number and its direction in bit 7. + @param Data A pointer to the buffer of data to transmit + from or receive into. + @param DataLength The lenght of the data buffer. + @param Timeout Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + + @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 +PeiUsbBulkTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout + ) +{ + EFI_STATUS Status; + PEI_USB_DEVICE *PeiUsbDev; + UINT32 TransferResult; + UINTN MaxPacketLength; + UINT8 DataToggle; + UINT8 OldToggle; + EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDescriptor; + UINT8 EndpointIndex; + VOID *Data2[EFI_USB_MAX_BULK_BUFFER_NUM]; + + PeiUsbDev = PEI_USB_DEVICE_FROM_THIS (This); + + EndpointDescriptor = NULL; + EndpointIndex = 0; + Data2[0] = Data; + Data2[1] = NULL; + + while (EndpointIndex < MAX_ENDPOINT) { + Status = PeiUsbGetEndpointDescriptor (PeiServices, This, EndpointIndex, &EndpointDescriptor); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + if (EndpointDescriptor->EndpointAddress == DeviceEndpoint) { + break; + } + + EndpointIndex++; + } + + if (EndpointIndex == MAX_ENDPOINT) { + return EFI_INVALID_PARAMETER; + } + + MaxPacketLength = PeiUsbDev->EndpointDesc[EndpointIndex]->MaxPacketSize; + if ((PeiUsbDev->DataToggle & (1 << EndpointIndex)) != 0) { + DataToggle = 1; + } else { + DataToggle = 0; + } + + OldToggle = DataToggle; + + if (PeiUsbDev->Usb2HcPpi != NULL) { + Status = PeiUsbDev->Usb2HcPpi->BulkTransfer ( + PeiServices, + PeiUsbDev->Usb2HcPpi, + PeiUsbDev->DeviceAddress, + DeviceEndpoint, + PeiUsbDev->DeviceSpeed, + MaxPacketLength, + Data2, + DataLength, + &DataToggle, + Timeout, + &(PeiUsbDev->Translator), + &TransferResult + ); + } else { + Status = PeiUsbDev->UsbHcPpi->BulkTransfer ( + PeiServices, + PeiUsbDev->UsbHcPpi, + PeiUsbDev->DeviceAddress, + DeviceEndpoint, + (UINT8) MaxPacketLength, + Data, + DataLength, + &DataToggle, + Timeout, + &TransferResult + ); + } + + if (OldToggle != DataToggle) { + PeiUsbDev->DataToggle = (UINT8) (PeiUsbDev->DataToggle ^ (1 << EndpointIndex)); + } + + return Status; +} + +/** + Get the usb interface descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param This Indicates the PEI_USB_IO_PPI instance. + @param InterfaceDescriptor Request interface descriptor. + + + @retval EFI_SUCCESS Usb interface descriptor is obtained successfully. + +**/ +EFI_STATUS +EFIAPI +PeiUsbGetInterfaceDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + OUT EFI_USB_INTERFACE_DESCRIPTOR **InterfaceDescriptor + ) +{ + PEI_USB_DEVICE *PeiUsbDev; + PeiUsbDev = PEI_USB_DEVICE_FROM_THIS (This); + *InterfaceDescriptor = PeiUsbDev->InterfaceDesc; + return EFI_SUCCESS; +} + +/** + Get the usb endpoint descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param This Indicates the PEI_USB_IO_PPI instance. + @param EndpointIndex The valid index of the specified endpoint. + @param EndpointDescriptor Request endpoint descriptor. + + @retval EFI_SUCCESS Usb endpoint descriptor is obtained successfully. + @retval EFI_NOT_FOUND Usb endpoint descriptor is NOT found. + +**/ +EFI_STATUS +EFIAPI +PeiUsbGetEndpointDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN UINT8 EndpointIndex, + OUT EFI_USB_ENDPOINT_DESCRIPTOR **EndpointDescriptor + ) +{ + PEI_USB_DEVICE *PeiUsbDev; + + PeiUsbDev = PEI_USB_DEVICE_FROM_THIS (This); + + ASSERT (EndpointDescriptor != NULL); + + // + // The valid range of EndpointIndex is 0..15 + // If EndpointIndex is lesser than 15 but larger than the number of interfaces, + // a EFI_NOT_FOUND should be returned + // + ASSERT (EndpointIndex <= 15); + + if (EndpointIndex >= PeiUsbDev->InterfaceDesc->NumEndpoints) { + return EFI_NOT_FOUND; + } + + *EndpointDescriptor = PeiUsbDev->EndpointDesc[EndpointIndex]; + + return EFI_SUCCESS; +} + +/** + Reset the port and re-configure the usb device. + + @param PeiServices General-purpose services that are available to every PEIM. + @param This Indicates the PEI_USB_IO_PPI instance. + + @retval EFI_SUCCESS Usb device is reset and configured successfully. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +EFIAPI +PeiUsbPortReset ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This + ) +{ + PEI_USB_DEVICE *PeiUsbDev; + EFI_STATUS Status; + UINT8 Address; + + PeiUsbDev = PEI_USB_DEVICE_FROM_THIS (This); + + ResetRootPort ( + PeiServices, + PeiUsbDev->UsbHcPpi, + PeiUsbDev->Usb2HcPpi, + PeiUsbDev->DeviceAddress, + 0 + ); + + // + // Set address + // + Address = PeiUsbDev->DeviceAddress; + PeiUsbDev->DeviceAddress = 0; + + Status = PeiUsbSetDeviceAddress ( + PeiServices, + This, + Address + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + PeiUsbDev->DeviceAddress = Address; + + // + // Set default configuration + // + Status = PeiUsbSetConfiguration ( + PeiServices, + This + ); + + return Status; +} diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c new file mode 100644 index 0000000000..4a5d8e8bc4 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c @@ -0,0 +1,1041 @@ +/** @file +The module to produce Usb Bus PPI. + +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 "UsbPeim.h" +#include "HubPeim.h" +#include "PeiUsbLib.h" + +// +// UsbIo PPI interface function +// +PEI_USB_IO_PPI mUsbIoPpi = { + PeiUsbControlTransfer, + PeiUsbBulkTransfer, + PeiUsbGetInterfaceDescriptor, + PeiUsbGetEndpointDescriptor, + PeiUsbPortReset +}; + +EFI_PEI_PPI_DESCRIPTOR mUsbIoPpiList = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gPeiUsbIoPpiGuid, + NULL +}; + +/** + The enumeration routine to detect device change. + + @param PeiServices Describes the list of possible PEI Services. + @param UsbHcPpi The pointer of PEI_USB_HOST_CONTROLLER_PPI instance. + @param Usb2HcPpi The pointer of PEI_USB2_HOST_CONTROLLER_PPI instance. + + @retval EFI_SUCCESS The usb is enumerated successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbEnumeration ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi, + IN PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi + ); + +/** + Configure new detected usb device. + + @param PeiServices Describes the list of possible PEI Services. + @param PeiUsbDevice The pointer of PEI_USB_DEVICE instance. + @param Port The port to be configured. + @param DeviceAddress The device address to be configured. + + @retval EFI_SUCCESS The new detected usb device is configured successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiConfigureUsbDevice ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_DEVICE *PeiUsbDevice, + IN UINT8 Port, + IN OUT UINT8 *DeviceAddress + ); + +/** + Get all configurations from a detected usb device. + + @param PeiServices Describes the list of possible PEI Services. + @param PeiUsbDevice The pointer of PEI_USB_DEVICE instance. + + @retval EFI_SUCCESS The new detected usb device is configured successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbGetAllConfiguration ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_DEVICE *PeiUsbDevice + ); + +/** + Get the start position of next wanted descriptor. + + @param Buffer Buffer containing data to parse. + @param Length Buffer length. + @param DescType Descriptor type. + @param DescLength Descriptor length. + @param ParsedBytes Bytes has been parsed. + + @retval EFI_SUCCESS Get wanted descriptor successfully. + @retval EFI_DEVICE_ERROR Error occurred. + +**/ +EFI_STATUS +GetExpectedDescriptor ( + IN UINT8 *Buffer, + IN UINTN Length, + IN UINT8 DescType, + IN UINT8 DescLength, + OUT UINTN *ParsedBytes + ); + +/** + The entrypoint of the module, it will enumerate all HCs. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS Usb initialization is done successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + @retval EFI_UNSUPPORTED Can't find required PPI. + +**/ +EFI_STATUS +EFIAPI +PeimInitializeUsb ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + UINTN Index; + PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi; + PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi; + + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { + return EFI_SUCCESS; + } + + // + // gPeiUsbHostControllerPpiGuid and gPeiUsb2HostControllerPpiGuid should not + // be produced at the same time + // + Index = 0; + while (TRUE) { + // + // Get UsbHcPpi at first. + // + Status = PeiServicesLocatePpi ( + &gPeiUsbHostControllerPpiGuid, + Index, + NULL, + (VOID **) &UsbHcPpi + ); + if (EFI_ERROR (Status)) { + // + // No more host controller, break out + // + break; + } + PeiUsbEnumeration ((EFI_PEI_SERVICES **) PeiServices, UsbHcPpi, NULL); + Index++; + } + + if (Index == 0) { + // + // Then try to get Usb2HcPpi. + // + while (TRUE) { + Status = PeiServicesLocatePpi ( + &gPeiUsb2HostControllerPpiGuid, + Index, + NULL, + (VOID **) &Usb2HcPpi + ); + if (EFI_ERROR (Status)) { + // + // No more host controller, break out + // + break; + } + PeiUsbEnumeration ((EFI_PEI_SERVICES **) PeiServices, NULL, Usb2HcPpi); + Index++; + } + } + + if (Index == 0) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + The Hub Enumeration just scans the hub ports one time. It also + doesn't support hot-plug. + + @param PeiServices Describes the list of possible PEI Services. + @param PeiUsbDevice The pointer of PEI_USB_DEVICE instance. + @param CurrentAddress The DeviceAddress of usb device. + + @retval EFI_SUCCESS The usb hub is enumerated successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiHubEnumeration ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_DEVICE *PeiUsbDevice, + IN UINT8 *CurrentAddress + ) +{ + UINTN Index; + EFI_STATUS Status; + PEI_USB_IO_PPI *UsbIoPpi; + EFI_USB_PORT_STATUS PortStatus; + UINTN MemPages; + EFI_PHYSICAL_ADDRESS AllocateAddress; + PEI_USB_DEVICE *NewPeiUsbDevice; + + + UsbIoPpi = &PeiUsbDevice->UsbIoPpi; + + for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) { + + Status = PeiHubGetPortStatus ( + PeiServices, + UsbIoPpi, + (UINT8) (Index + 1), + (UINT32 *) &PortStatus + ); + + if (EFI_ERROR (Status)) { + continue; + } + + if (IsPortConnectChange (PortStatus.PortChangeStatus)) { + PeiHubClearPortFeature ( + PeiServices, + UsbIoPpi, + (UINT8) (Index + 1), + EfiUsbPortConnectChange + ); + + MicroSecondDelay (100 * 1000); + + if (IsPortConnect (PortStatus.PortStatus)) { + + PeiHubGetPortStatus ( + PeiServices, + UsbIoPpi, + (UINT8) (Index + 1), + (UINT32 *) &PortStatus + ); + + // + // Begin to deal with the new device + // + MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1; + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + MemPages, + &AllocateAddress + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + NewPeiUsbDevice = (PEI_USB_DEVICE *) ((UINTN) AllocateAddress); + ZeroMem (NewPeiUsbDevice, sizeof (PEI_USB_DEVICE)); + + NewPeiUsbDevice->Signature = PEI_USB_DEVICE_SIGNATURE; + NewPeiUsbDevice->DeviceAddress = 0; + NewPeiUsbDevice->MaxPacketSize0 = 8; + NewPeiUsbDevice->DataToggle = 0; + CopyMem ( + &(NewPeiUsbDevice->UsbIoPpi), + &mUsbIoPpi, + sizeof (PEI_USB_IO_PPI) + ); + CopyMem ( + &(NewPeiUsbDevice->UsbIoPpiList), + &mUsbIoPpiList, + sizeof (EFI_PEI_PPI_DESCRIPTOR) + ); + NewPeiUsbDevice->UsbIoPpiList.Ppi = &NewPeiUsbDevice->UsbIoPpi; + NewPeiUsbDevice->AllocateAddress = (UINTN) AllocateAddress; + NewPeiUsbDevice->UsbHcPpi = PeiUsbDevice->UsbHcPpi; + NewPeiUsbDevice->Usb2HcPpi = PeiUsbDevice->Usb2HcPpi; + NewPeiUsbDevice->IsHub = 0x0; + NewPeiUsbDevice->DownStreamPortNo = 0x0; + + PeiResetHubPort (PeiServices, UsbIoPpi, (UINT8)(Index + 1)); + + PeiHubGetPortStatus ( + PeiServices, + UsbIoPpi, + (UINT8) (Index + 1), + (UINT32 *) &PortStatus + ); + + NewPeiUsbDevice->DeviceSpeed = (UINT8)IsPortLowSpeedDeviceAttached (PortStatus.PortStatus); + + if(NewPeiUsbDevice->DeviceSpeed != EFI_USB_SPEED_HIGH) { + if (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_HIGH) { + NewPeiUsbDevice->Translator.TranslatorPortNumber = (UINT8)Index; + NewPeiUsbDevice->Translator.TranslatorHubAddress = *CurrentAddress; + } else { + CopyMem(&(NewPeiUsbDevice->Translator), &(PeiUsbDevice->Translator), sizeof(EFI_USB2_HC_TRANSACTION_TRANSLATOR)); + } + } + + // + // Configure that Usb Device + // + Status = PeiConfigureUsbDevice ( + PeiServices, + NewPeiUsbDevice, + (UINT8) (Index + 1), + CurrentAddress + ); + + if (EFI_ERROR (Status)) { + continue; + } + + Status = PeiServicesInstallPpi (&NewPeiUsbDevice->UsbIoPpiList); + + if (NewPeiUsbDevice->InterfaceDesc->InterfaceClass == 0x09) { + NewPeiUsbDevice->IsHub = 0x1; + + Status = PeiDoHubConfig (PeiServices, NewPeiUsbDevice); + if (EFI_ERROR (Status)) { + return Status; + } + + PeiHubEnumeration (PeiServices, NewPeiUsbDevice, CurrentAddress); + } + } + + } + } + + + return EFI_SUCCESS; +} + +/** + The enumeration routine to detect device change. + + @param PeiServices Describes the list of possible PEI Services. + @param UsbHcPpi The pointer of PEI_USB_HOST_CONTROLLER_PPI instance. + @param Usb2HcPpi The pointer of PEI_USB2_HOST_CONTROLLER_PPI instance. + + @retval EFI_SUCCESS The usb is enumerated successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbEnumeration ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi, + IN PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi + ) +{ + UINT8 NumOfRootPort; + EFI_STATUS Status; + UINT8 Index; + EFI_USB_PORT_STATUS PortStatus; + PEI_USB_DEVICE *PeiUsbDevice; + UINTN MemPages; + EFI_PHYSICAL_ADDRESS AllocateAddress; + UINT8 CurrentAddress; + + + CurrentAddress = 0; + if (Usb2HcPpi != NULL){ + Usb2HcPpi->GetRootHubPortNumber ( + PeiServices, + Usb2HcPpi, + (UINT8 *) &NumOfRootPort + ); + } else { + UsbHcPpi->GetRootHubPortNumber ( + PeiServices, + UsbHcPpi, + (UINT8 *) &NumOfRootPort + ); + } + + for (Index = 0; Index < NumOfRootPort; Index++) { + // + // First get root port status to detect changes happen + // + if (Usb2HcPpi != NULL) { + Usb2HcPpi->GetRootHubPortStatus ( + PeiServices, + Usb2HcPpi, + (UINT8) Index, + &PortStatus + ); + } else { + UsbHcPpi->GetRootHubPortStatus ( + PeiServices, + UsbHcPpi, + (UINT8) Index, + &PortStatus + ); + } + DEBUG ((EFI_D_INFO, "USB Status --- ConnectChange[%04x] Status[%04x]\n", PortStatus.PortChangeStatus, PortStatus.PortStatus)); + if (IsPortConnectChange (PortStatus.PortChangeStatus)) { + // + // Changes happen, first clear this change status + // + if (Usb2HcPpi != NULL) { + Usb2HcPpi->ClearRootHubPortFeature ( + PeiServices, + Usb2HcPpi, + (UINT8) Index, + EfiUsbPortConnectChange + ); + } else { + UsbHcPpi->ClearRootHubPortFeature ( + PeiServices, + UsbHcPpi, + (UINT8) Index, + EfiUsbPortConnectChange + ); + } + MicroSecondDelay (100 * 1000); + + if (IsPortConnect (PortStatus.PortStatus)) { + if (Usb2HcPpi != NULL) { + Usb2HcPpi->GetRootHubPortStatus ( + PeiServices, + Usb2HcPpi, + (UINT8) Index, + &PortStatus + ); + } else { + UsbHcPpi->GetRootHubPortStatus ( + PeiServices, + UsbHcPpi, + (UINT8) Index, + &PortStatus + ); + } + + // + // Connect change happen + // + MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1; + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + MemPages, + &AllocateAddress + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + PeiUsbDevice = (PEI_USB_DEVICE *) ((UINTN) AllocateAddress); + ZeroMem (PeiUsbDevice, sizeof (PEI_USB_DEVICE)); + + PeiUsbDevice->Signature = PEI_USB_DEVICE_SIGNATURE; + PeiUsbDevice->DeviceAddress = 0; + PeiUsbDevice->MaxPacketSize0 = 8; + PeiUsbDevice->DataToggle = 0; + CopyMem ( + &(PeiUsbDevice->UsbIoPpi), + &mUsbIoPpi, + sizeof (PEI_USB_IO_PPI) + ); + CopyMem ( + &(PeiUsbDevice->UsbIoPpiList), + &mUsbIoPpiList, + sizeof (EFI_PEI_PPI_DESCRIPTOR) + ); + PeiUsbDevice->UsbIoPpiList.Ppi = &PeiUsbDevice->UsbIoPpi; + PeiUsbDevice->AllocateAddress = (UINTN) AllocateAddress; + PeiUsbDevice->UsbHcPpi = UsbHcPpi; + PeiUsbDevice->Usb2HcPpi = Usb2HcPpi; + PeiUsbDevice->IsHub = 0x0; + PeiUsbDevice->DownStreamPortNo = 0x0; + + ResetRootPort ( + PeiServices, + PeiUsbDevice->UsbHcPpi, + PeiUsbDevice->Usb2HcPpi, + Index, + 0 + ); + + if (Usb2HcPpi != NULL) { + Usb2HcPpi->GetRootHubPortStatus ( + PeiServices, + Usb2HcPpi, + (UINT8) Index, + &PortStatus + ); + } else { + UsbHcPpi->GetRootHubPortStatus ( + PeiServices, + UsbHcPpi, + (UINT8) Index, + &PortStatus + ); + } + + PeiUsbDevice->DeviceSpeed = (UINT8)IsPortLowSpeedDeviceAttached (PortStatus.PortStatus); + DEBUG ((EFI_D_INFO, "Device Speed =%d\n", PeiUsbDevice->DeviceSpeed)); + + // + // Configure that Usb Device + // + Status = PeiConfigureUsbDevice ( + PeiServices, + PeiUsbDevice, + Index, + &CurrentAddress + ); + + if (EFI_ERROR (Status)) { + continue; + } + DEBUG ((EFI_D_INFO, "PeiConfigureUsbDevice Success\n")); + + Status = PeiServicesInstallPpi (&PeiUsbDevice->UsbIoPpiList); + + if (PeiUsbDevice->InterfaceDesc->InterfaceClass == 0x09) { + PeiUsbDevice->IsHub = 0x1; + + Status = PeiDoHubConfig (PeiServices, PeiUsbDevice); + if (EFI_ERROR (Status)) { + return Status; + } + + PeiHubEnumeration (PeiServices, PeiUsbDevice, &CurrentAddress); + } + } else { + // + // Disconnect change happen, currently we don't support + // + } + } + } + + return EFI_SUCCESS; +} + +/** + Configure new detected usb device. + + @param PeiServices Describes the list of possible PEI Services. + @param PeiUsbDevice The pointer of PEI_USB_DEVICE instance. + @param Port The port to be configured. + @param DeviceAddress The device address to be configured. + + @retval EFI_SUCCESS The new detected usb device is configured successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiConfigureUsbDevice ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_DEVICE *PeiUsbDevice, + IN UINT8 Port, + IN OUT UINT8 *DeviceAddress + ) +{ + EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor; + EFI_STATUS Status; + PEI_USB_IO_PPI *UsbIoPpi; + UINT8 Retry; + + UsbIoPpi = &PeiUsbDevice->UsbIoPpi; + Status = EFI_SUCCESS; + ZeroMem (&DeviceDescriptor, sizeof (EFI_USB_DEVICE_DESCRIPTOR)); + // + // Get USB device descriptor + // + + for (Retry = 0; Retry < 3; Retry ++) { + + PeiUsbDevice->MaxPacketSize0 = 8; + + Status = PeiUsbGetDescriptor ( + PeiServices, + UsbIoPpi, + (USB_DT_DEVICE << 8), + 0, + 8, + &DeviceDescriptor + ); + + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "PeiUsbGet Device Descriptor the %d time Sucess\n", Retry)); + break; + } + } + + if (Retry == 3) { + DEBUG ((EFI_D_ERROR, "PeiUsbGet Device Descriptor fail\n", Retry)); + return Status; + } + + PeiUsbDevice->MaxPacketSize0 = DeviceDescriptor.MaxPacketSize0; + + (*DeviceAddress) ++; + + Status = PeiUsbSetDeviceAddress ( + PeiServices, + UsbIoPpi, + *DeviceAddress + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "PeiUsbSetDeviceAddress Failed\n")); + return Status; + } + + PeiUsbDevice->DeviceAddress = *DeviceAddress; + + // + // Get whole USB device descriptor + // + Status = PeiUsbGetDescriptor ( + PeiServices, + UsbIoPpi, + (USB_DT_DEVICE << 8), + 0, + (UINT16) sizeof (EFI_USB_DEVICE_DESCRIPTOR), + &DeviceDescriptor + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "PeiUsbGetDescriptor First Failed\n")); + return Status; + } + // + // Get its default configuration and its first interface + // + Status = PeiUsbGetAllConfiguration ( + PeiServices, + PeiUsbDevice + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PeiUsbSetConfiguration ( + PeiServices, + UsbIoPpi + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Get all configurations from a detected usb device. + + @param PeiServices Describes the list of possible PEI Services. + @param PeiUsbDevice The pointer of PEI_USB_DEVICE instance. + + @retval EFI_SUCCESS The new detected usb device is configured successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbGetAllConfiguration ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_DEVICE *PeiUsbDevice + ) +{ + EFI_STATUS Status; + EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc; + PEI_USB_IO_PPI *UsbIoPpi; + UINT16 ConfigDescLength; + UINT8 *Ptr; + UINTN SkipBytes; + UINTN LengthLeft; + UINTN Index; + UINTN NumOfEndpoint; + + UsbIoPpi = &PeiUsbDevice->UsbIoPpi; + + // + // First get its 4-byte configuration descriptor + // + Status = PeiUsbGetDescriptor ( + PeiServices, + UsbIoPpi, + (USB_DT_CONFIG << 8), // Value + 0, // Index + 4, // Length + PeiUsbDevice->ConfigurationData + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "PeiUsbGet Config Descriptor First Failed\n")); + return Status; + } + + ConfigDesc = (EFI_USB_CONFIG_DESCRIPTOR *) PeiUsbDevice->ConfigurationData; + ConfigDescLength = ConfigDesc->TotalLength; + + // + // Then we get the total descriptors for this configuration + // + Status = PeiUsbGetDescriptor ( + PeiServices, + UsbIoPpi, + (USB_DT_CONFIG << 8), + 0, + ConfigDescLength, + PeiUsbDevice->ConfigurationData + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "PeiUsbGet Config Descriptor all Failed\n")); + return Status; + } + // + // Parse this configuration descriptor + // First get the current config descriptor; + // + Status = GetExpectedDescriptor ( + PeiUsbDevice->ConfigurationData, + ConfigDescLength, + USB_DT_CONFIG, + (UINT8) sizeof (EFI_USB_CONFIG_DESCRIPTOR), + &SkipBytes + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Ptr = PeiUsbDevice->ConfigurationData + SkipBytes; + PeiUsbDevice->ConfigDesc = (EFI_USB_CONFIG_DESCRIPTOR *) Ptr; + + Ptr += sizeof (EFI_USB_CONFIG_DESCRIPTOR); + LengthLeft = ConfigDescLength - SkipBytes - sizeof (EFI_USB_CONFIG_DESCRIPTOR); + + // + // Get the first interface descriptor + // + Status = GetExpectedDescriptor ( + Ptr, + LengthLeft, + USB_DT_INTERFACE, + (UINT8) sizeof (EFI_USB_INTERFACE_DESCRIPTOR), + &SkipBytes + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Ptr += SkipBytes; + PeiUsbDevice->InterfaceDesc = (EFI_USB_INTERFACE_DESCRIPTOR *) Ptr; + + Ptr += sizeof (EFI_USB_INTERFACE_DESCRIPTOR); + LengthLeft -= SkipBytes; + LengthLeft -= sizeof (EFI_USB_INTERFACE_DESCRIPTOR); + + // + // Parse all the endpoint descriptor within this interface + // + NumOfEndpoint = PeiUsbDevice->InterfaceDesc->NumEndpoints; + ASSERT (NumOfEndpoint <= MAX_ENDPOINT); + + for (Index = 0; Index < NumOfEndpoint; Index++) { + // + // Get the endpoint descriptor + // + Status = GetExpectedDescriptor ( + Ptr, + LengthLeft, + USB_DT_ENDPOINT, + (UINT8) sizeof (EFI_USB_ENDPOINT_DESCRIPTOR), + &SkipBytes + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Ptr += SkipBytes; + PeiUsbDevice->EndpointDesc[Index] = (EFI_USB_ENDPOINT_DESCRIPTOR *) Ptr; + + Ptr += sizeof (EFI_USB_ENDPOINT_DESCRIPTOR); + LengthLeft -= SkipBytes; + LengthLeft -= sizeof (EFI_USB_ENDPOINT_DESCRIPTOR); + } + + return EFI_SUCCESS; +} + +/** + Get the start position of next wanted descriptor. + + @param Buffer Buffer containing data to parse. + @param Length Buffer length. + @param DescType Descriptor type. + @param DescLength Descriptor length. + @param ParsedBytes Bytes has been parsed. + + @retval EFI_SUCCESS Get wanted descriptor successfully. + @retval EFI_DEVICE_ERROR Error occurred. + +**/ +EFI_STATUS +GetExpectedDescriptor ( + IN UINT8 *Buffer, + IN UINTN Length, + IN UINT8 DescType, + IN UINT8 DescLength, + OUT UINTN *ParsedBytes + ) +{ + UINT16 DescriptorHeader; + UINT8 Len; + UINT8 *Ptr; + UINTN Parsed; + + Parsed = 0; + Ptr = Buffer; + + while (TRUE) { + // + // Buffer length should not less than Desc length + // + if (Length < DescLength) { + return EFI_DEVICE_ERROR; + } + + DescriptorHeader = (UINT16) (*Ptr + ((*(Ptr + 1)) << 8)); + + Len = Buffer[0]; + + // + // Check to see if it is a start of expected descriptor + // + if (DescriptorHeader == ((DescType << 8) | DescLength)) { + break; + } + + if ((UINT8) (DescriptorHeader >> 8) == DescType) { + if (Len > DescLength) { + return EFI_DEVICE_ERROR; + } + } + // + // Descriptor length should be at least 2 + // and should not exceed the buffer length + // + if (Len < 2) { + return EFI_DEVICE_ERROR; + } + + if (Len > Length) { + return EFI_DEVICE_ERROR; + } + // + // Skip this mismatch descriptor + // + Length -= Len; + Ptr += Len; + Parsed += Len; + } + + *ParsedBytes = Parsed; + + return EFI_SUCCESS; +} + +/** + Send reset signal over the given root hub port. + + @param PeiServices Describes the list of possible PEI Services. + @param UsbHcPpi The pointer of PEI_USB_HOST_CONTROLLER_PPI instance. + @param Usb2HcPpi The pointer of PEI_USB2_HOST_CONTROLLER_PPI instance. + @param PortNum The port to be reset. + @param RetryIndex The retry times. + +**/ +VOID +ResetRootPort ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi, + IN PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi, + IN UINT8 PortNum, + IN UINT8 RetryIndex + ) +{ + EFI_STATUS Status; + + + if (Usb2HcPpi != NULL) { + MicroSecondDelay (200 * 1000); + + // + // reset root port + // + Status = Usb2HcPpi->SetRootHubPortFeature ( + PeiServices, + Usb2HcPpi, + PortNum, + EfiUsbPortReset + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SetRootHubPortFeature EfiUsbPortReset Failed\n")); + return; + } + + MicroSecondDelay (200 * 1000); + + // + // clear reset root port + // + Status = Usb2HcPpi->ClearRootHubPortFeature ( + PeiServices, + Usb2HcPpi, + PortNum, + EfiUsbPortReset + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "ClearRootHubPortFeature EfiUsbPortReset Failed\n")); + return; + } + + MicroSecondDelay (1 * 1000); + + Usb2HcPpi->ClearRootHubPortFeature ( + PeiServices, + Usb2HcPpi, + PortNum, + EfiUsbPortConnectChange + ); + + // + // Set port enable + // + Usb2HcPpi->SetRootHubPortFeature( + PeiServices, + Usb2HcPpi, + PortNum, + EfiUsbPortEnable + ); + + Usb2HcPpi->ClearRootHubPortFeature ( + PeiServices, + Usb2HcPpi, + PortNum, + EfiUsbPortEnableChange + ); + + MicroSecondDelay ((RetryIndex + 1) * 50 * 1000); + } else { + MicroSecondDelay (200 * 1000); + + // + // reset root port + // + Status = UsbHcPpi->SetRootHubPortFeature ( + PeiServices, + UsbHcPpi, + PortNum, + EfiUsbPortReset + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SetRootHubPortFeature EfiUsbPortReset Failed\n")); + return; + } + + MicroSecondDelay (200 * 1000); + + // + // clear reset root port + // + Status = UsbHcPpi->ClearRootHubPortFeature ( + PeiServices, + UsbHcPpi, + PortNum, + EfiUsbPortReset + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "ClearRootHubPortFeature EfiUsbPortReset Failed\n")); + return; + } + + MicroSecondDelay (1 * 1000); + + UsbHcPpi->ClearRootHubPortFeature ( + PeiServices, + UsbHcPpi, + PortNum, + EfiUsbPortConnectChange + ); + + // + // Set port enable + // + UsbHcPpi->SetRootHubPortFeature( + PeiServices, + UsbHcPpi, + PortNum, + EfiUsbPortEnable + ); + + UsbHcPpi->ClearRootHubPortFeature ( + PeiServices, + UsbHcPpi, + PortNum, + EfiUsbPortEnableChange + ); + + MicroSecondDelay ((RetryIndex + 1) * 50 * 1000); + } + return; +} + + diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.h b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.h new file mode 100644 index 0000000000..c9e75bad53 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.h @@ -0,0 +1,203 @@ +/** @file +Usb Peim definition. + +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 _PEI_USB_PEIM_H_ +#define _PEI_USB_PEIM_H_ + + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_ROOT_PORT 2 +#define MAX_ENDPOINT 16 + +#define USB_SLOW_SPEED_DEVICE 0x01 +#define USB_FULL_SPEED_DEVICE 0x02 + +#define PEI_USB_DEVICE_SIGNATURE SIGNATURE_32 ('U', 's', 'b', 'D') +typedef struct { + UINTN Signature; + PEI_USB_IO_PPI UsbIoPpi; + EFI_PEI_PPI_DESCRIPTOR UsbIoPpiList; + UINT8 DeviceAddress; + UINT8 MaxPacketSize0; + UINT8 DeviceSpeed; + UINT8 DataToggle; + UINT8 IsHub; + UINT8 DownStreamPortNo; + UINT8 Reserved[2]; // Padding for IPF + UINTN AllocateAddress; + PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi; + PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi; + UINT8 ConfigurationData[1024]; + EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc; + EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDesc; + EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDesc[MAX_ENDPOINT]; + EFI_USB2_HC_TRANSACTION_TRANSLATOR Translator; +} PEI_USB_DEVICE; + +#define PEI_USB_DEVICE_FROM_THIS(a) CR (a, PEI_USB_DEVICE, UsbIoPpi, PEI_USB_DEVICE_SIGNATURE) + + +/** + Submits control transfer to a target USB device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB_IO_PPI. + @param Request USB device request to send. + @param Direction Specifies the data direction for the data stage. + @param Timeout Indicates the maximum timeout, in millisecond. + @param Data Data buffer to be transmitted or received from USB device. + @param DataLength The size (in bytes) of the data buffer. + + @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 +PeiUsbControlTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINT32 Timeout, + IN OUT VOID *Data, OPTIONAL + IN UINTN DataLength OPTIONAL + ); + +/** + 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_IO_PPI. + @param DeviceEndpoint Endpoint number and its direction in bit 7. + @param Data A pointer to the buffer of data to transmit + from or receive into. + @param DataLength The lenght of the data buffer. + @param Timeout Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + + @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 +PeiUsbBulkTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout + ); + +/** + Get the usb interface descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param This Indicates the PEI_USB_IO_PPI instance. + @param InterfaceDescriptor Request interface descriptor. + + + @retval EFI_SUCCESS Usb interface descriptor is obtained successfully. + +**/ +EFI_STATUS +EFIAPI +PeiUsbGetInterfaceDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + OUT EFI_USB_INTERFACE_DESCRIPTOR **InterfaceDescriptor + ); + +/** + Get the usb endpoint descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param This Indicates the PEI_USB_IO_PPI instance. + @param EndpointIndex The valid index of the specified endpoint. + @param EndpointDescriptor Request endpoint descriptor. + + @retval EFI_SUCCESS Usb endpoint descriptor is obtained successfully. + @retval EFI_NOT_FOUND Usb endpoint descriptor is NOT found. + +**/ +EFI_STATUS +EFIAPI +PeiUsbGetEndpointDescriptor ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN UINT8 EndpointIndex, + OUT EFI_USB_ENDPOINT_DESCRIPTOR **EndpointDescriptor + ); + +/** + Reset the port and re-configure the usb device. + + @param PeiServices General-purpose services that are available to every PEIM. + @param This Indicates the PEI_USB_IO_PPI instance. + + @retval EFI_SUCCESS Usb device is reset and configured successfully. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +EFIAPI +PeiUsbPortReset ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This + ); + +/** + Send reset signal over the given root hub port. + + @param PeiServices Describes the list of possible PEI Services. + @param UsbHcPpi The pointer of PEI_USB_HOST_CONTROLLER_PPI instance. + @param Usb2HcPpi The pointer of PEI_USB2_HOST_CONTROLLER_PPI instance. + @param PortNum The port to be reset. + @param RetryIndex The retry times. + +**/ +VOID +ResetRootPort ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi, + IN PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi, + IN UINT8 PortNum, + IN UINT8 RetryIndex + ); + +#endif diff --git a/MdeModulePkg/Include/Ppi/Usb2HostController.h b/MdeModulePkg/Include/Ppi/Usb2HostController.h new file mode 100644 index 0000000000..e6d00ee92e --- /dev/null +++ b/MdeModulePkg/Include/Ppi/Usb2HostController.h @@ -0,0 +1,263 @@ +/** @file + Defines the USB Host Controller PPI that provides I/O services for a USB Host + Controller that may be used to access recovery devices. These interfaces are + modeled on the UEFI 2.3 specification EFI_USB2_HOST_CONTROLLER_PROTOCOL. + Refer to section 16.1 of the UEFI 2.3 Specification for more information on + these interfaces. + +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 _PEI_USB2_HOST_CONTROLLER_PPI_H_ +#define _PEI_USB2_HOST_CONTROLLER_PPI_H_ + +#include + +/// +/// Global ID for the PEI_USB2_HOST_CONTROLLER_PPI. +/// +#define PEI_USB2_HOST_CONTROLLER_PPI_GUID \ + { \ + 0xa7d09fe1, 0x74d4, 0x4ba5, { 0x84, 0x7c, 0x12, 0xed, 0x5b, 0x19, 0xad, 0xe4 } \ + } + +/// +/// Forward declaration for the PEI_USB2_HOST_CONTROLLER_PPI. +/// +typedef struct _PEI_USB2_HOST_CONTROLLER_PPI PEI_USB2_HOST_CONTROLLER_PPI; + +/** + Initiate a USB control transfer using a specific USB Host controller on the USB bus. + + @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[in] DeviceAddress Represents the address of the target device + on the USB. + @param[in] DeviceSpeed Indicates device speed. + @param[in] MaximumPacketLength Indicates the maximum packet size that the + default control transfer + endpoint is capable of sending or receiving. + @param[in] Request A pointer to the USB device request that + will be sent to the USB device. + @param[in] TransferDirection Specifies the data direction for the transfer. + There are three values available: + EfiUsbDataIn, EfiUsbDataOut and EfiUsbNoData. + @param[in,out] Data A pointer to the buffer of data that will + be transmitted to USB device or + received from USB device. + @param[in,out] DataLength On input, indicates the size, in bytes, of + the data buffer specified by Data. + On output, indicates the amount of data + actually transferred. + @param[in] TimeOut Indicates the maximum time, in milliseconds, + that the transfer is allowed to complete. + @param[in] Translator A pointer to the transaction translator data. + @param[out] TransferResult A pointer to the detailed result information + generated by this control transfer. + + @retval EFI_SUCCESS The control transfer was completed successfully. + @retval EFI_DEVICE_ERROR The control transfer failed due to host controller + or device error. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The control transfer could not be completed due to a lack of resources. + @retval EFI_TIMEOUT The control transfer failed due to timeout. + + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB2_HOST_CONTROLLER_CONTROL_TRANSFER)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 DeviceAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data OPTIONAL, + IN OUT UINTN *DataLength OPTIONAL, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ); + +/** + Initiate a USB bulk transfer using a specific USB Host controller on the USB bus. + + @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[in] DeviceAddress Represents the address of the target device + on the USB. + @param[in] EndPointAddress The combination of an endpoint number and + an endpoint direction of the target USB device. + @param[in] DeviceSpeed Indicates device speed. + @param[in] MaximumPacketLength Indicates the maximum packet size the target + endpoint is capable of sending or receiving. + @param[in,out] Data Array of pointers to the buffers of data + that will be transmitted to USB device or + received from USB device. + @param[in,out] DataLength When input, indicates the size, in bytes, of + the data buffers specified by Data. When output, + indicates the data size actually transferred. + @param[in,out] DataToggle A pointer to the data toggle value. + @param[in] TimeOut Indicates the maximum time, in milliseconds, + in which the transfer is allowed to complete. + @param[in] Translator A pointer to the transaction translator data. + @param[out] TransferResult A pointer to the detailed result information + of the bulk transfer. + + @retval EFI_SUCCESS The bulk transfer was completed successfully. + @retval EFI_DEVICE_ERROR The bulk transfer failed due to host controller or device error. + Caller should check TransferResult for detailed error information. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The bulk transfer could not be submitted due to a lack of resources. + @retval EFI_TIMEOUT The bulk transfer failed due to timeout. + + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB2_HOST_CONTROLLER_BULK_TRANSFER)( + 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 + ); + +/** + 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. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB2_HOST_CONTROLLER_GET_ROOTHUB_PORT_NUMBER)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + OUT UINT8 *PortNumber + ); + +/** + Retrieves the current status of a USB root hub port. + + @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[in] PortNumber Specifies the root hub port from which the status is + to be retrieved. + This value is zero based. + @param[out] PortStatus A pointer to the current port status bits and port + status change bits. + + @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. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB2_HOST_CONTROLLER_GET_ROOTHUB_PORT_STATUS)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus + ); + +/** + Sets a feature for the specified root hub port. + + @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[in] PortNumber Specifies the root hub port whose feature is requested + to be set. This value is zero based. + @param[in] PortFeature Indicates the feature selector associated with the feature + set request. + + @retval EFI_SUCCESS The feature specified by PortFeature was set for + the USB root hub port specified by PortNumber. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid + for this function. + @retval EFI_TIMEOUT The time out occurred + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB2_HOST_CONTROLLER_SET_ROOTHUB_PORT_FEATURE)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ); + +/** + Clears a feature for the specified root hub port. + + @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[in] PortNumber Specifies the root hub port whose feature is + requested to be cleared. + @param[in] PortFeature Indicates the feature selector associated with the + feature clear request. + + @return EFI_SUCCESS The feature specified by PortFeature was cleared + for the USB root hub port specified by PortNumber. + @return EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB2_HOST_CONTROLLER_CLEAR_ROOTHUB_PORT_FEATURE)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ); + +/// +/// This PPI contains a set of services to interact with the USB host controller. +/// These interfaces are modeled on the UEFI 2.3 specification protocol +/// EFI_USB2_HOST_CONTROLLER_PROTOCOL. Refer to section 16.1 of the UEFI 2.3 +/// Specification for more information on these interfaces. +/// +struct _PEI_USB2_HOST_CONTROLLER_PPI { + PEI_USB2_HOST_CONTROLLER_CONTROL_TRANSFER ControlTransfer; + PEI_USB2_HOST_CONTROLLER_BULK_TRANSFER BulkTransfer; + PEI_USB2_HOST_CONTROLLER_GET_ROOTHUB_PORT_NUMBER GetRootHubPortNumber; + PEI_USB2_HOST_CONTROLLER_GET_ROOTHUB_PORT_STATUS GetRootHubPortStatus; + PEI_USB2_HOST_CONTROLLER_SET_ROOTHUB_PORT_FEATURE SetRootHubPortFeature; + PEI_USB2_HOST_CONTROLLER_CLEAR_ROOTHUB_PORT_FEATURE ClearRootHubPortFeature; +}; + +extern EFI_GUID gPeiUsb2HostControllerPpiGuid; + +#endif + diff --git a/MdeModulePkg/Include/Ppi/UsbController.h b/MdeModulePkg/Include/Ppi/UsbController.h new file mode 100644 index 0000000000..10e025df55 --- /dev/null +++ b/MdeModulePkg/Include/Ppi/UsbController.h @@ -0,0 +1,88 @@ +/** @file + Define APIs to retrieve USB Host Controller Info such as controller type and + I/O Port Base Address. + +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 _PEI_USB_CONTROLLER_PPI_H_ +#define _PEI_USB_CONTROLLER_PPI_H_ + +/// +/// Global ID for the PEI_USB_CONTROLLER_PPI. +/// +#define PEI_USB_CONTROLLER_PPI_GUID \ + { \ + 0x3bc1f6de, 0x693e, 0x4547,{ 0xa3, 0x0, 0x21, 0x82, 0x3c, 0xa4, 0x20, 0xb2} \ + } + +/// +/// Forward declaration for the PEI_USB_CONTROLLER_PPI. +/// +typedef struct _PEI_USB_CONTROLLER_PPI PEI_USB_CONTROLLER_PPI; + +/// +/// This bit is used in the ControllerType return parameter of GetUsbController() +/// to identify the USB Host Controller type as UHCI +/// +#define PEI_UHCI_CONTROLLER 0x01 + +/// +/// This bit is used in the ControllerType return parameter of GetUsbController() +/// to identify the USB Host Controller type as OHCI +/// +#define PEI_OHCI_CONTROLLER 0x02 + +/// +/// This bit is used in the ControllerType return parameter of GetUsbController() +/// to identify the USB Host Controller type as EHCI +/// +#define PEI_EHCI_CONTROLLER 0x03 + +/** + Retrieve USB Host Controller Info such as controller type and I/O Base Address. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the PEI_USB_CONTROLLER_PPI. + @param[in] ControllerId The ID of the USB controller. + @param[out] ControllerType On output, returns the type of the USB controller. + @param[out] BaseAddress On output, returns the base address of UHCI's I/O ports + if UHCI is enabled or the base address of EHCI's MMIO + if EHCI is enabled. + + @retval EFI_SUCCESS USB controller attributes were returned successfully. + @retval EFI_INVALID_PARAMETER ControllerId is greater than the maximum number + of USB controller supported by this platform. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_GET_USB_CONTROLLER)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_CONTROLLER_PPI *This, + IN UINT8 UsbControllerId, + OUT UINTN *ControllerType, + OUT UINTN *BaseAddress + ); + +/// +/// This PPI contains a single service to retrieve the USB Host Controller type +/// and the base address of the I/O ports used to access the USB Host Controller. +/// +struct _PEI_USB_CONTROLLER_PPI { + PEI_GET_USB_CONTROLLER GetUsbController; +}; + +extern EFI_GUID gPeiUsbControllerPpiGuid; + +#endif diff --git a/MdeModulePkg/Include/Ppi/UsbHostController.h b/MdeModulePkg/Include/Ppi/UsbHostController.h new file mode 100644 index 0000000000..107b7997d2 --- /dev/null +++ b/MdeModulePkg/Include/Ppi/UsbHostController.h @@ -0,0 +1,251 @@ +/** @file + Defines the USB Host Controller PPI that provides I/O services for a USB Host + Controller that may be used to access recovery devices. These interfaces are + modeled on the UEFI 2.3 specification EFI_USB2_HOST_CONTROLLER_PROTOCOL. + Refer to section 16.1 of the UEFI 2.3 Specification for more information on + these interfaces. + +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 _PEI_USB_HOST_CONTROLLER_PPI_H_ +#define _PEI_USB_HOST_CONTROLLER_PPI_H_ + +#include + +/// +/// Global ID for the PEI_USB_HOST_CONTROLLER_PPI. +/// +#define PEI_USB_HOST_CONTROLLER_PPI_GUID \ + { \ + 0x652b38a9, 0x77f4, 0x453f, { 0x89, 0xd5, 0xe7, 0xbd, 0xc3, 0x52, 0xfc, 0x53} \ + } + +/// +/// Forward declaration for the PEI_USB_HOST_CONTROLLER_PPI. +/// +typedef struct _PEI_USB_HOST_CONTROLLER_PPI PEI_USB_HOST_CONTROLLER_PPI; + +/** + Initiate a USB control transfer using a specific USB Host controller on the USB bus. + + @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[in] DeviceAddress Represents the address of the target device + on the USB. + @param[in] DeviceSpeed Indicates device speed. + @param[in] MaximumPacketLength Indicates the maximum packet size that the + default control transfer + endpoint is capable of sending or receiving. + @param[in] Request A pointer to the USB device request that + will be sent to the USB device. + @param[in] TransferDirection Specifies the data direction for the transfer. + There are three values available: + EfiUsbDataIn, EfiUsbDataOut and EfiUsbNoData. + @param[in,out] Data A pointer to the buffer of data that will + be transmitted to USB device or + received from USB device. + @param[in,out] DataLength On input, indicates the size, in bytes, of + the data buffer specified by Data. + On output, indicates the amount of data + actually transferred. + @param[in] TimeOut Indicates the maximum time, in milliseconds, + that the transfer is allowed to complete. + @param[out] TransferResult A pointer to the detailed result information + generated by this control transfer. + + @retval EFI_DEVICE_ERROR The control transfer failed due to host controller + or device error. + @retval EFI_SUCCESS The control transfer was completed successfully. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB_HOST_CONTROLLER_CONTROL_TRANSFER)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 DeviceAddress, + IN UINT8 DeviceSpeed, + IN UINT8 MaximumPacketLength, + IN 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 + ); + +/** + Initiate a USB bulk transfer using a specific USB Host controller on the USB bus. + + @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[in] DeviceAddress Represents the address of the target device + on the USB. + @param[in] EndPointAddress The combination of an endpoint number and + an endpoint direction of the target USB device. + @param[in] MaximumPacketLength Indicates the maximum packet size the target + endpoint is capable of sending or receiving. + @param[in,out] Data Array of pointers to the buffers of data + that will be transmitted to USB device or + received from USB device. + @param[in,out] DataLength When input, indicates the size, in bytes, of + the data buffers specified by Data. When output, + indicates the data size actually transferred. + @param[in,out] DataToggle A pointer to the data toggle value. + @param[in] TimeOut Indicates the maximum time, in milliseconds, + in which the transfer is allowed to complete. + @param[out] TransferResult A pointer to the detailed result information + of the bulk transfer. + + @retval EFI_SUCCESS The bulk transfer was completed successfully. + @retval EFI_DEVICE_ERROR The bulk transfer failed due to host controller or device error. + Caller should check TransferResult for detailed error information. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB_HOST_CONTROLLER_BULK_TRANSFER)( + 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_DEVICE_ERROR An error was encountered while attempting to retrieve + the port number. + @retval EFI_INVALID_PARAMETER PortNumber is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB_HOST_CONTROLLER_GET_ROOTHUB_PORT_NUMBER)( + 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[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[in] PortNumber Specifies the root hub port from which the status is + to be retrieved. + This value is zero based. + @param[out] PortStatus A pointer to the current port status bits and port + status change bits. + + @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. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB_HOST_CONTROLLER_GET_ROOTHUB_PORT_STATUS)( + 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[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[in] PortNumber Specifies the root hub port whose feature is requested + to be set. This value is zero based. + @param[in] PortFeature Indicates the feature selector associated with the feature + set request. + + @retval EFI_SUCCESS The feature specified by PortFeature was set for + the USB root hub port specified by PortNumber. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid + for this function. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB_HOST_CONTROLLER_SET_ROOTHUB_PORT_FEATURE)( + 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[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[in] PortNumber Specifies the root hub port whose feature is + requested to be cleared. + @param[in] PortFeature Indicates the feature selector associated with the + feature clear request. + + @return EFI_SUCCESS The feature specified by PortFeature was cleared + for the USB root hub port specified by PortNumber. + @return EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + @return EFI_DEVICE_ERROR Can't read the register. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB_HOST_CONTROLLER_CLEAR_ROOTHUB_PORT_FEATURE)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ); + +/// +/// This PPI contains a set of services to interact with the USB host controller. +/// These interfaces are modeled on the UEFI 2.3 specification protocol +/// EFI_USB2_HOST_CONTROLLER_PROTOCOL. Refer to section 16.1 of the UEFI 2.3 +/// Specification for more information on these interfaces. +/// +struct _PEI_USB_HOST_CONTROLLER_PPI { + PEI_USB_HOST_CONTROLLER_CONTROL_TRANSFER ControlTransfer; + PEI_USB_HOST_CONTROLLER_BULK_TRANSFER BulkTransfer; + PEI_USB_HOST_CONTROLLER_GET_ROOTHUB_PORT_NUMBER GetRootHubPortNumber; + PEI_USB_HOST_CONTROLLER_GET_ROOTHUB_PORT_STATUS GetRootHubPortStatus; + PEI_USB_HOST_CONTROLLER_SET_ROOTHUB_PORT_FEATURE SetRootHubPortFeature; + PEI_USB_HOST_CONTROLLER_CLEAR_ROOTHUB_PORT_FEATURE ClearRootHubPortFeature; +}; + +extern EFI_GUID gPeiUsbHostControllerPpiGuid; + +#endif + diff --git a/MdeModulePkg/Include/Ppi/UsbIo.h b/MdeModulePkg/Include/Ppi/UsbIo.h new file mode 100644 index 0000000000..16660f88ae --- /dev/null +++ b/MdeModulePkg/Include/Ppi/UsbIo.h @@ -0,0 +1,190 @@ +/** @file + Defines the PEI_USB_IO_PPI that the USB-related PEIM can use for I/O operations + on the USB BUS. This interface enables recovery from a + USB-class storage device, such as USB CD/DVD, USB hard drive, or USB FLASH + drive. These interfaces are modeled on the UEFI 2.3 specification EFI_USB_IO_PROTOCOL. + Refer to section 16.2.4 of the UEFI 2.3 Specification for more information on + these interfaces. + +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 _PEI_USB_IO_PPI_H_ +#define _PEI_USB_IO_PPI_H_ + +#include + +/// +/// Global ID for the PEI_USB_IO_PPI. +/// +#define PEI_USB_IO_PPI_GUID \ + { \ + 0x7c29785c, 0x66b9, 0x49fc, { 0xb7, 0x97, 0x1c, 0xa5, 0x55, 0xe, 0xf2, 0x83} \ + } + +/// +/// Forward declaration for the PEI_USB_IO_PPI. +/// +typedef struct _PEI_USB_IO_PPI PEI_USB_IO_PPI; + +/** + Submits control transfer to a target USB device. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the PEI_USB_IO_PPI. + @param[in] Request A pointer to the USB device request that will be + sent to the USB device. + @param[in] Direction Specifies the data direction for the transfer. There + are three values available: + EfiUsbDataIn, EfiUsbDataOut and EfiUsbNoData. + @param[in] TimeOut Indicates the maximum time, in milliseconds, that + the transfer is allowed to complete. + @param[in,out] Data A pointer to the buffer of data that will be + transmitted to or received from the USB device. + @param[in] DataLength On input, indicates the size, in bytes, of the data + buffer specified by Data. + + @retval EFI_SUCCESS The control transfer was completed successfully. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The control transfer could not be completed due + to a lack of resources. + @retval EFI_TIMEOUT The control transfer failed due to timeout. + @retval EFI_DEVICE_ERROR The control transfer failed due to host controller + or device error. + Caller should check TransferResult for detailed + error information. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB_CONTROL_TRANSFER)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINT32 Timeout, + IN OUT VOID *Data OPTIONAL, + IN UINTN DataLength OPTIONAL + ); + +/** + Submits bulk transfer to a target USB device. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the PEI_USB_IO_PPI. + @param[in] EndPointAddress The endpoint address. + @param[in] Data The data buffer to be transfered. + @param[in] DataLength The length of data buffer. + @param[in] TimeOut The timeout for the transfer, in milliseconds. + + @retval EFI_SUCCESS The bulk transfer completed successfully. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The bulk transfer could not be completed due to + a lack of resources. + @retval EFI_TIMEOUT The bulk transfer failed due to timeout. + @retval EFI_DEVICE_ERROR The bulk transfer failed due to host controller + or device error. + Caller should check TransferResult for detailed + error information. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB_BULK_TRANSFER)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout + ); + +/** + Get interface descriptor from a USB device. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the PEI_USB_IO_PPI. + @param[in] InterfaceDescriptor The interface descriptor. + + @retval EFI_SUCCESS The interface descriptor was returned. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_DEVICE_ERROR A device error occurred, the function failed to + get the interface descriptor. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB_GET_INTERFACE_DESCRIPTOR)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN EFI_USB_INTERFACE_DESCRIPTOR **InterfaceDescriptor + ); + +/** + Get endpoint descriptor from a USB device. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the PEI_USB_IO_PPI. + @param[in] EndPointIndex The index of the end point. + @param[in] EndpointDescriptor The endpoint descriptor. + + @retval EFI_SUCCESS The endpoint descriptor was returned. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_DEVICE_ERROR A device error occurred, the function failed to + get the endpoint descriptor. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB_GET_ENDPOINT_DESCRIPTOR)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This, + IN UINT8 EndpointIndex, + IN EFI_USB_ENDPOINT_DESCRIPTOR **EndpointDescriptor + ); + +/** + Issue a port reset to the device. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the PEI_USB_IO_PPI. + + @retval EFI_SUCCESS The port reset was issued successfully. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_DEVICE_ERROR Device error occurred. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_USB_PORT_RESET)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *This + ); + +/// +/// This PPI contains a set of services to interact with the USB host controller. +/// These interfaces are modeled on the UEFI 2.3 specification EFI_USB_IO_PROTOCOL. +/// Refer to section 16.2.4 of the UEFI 2.3 Specification for more information on +/// these interfaces. +/// +struct _PEI_USB_IO_PPI { + PEI_USB_CONTROL_TRANSFER UsbControlTransfer; + PEI_USB_BULK_TRANSFER UsbBulkTransfer; + PEI_USB_GET_INTERFACE_DESCRIPTOR UsbGetInterfaceDescriptor; + PEI_USB_GET_ENDPOINT_DESCRIPTOR UsbGetEndpointDescriptor; + PEI_USB_PORT_RESET UsbPortReset; +}; + +extern EFI_GUID gPeiUsbIoPpiGuid; + +#endif diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index f2dbb71556..0987b7955d 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -173,6 +173,19 @@ # Include/Guid/EventIdle.h gIdleLoopEventGuid = { 0x3c8d294c, 0x5fc3, 0x4451, { 0xbb, 0x31, 0xc4, 0xc0, 0x32, 0x29, 0x5e, 0x6c } } +[Ppis] + ## Include/Ppi/UsbHostController.h + gPeiUsbHostControllerPpiGuid = { 0x652B38A9, 0x77F4, 0x453F, { 0x89, 0xD5, 0xE7, 0xBD, 0xC3, 0x52, 0xFC, 0x53 }} + + ## Include/Ppi/Usb2HostController.h + gPeiUsb2HostControllerPpiGuid = { 0xfedd6305, 0xe2d7, 0x4ed5, { 0x9f, 0xaa, 0xda, 0x8, 0xe, 0x33, 0x6c, 0x22 }} + + ## Include/Ppi/UsbController.h + gPeiUsbControllerPpiGuid = { 0x3BC1F6DE, 0x693E, 0x4547, { 0xA3, 0x00, 0x21, 0x82, 0x3C, 0xA4, 0x20, 0xB2 }} + + ## Include/Ppi/UsbIo.h + gPeiUsbIoPpiGuid = { 0x7C29785C, 0x66B9, 0x49FC, { 0xB7, 0x97, 0x1C, 0xA5, 0x55, 0x0E, 0xF2, 0x83 }} + [Protocols] ## Load File protocol provides capability to load and unload EFI image into memory and execute it. # Include/Protocol/LoadPe32Image.h diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 5fd84ee5fb..a81e4d8f7a 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -185,6 +185,10 @@ MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf + MdeModulePkg/Bus/Pci/UhciPei/UhciPei.inf + MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf + MdeModulePkg/Bus/Usb/UsbBusPei/UsbBusPei.inf + MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPei.inf MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf -- 2.39.2