/** @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. <BR> | |
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; | |
} |