mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-28 12:25:19 +01:00
7c0aa811ec
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
3296 lines
79 KiB
C
3296 lines
79 KiB
C
/** @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;
|
|
|
|
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;
|
|
}
|
|
|