From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- roms/edk2/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.c | 3297 ++++++++++++++++++++++ 1 file changed, 3297 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.c (limited to 'roms/edk2/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.c') diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.c b/roms/edk2/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.c new file mode 100644 index 000000000..a05834da3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciPei/UhcPeim.c @@ -0,0 +1,3297 @@ +/** @file +PEIM to produce gPeiUsbHostControllerPpiGuid based on gPeiUsbControllerPpiGuid +which is used to enable recovery function from USB Drivers. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "UhcPeim.h" + +/** + Stop the host controller. + + @param Uhc The UHCI device. + @param Timeout Max time allowed. + + @retval EFI_SUCCESS The host controller is stopped. + @retval EFI_TIMEOUT Failed to stop the host controller. + +**/ +EFI_STATUS +UhciStopHc ( + IN USB_UHC_DEV *Uhc, + IN UINTN Timeout + ) +{ + UINT16 CommandContent; + UINT16 UsbSts; + UINTN Index; + + CommandContent = USBReadPortW (Uhc, Uhc->UsbHostControllerBaseAddress + USBCMD); + CommandContent &= USBCMD_RS; + USBWritePortW (Uhc, Uhc->UsbHostControllerBaseAddress + USBCMD, CommandContent); + + // + // ensure the HC is in halt status after send the stop command + // Timeout is in us unit. + // + for (Index = 0; Index < (Timeout / 50) + 1; Index++) { + UsbSts = USBReadPortW (Uhc, Uhc->UsbHostControllerBaseAddress + USBSTS); + + if ((UsbSts & USBSTS_HCH) == USBSTS_HCH) { + return EFI_SUCCESS; + } + + MicroSecondDelay (50); + } + + return EFI_TIMEOUT; +} + +/** + One notified function to stop the Host Controller at the end of PEI + + @param[in] PeiServices Pointer to PEI Services Table. + @param[in] NotifyDescriptor Pointer to the descriptor for the Notification event that + caused this function to execute. + @param[in] Ppi Pointer to the PPI data associated with this function. + + @retval EFI_SUCCESS The function completes successfully + @retval others +**/ +EFI_STATUS +EFIAPI +UhcEndOfPei ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + USB_UHC_DEV *Uhc; + + Uhc = PEI_RECOVERY_USB_UHC_DEV_FROM_THIS_NOTIFY (NotifyDescriptor); + + // + // Stop the Host Controller + // + UhciStopHc (Uhc, 1000 * 1000); + + return EFI_SUCCESS; +} + +/** + 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; + IoMmuInit (&UhcDev->IoMmu); + 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; + } + + UhcDev->EndOfPeiNotifyList.Flags = (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST); + UhcDev->EndOfPeiNotifyList.Guid = &gEfiEndOfPeiSignalPpiGuid; + UhcDev->EndOfPeiNotifyList.Notify = UhcEndOfPei; + + PeiServicesNotifyPpi (&UhcDev->EndOfPeiNotifyList); + + 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. + If Timeout is 0, then the caller must wait for the function + to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR is returned. + @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 DataToggle; + UINT8 *RequestPhy; + VOID *RequestMap; + UINT8 *DataPhy; + VOID *DataMap; + + UhcDev = PEI_RECOVERY_USB_UHC_DEV_FROM_UHCI_THIS (This); + + StatusReg = UhcDev->UsbHostControllerBaseAddress + USBSTS; + + PktID = INPUT_PACKET_ID; + + RequestMap = NULL; + + 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); + + // + // Map the Request and data for bus master access, + // then create a list of TD for this transfer + // + Status = UhciMapUserRequest (UhcDev, Request, &RequestPhy, &RequestMap); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = UhciMapUserData (UhcDev, TransferDirection, Data, DataLength, &PktID, &DataPhy, &DataMap); + + if (EFI_ERROR (Status)) { + if (RequestMap != NULL) { + IoMmuUnmap (UhcDev->IoMmu, RequestMap); + } + return Status; + } + + // + // generate Setup Stage TD + // + + PtrQH = UhcDev->ConfigQH; + + GenSetupStageTD ( + UhcDev, + DeviceAddress, + 0, + DeviceSpeed, + (UINT8 *) Request, + RequestPhy, + (UINT8) sizeof (EFI_USB_DEVICE_REQUEST), + &PtrSetupTD + ); + + // + // link setup TD structures to QH structure + // + LinkTDToQH (PtrQH, PtrSetupTD); + + PtrPreTD = PtrSetupTD; + + // + // Data Stage of Control Transfer + // + + if (TransferDirection == EfiUsbNoData) { + DataLen = 0; + } else { + DataLen = (UINT32) *DataLength; + } + + 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, + Data, + DataPhy, + PacketSize, + PktID, + DataToggle, + DeviceSpeed, + &PtrTD + ); + + // + // Link two TDs in vertical depth + // + LinkTDToTD (PtrPreTD, PtrTD); + PtrPreTD = PtrTD; + + DataToggle ^= 1; + Data = (VOID *) ((UINT8 *) Data + PacketSize); + DataPhy += 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)) { + *TransferResult |= EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + } + + ClearStatusReg (UhcDev, StatusReg); + + if (DataMap != NULL) { + IoMmuUnmap (UhcDev->IoMmu, DataMap); + } + if (RequestMap != NULL) { + IoMmuUnmap (UhcDev->IoMmu, RequestMap); + } + + 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. + If Timeout is 0, then the caller must wait for the function + to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR is returned. + @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; + + BOOLEAN IsFirstTD; + + EFI_STATUS Status; + + EFI_USB_DATA_DIRECTION TransferDirection; + + BOOLEAN ShortPacketEnable; + + UINT16 CommandContent; + + UINT8 *DataPhy; + VOID *DataMap; + + 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; + + 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); + + // + // Map the source data buffer for bus master access, + // then create a list of TDs + // + if ((EndPointAddress & 0x80) != 0) { + TransferDirection = EfiUsbDataIn; + } else { + TransferDirection = EfiUsbDataOut; + } + + Status = UhciMapUserData (UhcDev, TransferDirection, Data, DataLength, &PktID, &DataPhy, &DataMap); + + if (EFI_ERROR (Status)) { + return Status; + } + + DataLen = (UINT32) *DataLength; + + 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, + Data, + DataPhy, + 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; + Data = (VOID *) ((UINT8 *) Data + PacketSize); + DataPhy += 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)) { + *TransferResult |= EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + } + + ClearStatusReg (UhcDev, StatusReg); + + if (DataMap != NULL) { + IoMmuUnmap (UhcDev->IoMmu, DataMap); + } + + 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; + } + ASSERT (UhcDev->ConfigQH != NULL); + + Status = CreateQH(UhcDev, &UhcDev->BulkQH); + if (Status != EFI_SUCCESS) { + return EFI_OUT_OF_RESOURCES; + } + ASSERT (UhcDev->BulkQH != NULL); + + // + //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; + } +} + + + +/** + 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; +} + + + +/** + 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; +} + + + +/** + 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 CPU memory address of request structure buffer to transfer. + @param RequestPhy PCI memory address of request structure buffer to transfer. + @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 *RequestPhy, + 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; + // + // Set the beginning address of the buffer that will be used + // during the transaction. + // + TdStruct->TDData.TDBufferPtr = (UINT32) (UINTN) RequestPhy; + + *PtrTD = TdStruct; + + return EFI_SUCCESS; +} + +/** + Generate Data Stage TD. + + @param UhcDev The UHCI device. + @param DevAddr Device address. + @param Endpoint Endpoint number. + @param PtrData CPU memory address of user data buffer to transfer. + @param DataPhy PCI memory address of user data buffer to transfer. + @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 *DataPhy, + 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; + // + // Set the beginning address of the buffer that will be used + // during the transaction. + // + TdStruct->TDData.TDBufferPtr = (UINT32) (UINTN) DataPhy; + + *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; + // + // Set the beginning address of the buffer that will be used + // during the transaction. + // + PtrTDStruct->TDData.TDBufferPtr = 0; + + *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); +} + + + +/** + 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; +} + +/** + 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; + BOOLEAN InfiniteLoop; + + ErrTDPos = 0; + *TransferResult = EFI_USB_NOERROR; + *ActualLen = 0; + InfiniteLoop = FALSE; + + Delay = TimeOut * STALL_1_MILLI_SECOND; + // + // If Timeout is 0, then the caller must wait for the function to be completed + // until EFI_SUCCESS or EFI_DEVICE_ERROR is returned. + // + if (TimeOut == 0) { + InfiniteLoop = TRUE; + } + + 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 (STALL_1_MICRO_SECOND); + Delay--; + + } while (InfiniteLoop || (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; + BOOLEAN InfiniteLoop; + + ErrTDPos = 0; + *TransferResult = EFI_USB_NOERROR; + *ActualLen = 0; + InfiniteLoop = FALSE; + + Delay = TimeOut * STALL_1_MILLI_SECOND; + // + // If Timeout is 0, then the caller must wait for the function to be completed + // until EFI_SUCCESS or EFI_DEVICE_ERROR is returned. + // + if (TimeOut == 0) { + InfiniteLoop = TRUE; + } + + 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 (STALL_1_MICRO_SECOND); + Delay--; + + } while (InfiniteLoop || (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; + UINT8 *TempPtr; + UINTN MemPages; + UINT8 *Ptr; + VOID *Mapping; + EFI_PHYSICAL_ADDRESS MappedAddr; + + // + // Memory Block uses MemoryBlockSizeInPages pages, + // memory management header and bit array use 1 page + // + MemPages = MemoryBlockSizeInPages + 1; + Status = IoMmuAllocateBuffer ( + UhcDev->IoMmu, + MemPages, + (VOID **) &TempPtr, + &MappedAddr, + &Mapping + ); + if (EFI_ERROR (Status) || (TempPtr == NULL)) { + return EFI_OUT_OF_RESOURCES; + } + + Ptr = 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 = 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 array + // + 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; + } + } +} + + + + + +/** + Map address of request structure buffer. + + @param Uhc The UHCI device. + @param Request The user request buffer. + @param MappedAddr Mapped address of request. + @param Map Identificaion of this mapping to return. + + @return EFI_SUCCESS Success. + @return EFI_DEVICE_ERROR Fail to map the user request. + +**/ +EFI_STATUS +UhciMapUserRequest ( + IN USB_UHC_DEV *Uhc, + IN OUT VOID *Request, + OUT UINT8 **MappedAddr, + OUT VOID **Map + ) +{ + EFI_STATUS Status; + UINTN Len; + EFI_PHYSICAL_ADDRESS PhyAddr; + + Len = sizeof (EFI_USB_DEVICE_REQUEST); + Status = IoMmuMap ( + Uhc->IoMmu, + EdkiiIoMmuOperationBusMasterRead, + Request, + &Len, + &PhyAddr, + Map + ); + + if (!EFI_ERROR (Status)) { + *MappedAddr = (UINT8 *) (UINTN) PhyAddr; + } + + return Status; +} + +/** + Map address of user data buffer. + + @param Uhc The UHCI device. + @param Direction Direction of the data transfer. + @param Data The user data buffer. + @param Len Length of the user data. + @param PktId Packet identificaion. + @param MappedAddr Mapped address to return. + @param Map Identificaion of this mapping to return. + + @return EFI_SUCCESS Success. + @return EFI_DEVICE_ERROR Fail to map the user data. + +**/ +EFI_STATUS +UhciMapUserData ( + IN USB_UHC_DEV *Uhc, + IN EFI_USB_DATA_DIRECTION Direction, + IN VOID *Data, + IN OUT UINTN *Len, + OUT UINT8 *PktId, + OUT UINT8 **MappedAddr, + OUT VOID **Map + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS PhyAddr; + + Status = EFI_SUCCESS; + + switch (Direction) { + case EfiUsbDataIn: + // + // BusMasterWrite means cpu read + // + *PktId = INPUT_PACKET_ID; + Status = IoMmuMap ( + Uhc->IoMmu, + EdkiiIoMmuOperationBusMasterWrite, + Data, + Len, + &PhyAddr, + Map + ); + + if (EFI_ERROR (Status)) { + goto EXIT; + } + + *MappedAddr = (UINT8 *) (UINTN) PhyAddr; + break; + + case EfiUsbDataOut: + *PktId = OUTPUT_PACKET_ID; + Status = IoMmuMap ( + Uhc->IoMmu, + EdkiiIoMmuOperationBusMasterRead, + Data, + Len, + &PhyAddr, + Map + ); + + if (EFI_ERROR (Status)) { + goto EXIT; + } + + *MappedAddr = (UINT8 *) (UINTN) PhyAddr; + break; + + case EfiUsbNoData: + if ((Len != NULL) && (*Len != 0)) { + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + + *PktId = OUTPUT_PACKET_ID; + *MappedAddr = NULL; + *Map = NULL; + break; + + default: + Status = EFI_INVALID_PARAMETER; + } + +EXIT: + return Status; +} + -- cgit 1.2.3-korg