/** @file
This file contains the implementation of Usb Hc Protocol.
Copyright (c) 2013-2015 Intel Corporation.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "OhcPeim.h"
/**
Submits control transfer to a target USB device.
@param PeiServices The pointer of EFI_PEI_SERVICES.
@param This The pointer of PEI_USB_HOST_CONTROLLER_PPI.
@param DeviceAddress The target device address.
@param DeviceSpeed Target device speed.
@param MaximumPacketLength Maximum packet size the default control transfer
endpoint is capable of sending or receiving.
@param Request USB device request to send.
@param TransferDirection Specifies the data direction for the data stage.
@param Data Data buffer to be transmitted or received from USB device.
@param DataLength The size (in bytes) of the data buffer.
@param TimeOut Indicates the maximum timeout, in millisecond.
@param TransferResult Return the result of this control transfer.
@retval EFI_SUCCESS Transfer was completed successfully.
@retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resources.
@retval EFI_INVALID_PARAMETER Some parameters are invalid.
@retval EFI_TIMEOUT Transfer failed due to timeout.
@retval EFI_DEVICE_ERROR Transfer failed due to host controller or device error.
**/
EFI_STATUS
EFIAPI
OhciControlTransfer (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_HOST_CONTROLLER_PPI *This,
IN UINT8 DeviceAddress,
IN UINT8 DeviceSpeed,
IN UINT8 MaxPacketLength,
IN EFI_USB_DEVICE_REQUEST *Request,
IN EFI_USB_DATA_DIRECTION TransferDirection,
IN OUT VOID *Data,
IN OUT UINTN *DataLength,
IN UINTN TimeOut,
OUT UINT32 *TransferResult
)
{
USB_OHCI_HC_DEV *Ohc;
ED_DESCRIPTOR *Ed;
TD_DESCRIPTOR *HeadTd;
TD_DESCRIPTOR *SetupTd;
TD_DESCRIPTOR *DataTd;
TD_DESCRIPTOR *StatusTd;
TD_DESCRIPTOR *EmptyTd;
EFI_STATUS Status;
UINT32 DataPidDir;
UINT32 StatusPidDir;
UINTN TimeCount;
UINT32 ErrorCode;
UINTN ActualSendLength;
UINTN LeftLength;
UINT8 DataToggle;
EFI_PHYSICAL_ADDRESS ReqMapPhyAddr = 0;
UINTN DataMapLength = 0;
EFI_PHYSICAL_ADDRESS DataMapPhyAddr = 0;
HeadTd = NULL;
DataTd = NULL;
if ((TransferDirection != EfiUsbDataOut && TransferDirection != EfiUsbDataIn &&
TransferDirection != EfiUsbNoData) ||
Request == NULL || DataLength == NULL || TransferResult == NULL ||
(TransferDirection == EfiUsbNoData && (*DataLength != 0 || Data != NULL)) ||
(TransferDirection != EfiUsbNoData && (*DataLength == 0 || Data == NULL)) ||
(DeviceSpeed != EFI_USB_SPEED_LOW && DeviceSpeed != EFI_USB_SPEED_FULL) ||
(MaxPacketLength != 8 && MaxPacketLength != 16 &&
MaxPacketLength != 32 && MaxPacketLength != 64)) {
DEBUG ((EFI_D_INFO, "OhciControlTransfer: EFI_INVALID_PARAMETER\n"));
return EFI_INVALID_PARAMETER;
}
if (*DataLength > MAX_BYTES_PER_TD) {
DEBUG ((EFI_D_ERROR, "OhciControlTransfer: Request data size is too large\n"));
return EFI_INVALID_PARAMETER;
}
Ohc = PEI_RECOVERY_USB_OHC_DEV_FROM_EHCI_THIS(This);
if (TransferDirection == EfiUsbDataIn) {
DataPidDir = TD_IN_PID;
StatusPidDir = TD_OUT_PID;
} else {
DataPidDir = TD_OUT_PID;
StatusPidDir = TD_IN_PID;
}
OhciSetHcControl (Ohc, CONTROL_ENABLE, 0);
if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 0) {
MicroSecondDelay (HC_1_MILLISECOND);
if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 0) {
*TransferResult = EFI_USB_ERR_SYSTEM;
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to disable CONTROL transfer\n"));
return EFI_DEVICE_ERROR;
}
}
OhciSetMemoryPointer (Ohc, HC_CONTROL_HEAD, NULL);
Ed = OhciCreateED (Ohc);
if (Ed == NULL) {
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate ED buffer\n"));
return EFI_OUT_OF_RESOURCES;
}
OhciSetEDField (Ed, ED_SKIP, 1);
OhciSetEDField (Ed, ED_FUNC_ADD, DeviceAddress);
OhciSetEDField (Ed, ED_ENDPT_NUM, 0);
OhciSetEDField (Ed, ED_DIR, ED_FROM_TD_DIR);
OhciSetEDField (Ed, ED_SPEED, DeviceSpeed);
OhciSetEDField (Ed, ED_FORMAT | ED_HALTED | ED_DTTOGGLE, 0);
OhciSetEDField (Ed, ED_MAX_PACKET, MaxPacketLength);
OhciSetEDField (Ed, ED_PDATA, 0);
OhciSetEDField (Ed, ED_ZERO, 0);
OhciSetEDField (Ed, ED_TDHEAD_PTR, (UINT32) NULL);
OhciSetEDField (Ed, ED_TDTAIL_PTR, (UINT32) NULL);
OhciSetEDField (Ed, ED_NEXT_EDPTR, (UINT32) NULL);
OhciAttachEDToList (Ohc, CONTROL_LIST, Ed, NULL);
//
// Setup Stage
//
if(Request != NULL) {
ReqMapPhyAddr = (EFI_PHYSICAL_ADDRESS)(UINTN)Request;
}
SetupTd = OhciCreateTD (Ohc);
if (SetupTd == NULL) {
Status = EFI_OUT_OF_RESOURCES;
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate Setup TD buffer\n"));
goto FREE_ED_BUFF;
}
HeadTd = SetupTd;
OhciSetTDField (SetupTd, TD_PDATA, 0);
OhciSetTDField (SetupTd, TD_BUFFER_ROUND, 1);
OhciSetTDField (SetupTd, TD_DIR_PID, TD_SETUP_PID);
OhciSetTDField (SetupTd, TD_DELAY_INT, TD_NO_DELAY);
OhciSetTDField (SetupTd, TD_DT_TOGGLE, 2);
OhciSetTDField (SetupTd, TD_ERROR_CNT, 0);
OhciSetTDField (SetupTd, TD_COND_CODE, TD_TOBE_PROCESSED);
OhciSetTDField (SetupTd, TD_CURR_BUFFER_PTR, (UINTN)ReqMapPhyAddr);
OhciSetTDField (SetupTd, TD_NEXT_PTR, (UINT32) NULL);
OhciSetTDField (SetupTd, TD_BUFFER_END_PTR, (UINTN)ReqMapPhyAddr + sizeof (EFI_USB_DEVICE_REQUEST) - 1);
SetupTd->ActualSendLength = 0;
SetupTd->DataBuffer = NULL;
SetupTd->NextTDPointer = NULL;
DataMapLength = *DataLength;
if ((Data != NULL) && (DataMapLength != 0)) {
DataMapPhyAddr = (EFI_PHYSICAL_ADDRESS)(UINTN)Data;
}
//
//Data Stage
//
LeftLength = DataMapLength;
ActualSendLength = DataMapLength;
DataToggle = 1;
while (LeftLength > 0) {
ActualSendLength = LeftLength;
if (LeftLength > MaxPacketLength) {
ActualSendLength = MaxPacketLength;
}
DataTd = OhciCreateTD (Ohc);
if (DataTd == NULL) {
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate Data TD buffer\n"));
Status = EFI_OUT_OF_RESOURCES;
goto FREE_TD_BUFF;
}
OhciSetTDField (DataTd, TD_PDATA, 0);
OhciSetTDField (DataTd, TD_BUFFER_ROUND, 1);
OhciSetTDField (DataTd, TD_DIR_PID, DataPidDir);
OhciSetTDField (DataTd, TD_DELAY_INT, TD_NO_DELAY);
OhciSetTDField (DataTd, TD_DT_TOGGLE, DataToggle);
OhciSetTDField (DataTd, TD_ERROR_CNT, 0);
OhciSetTDField (DataTd, TD_COND_CODE, TD_TOBE_PROCESSED);
OhciSetTDField (DataTd, TD_CURR_BUFFER_PTR, (UINT32) DataMapPhyAddr);
OhciSetTDField (DataTd, TD_BUFFER_END_PTR, (UINT32) DataMapPhyAddr + ActualSendLength - 1);
OhciSetTDField (DataTd, TD_NEXT_PTR, (UINT32) NULL);
DataTd->ActualSendLength = ActualSendLength;
DataTd->DataBuffer = (UINT8 *)(UINTN)DataMapPhyAddr;
DataTd->NextTDPointer = 0;
OhciLinkTD (HeadTd, DataTd);
DataToggle ^= 1;
DataMapPhyAddr += ActualSendLength;
LeftLength -= ActualSendLength;
}
//
// Status Stage
//
StatusTd = OhciCreateTD (Ohc);
if (StatusTd == NULL) {
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate Status TD buffer\n"));
Status = EFI_OUT_OF_RESOURCES;
goto FREE_TD_BUFF;
}
OhciSetTDField (StatusTd, TD_PDATA, 0);
OhciSetTDField (StatusTd, TD_BUFFER_ROUND, 1);
OhciSetTDField (StatusTd, TD_DIR_PID, StatusPidDir);
OhciSetTDField (StatusTd, TD_DELAY_INT, 7);
OhciSetTDField (StatusTd, TD_DT_TOGGLE, 3);
OhciSetTDField (StatusTd, TD_ERROR_CNT, 0);
OhciSetTDField (StatusTd, TD_COND_CODE, TD_TOBE_PROCESSED);
OhciSetTDField (StatusTd, TD_CURR_BUFFER_PTR, (UINT32) NULL);
OhciSetTDField (StatusTd, TD_NEXT_PTR, (UINT32) NULL);
OhciSetTDField (StatusTd, TD_BUFFER_END_PTR, (UINT32) NULL);
StatusTd->ActualSendLength = 0;
StatusTd->DataBuffer = NULL;
StatusTd->NextTDPointer = NULL;
OhciLinkTD (HeadTd, StatusTd);
//
// Empty Stage
//
EmptyTd = OhciCreateTD (Ohc);
if (EmptyTd == NULL) {
Status = EFI_OUT_OF_RESOURCES;
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate Empty TD buffer\n"));
goto FREE_TD_BUFF;
}
OhciSetTDField (EmptyTd, TD_PDATA, 0);
OhciSetTDField (EmptyTd, TD_BUFFER_ROUND, 0);
OhciSetTDField (EmptyTd, TD_DIR_PID, 0);
OhciSetTDField (EmptyTd, TD_DELAY_INT, 0);
//OhciSetTDField (EmptyTd, TD_DT_TOGGLE, CurrentToggle);
EmptyTd->Word0.DataToggle = 0;
OhciSetTDField (EmptyTd, TD_ERROR_CNT, 0);
OhciSetTDField (EmptyTd, TD_COND_CODE, 0);
OhciSetTDField (EmptyTd, TD_CURR_BUFFER_PTR, 0);
OhciSetTDField (EmptyTd, TD_BUFFER_END_PTR, 0);
OhciSetTDField (EmptyTd, TD_NEXT_PTR, 0);
EmptyTd->ActualSendLength = 0;
EmptyTd->DataBuffer = NULL;
EmptyTd->NextTDPointer = NULL;
OhciLinkTD (HeadTd, EmptyTd);
Ed->TdTailPointer = EmptyTd;
OhciAttachTDListToED (Ed, HeadTd);
//
OhciSetEDField (Ed, ED_SKIP, 0);
MicroSecondDelay (20 * HC_1_MILLISECOND);
OhciSetHcCommandStatus (Ohc, CONTROL_LIST_FILLED, 1);
OhciSetHcControl (Ohc, CONTROL_ENABLE, 1);
MicroSecondDelay (20 * HC_1_MILLISECOND);
if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 1) {
MicroSecondDelay (HC_1_MILLISECOND);
if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 1) {
*TransferResult = EFI_USB_ERR_SYSTEM;
Status = EFI_DEVICE_ERROR;
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to enable CONTROL transfer\n"));
goto FREE_TD_BUFF;
}
}
TimeCount = 0;
Status = CheckIfDone (Ohc, CONTROL_LIST, Ed, HeadTd, &ErrorCode);
while (Status == EFI_NOT_READY && TimeCount <= TimeOut) {
MicroSecondDelay (HC_1_MILLISECOND);
TimeCount++;
Status = CheckIfDone (Ohc, CONTROL_LIST, Ed, HeadTd, &ErrorCode);
}
//
*TransferResult = ConvertErrorCode (ErrorCode);
if (ErrorCode != TD_NO_ERROR) {
if (ErrorCode == TD_TOBE_PROCESSED) {
DEBUG ((EFI_D_INFO, "Control pipe timeout, > %d mS\r\n", TimeOut));
} else {
DEBUG ((EFI_D_INFO, "Control pipe broken\r\n"));
}
*DataLength = 0;
}
OhciSetHcControl (Ohc, CONTROL_ENABLE, 0);
if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 0) {
MicroSecondDelay (HC_1_MILLISECOND);
if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 0) {
*TransferResult = EFI_USB_ERR_SYSTEM;
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Cannot disable CONTROL_ENABLE transfer\n"));
goto FREE_TD_BUFF;
}
}
FREE_TD_BUFF:
while (HeadTd) {
DataTd = HeadTd;
HeadTd = HeadTd->NextTDPointer;
UsbHcFreeMem(Ohc->MemPool, DataTd, sizeof(TD_DESCRIPTOR));
}
FREE_ED_BUFF:
UsbHcFreeMem(Ohc->MemPool, Ed, sizeof(ED_DESCRIPTOR));
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 MaxiPacketLength Maximum packet size the endpoint is capable of
sending or receiving.
@param Data A pointers to the buffers of data to transmit
from or receive into.
@param DataLength The lenght of the data buffer.
@param DataToggle On input, the initial data toggle for the transfer;
On output, it is updated to to next data toggle to use of
the subsequent bulk transfer.
@param TimeOut Indicates the maximum time, in millisecond, which the
transfer is allowed to complete.
@param TransferResult A pointer to the detailed result information of the
bulk transfer.
@retval EFI_SUCCESS The transfer was completed successfully.
@retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource.
@retval EFI_INVALID_PARAMETER Parameters are invalid.
@retval EFI_TIMEOUT The transfer failed due to timeout.
@retval EFI_DEVICE_ERROR The transfer failed due to host controller error.
**/
EFI_STATUS
EFIAPI
OhciBulkTransfer (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_HOST_CONTROLLER_PPI *This,
IN UINT8 DeviceAddress,
IN UINT8 EndPointAddress,
IN UINT8 MaxPacketLength,
IN OUT VOID *Data,
IN OUT UINTN *DataLength,
IN OUT UINT8 *DataToggle,
IN UINTN TimeOut,
OUT UINT32 *TransferResult
)
{
USB_OHCI_HC_DEV *Ohc;
ED_DESCRIPTOR *Ed;
UINT32 DataPidDir;
TD_DESCRIPTOR *HeadTd;
TD_DESCRIPTOR *DataTd;
TD_DESCRIPTOR *EmptyTd;
EFI_STATUS Status;
UINT8 EndPointNum;
UINTN TimeCount;
UINT32 ErrorCode;
UINT8 CurrentToggle;
UINTN MapLength;
EFI_PHYSICAL_ADDRESS MapPyhAddr;
UINTN LeftLength;
UINTN ActualSendLength;
BOOLEAN FirstTD;
MapLength = 0;
MapPyhAddr = 0;
LeftLength = 0;
Status = EFI_SUCCESS;
if (Data == NULL || DataLength == NULL || DataToggle == NULL || TransferResult == NULL ||
*DataLength == 0 || (*DataToggle != 0 && *DataToggle != 1) ||
(MaxPacketLength != 8 && MaxPacketLength != 16 &&
MaxPacketLength != 32 && MaxPacketLength != 64)) {
return EFI_INVALID_PARAMETER;
}
Ohc = PEI_RECOVERY_USB_OHC_DEV_FROM_EHCI_THIS (This);
if ((EndPointAddress & 0x80) != 0) {
DataPidDir = TD_IN_PID;
} else {
DataPidDir = TD_OUT_PID;
}
EndPointNum = (EndPointAddress & 0xF);
OhciSetHcControl (Ohc, BULK_ENABLE, 0);
if (OhciGetHcControl (Ohc, BULK_ENABLE) != 0) {
MicroSecondDelay (HC_1_MILLISECOND);
if (OhciGetHcControl (Ohc, BULK_ENABLE) != 0) {
*TransferResult = EFI_USB_ERR_SYSTEM;
return EFI_DEVICE_ERROR;
}
}
OhciSetMemoryPointer (Ohc, HC_BULK_HEAD, NULL);
Ed = OhciCreateED (Ohc);
if (Ed == NULL) {
DEBUG ((EFI_D_INFO, "OhcBulkTransfer: Fail to allocate ED buffer\r\n"));
return EFI_OUT_OF_RESOURCES;
}
OhciSetEDField (Ed, ED_SKIP, 1);
OhciSetEDField (Ed, ED_FUNC_ADD, DeviceAddress);
OhciSetEDField (Ed, ED_ENDPT_NUM, EndPointNum);
OhciSetEDField (Ed, ED_DIR, ED_FROM_TD_DIR);
OhciSetEDField (Ed, ED_SPEED, HI_SPEED);
OhciSetEDField (Ed, ED_FORMAT | ED_HALTED | ED_DTTOGGLE, 0);
OhciSetEDField (Ed, ED_MAX_PACKET, MaxPacketLength);
OhciSetEDField (Ed, ED_PDATA, 0);
OhciSetEDField (Ed, ED_ZERO, 0);
OhciSetEDField (Ed, ED_TDHEAD_PTR, (UINT32) NULL);
OhciSetEDField (Ed, ED_TDTAIL_PTR, (UINT32) NULL);
OhciSetEDField (Ed, ED_NEXT_EDPTR, (UINT32) NULL);
OhciAttachEDToList (Ohc, BULK_LIST, Ed, NULL);
if(Data != NULL) {
MapLength = *DataLength;
MapPyhAddr = (EFI_PHYSICAL_ADDRESS)(UINTN)Data;
}
//
//Data Stage
//
LeftLength = MapLength;
ActualSendLength = MapLength;
CurrentToggle = *DataToggle;
HeadTd = NULL;
FirstTD = TRUE;
while (LeftLength > 0) {
ActualSendLength = LeftLength;
if (LeftLength > MaxPacketLength) {
ActualSendLength = MaxPacketLength;
}
DataTd = OhciCreateTD (Ohc);
if (DataTd == NULL) {
DEBUG ((EFI_D_INFO, "OhcBulkTransfer: Fail to allocate Data TD buffer\r\n"));
Status = EFI_OUT_OF_RESOURCES;
goto FREE_TD_BUFF;
}
OhciSetTDField (DataTd, TD_PDATA, 0);
OhciSetTDField (DataTd, TD_BUFFER_ROUND, 1);
OhciSetTDField (DataTd, TD_DIR_PID, DataPidDir);
OhciSetTDField (DataTd, TD_DELAY_INT, TD_NO_DELAY);
OhciSetTDField (DataTd, TD_DT_TOGGLE, CurrentToggle);
OhciSetTDField (DataTd, TD_ERROR_CNT, 0);
OhciSetTDField (DataTd, TD_COND_CODE, TD_TOBE_PROCESSED);
OhciSetTDField (DataTd, TD_CURR_BUFFER_PTR, (UINT32) MapPyhAddr);
OhciSetTDField (DataTd, TD_BUFFER_END_PTR, (UINT32) MapPyhAddr + ActualSendLength - 1);
OhciSetTDField (DataTd, TD_NEXT_PTR, (UINT32) NULL);
DataTd->ActualSendLength = ActualSendLength;
DataTd->DataBuffer = (UINT8 *)(UINTN)MapPyhAddr;
DataTd->NextTDPointer = 0;
if (FirstTD) {
HeadTd = DataTd;
FirstTD = FALSE;
} else {
OhciLinkTD (HeadTd, DataTd);
}
CurrentToggle ^= 1;
MapPyhAddr += ActualSendLength;
LeftLength -= ActualSendLength;
}
//
// Empty Stage
//
EmptyTd = OhciCreateTD (Ohc);
if (EmptyTd == NULL) {
Status = EFI_OUT_OF_RESOURCES;
DEBUG ((EFI_D_INFO, "OhcBulkTransfer: Fail to allocate Empty TD buffer\r\n"));
goto FREE_TD_BUFF;
}
OhciSetTDField (EmptyTd, TD_PDATA, 0);
OhciSetTDField (EmptyTd, TD_BUFFER_ROUND, 0);
OhciSetTDField (EmptyTd, TD_DIR_PID, 0);
OhciSetTDField (EmptyTd, TD_DELAY_INT, 0);
//OhciSetTDField (EmptyTd, TD_DT_TOGGLE, CurrentToggle);
EmptyTd->Word0.DataToggle = 0;
OhciSetTDField (EmptyTd, TD_ERROR_CNT, 0);
OhciSetTDField (EmptyTd, TD_COND_CODE, 0);
OhciSetTDField (EmptyTd, TD_CURR_BUFFER_PTR, 0);
OhciSetTDField (EmptyTd, TD_BUFFER_END_PTR, 0);
OhciSetTDField (EmptyTd, TD_NEXT_PTR, 0);
EmptyTd->ActualSendLength = 0;
EmptyTd->DataBuffer = NULL;
EmptyTd->NextTDPointer = NULL;
OhciLinkTD (HeadTd, EmptyTd);
Ed->TdTailPointer = EmptyTd;
OhciAttachTDListToED (Ed, HeadTd);
OhciSetEDField (Ed, ED_SKIP, 0);
OhciSetHcCommandStatus (Ohc, BULK_LIST_FILLED, 1);
OhciSetHcControl (Ohc, BULK_ENABLE, 1);
if (OhciGetHcControl (Ohc, BULK_ENABLE) != 1) {
MicroSecondDelay (HC_1_MILLISECOND);
if (OhciGetHcControl (Ohc, BULK_ENABLE) != 1) {
*TransferResult = EFI_USB_ERR_SYSTEM;
goto FREE_TD_BUFF;
}
}
TimeCount = 0;
Status = CheckIfDone (Ohc, BULK_LIST, Ed, HeadTd, &ErrorCode);
while (Status == EFI_NOT_READY && TimeCount <= TimeOut) {
MicroSecondDelay (HC_1_MILLISECOND);
TimeCount++;
Status = CheckIfDone (Ohc, BULK_LIST, Ed, HeadTd, &ErrorCode);
}
*TransferResult = ConvertErrorCode (ErrorCode);
if (ErrorCode != TD_NO_ERROR) {
if (ErrorCode == TD_TOBE_PROCESSED) {
DEBUG ((EFI_D_INFO, "Bulk pipe timeout, > %d mS\r\n", TimeOut));
} else {
DEBUG ((EFI_D_INFO, "Bulk pipe broken\r\n"));
}
*DataLength = 0;
}
*DataToggle = (UINT8) OhciGetEDField (Ed, ED_DTTOGGLE);
FREE_TD_BUFF:
while (HeadTd) {
DataTd = HeadTd;
HeadTd = HeadTd->NextTDPointer;
UsbHcFreeMem(Ohc->MemPool, DataTd, sizeof(TD_DESCRIPTOR));
}
UsbHcFreeMem(Ohc->MemPool, Ed, sizeof(ED_DESCRIPTOR));
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] NumOfPorts 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
OhciGetRootHubNumOfPorts (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_HOST_CONTROLLER_PPI *This,
OUT UINT8 *NumOfPorts
)
{
USB_OHCI_HC_DEV *Ohc;
if (NumOfPorts == NULL) {
return EFI_INVALID_PARAMETER;
}
Ohc = PEI_RECOVERY_USB_OHC_DEV_FROM_EHCI_THIS (This);
*NumOfPorts = (UINT8)OhciGetRootHubDescriptor(Ohc, RH_NUM_DS_PORTS);
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
OhciGetRootHubPortStatus (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_HOST_CONTROLLER_PPI *This,
IN UINT8 PortNumber,
OUT EFI_USB_PORT_STATUS *PortStatus
)
{
USB_OHCI_HC_DEV *Ohc;
UINT8 NumOfPorts;
Ohc = PEI_RECOVERY_USB_OHC_DEV_FROM_EHCI_THIS (This);
OhciGetRootHubNumOfPorts (PeiServices, This, &NumOfPorts);
if (PortNumber >= NumOfPorts) {
return EFI_INVALID_PARAMETER;
}
PortStatus->PortStatus = 0;
PortStatus->PortChangeStatus = 0;
if (OhciReadRootHubPortStatus (Ohc,PortNumber, RH_CURR_CONNECT_STAT)) {
PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION;
}
if (OhciReadRootHubPortStatus (Ohc,PortNumber, RH_PORT_ENABLE_STAT)) {
PortStatus->PortStatus |= USB_PORT_STAT_ENABLE;
}
if (OhciReadRootHubPortStatus (Ohc,PortNumber, RH_PORT_SUSPEND_STAT)) {
PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND;
}
if (OhciReadRootHubPortStatus (Ohc,PortNumber, RH_PORT_OC_INDICATOR)) {
PortStatus->PortStatus |= USB_PORT_STAT_OVERCURRENT;
}
if (OhciReadRootHubPortStatus (Ohc,PortNumber, RH_PORT_RESET_STAT)) {
PortStatus->PortStatus |= USB_PORT_STAT_RESET;
}
if (OhciReadRootHubPortStatus (Ohc,PortNumber, RH_PORT_POWER_STAT)) {
PortStatus->PortStatus |= USB_PORT_STAT_POWER;
}
if (OhciReadRootHubPortStatus (Ohc,PortNumber, RH_LSDEVICE_ATTACHED)) {
PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED;
}
if (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_ENABLE_STAT_CHANGE)) {
PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE;
}
if (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_CONNECT_STATUS_CHANGE)) {
PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION;
}
if (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_SUSPEND_STAT_CHANGE)) {
PortStatus->PortChangeStatus |= USB_PORT_STAT_C_SUSPEND;
}
if (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_OC_INDICATOR_CHANGE)) {
PortStatus->PortChangeStatus |= USB_PORT_STAT_C_OVERCURRENT;
}
if (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_RESET_STAT_CHANGE)) {
PortStatus->PortChangeStatus |= USB_PORT_STAT_C_RESET;
}
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
OhciSetRootHubPortFeature (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_HOST_CONTROLLER_PPI *This,
IN UINT8 PortNumber,
IN EFI_USB_PORT_FEATURE PortFeature
)
{
USB_OHCI_HC_DEV *Ohc;
EFI_STATUS Status;
UINT8 NumOfPorts;
UINTN RetryTimes;
OhciGetRootHubNumOfPorts (PeiServices, This, &NumOfPorts);
if (PortNumber >= NumOfPorts) {
return EFI_INVALID_PARAMETER;
}
Ohc = PEI_RECOVERY_USB_OHC_DEV_FROM_EHCI_THIS (This);
Status = EFI_SUCCESS;
switch (PortFeature) {
case EfiUsbPortPower:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_SET_PORT_POWER);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);
RetryTimes++;
} while (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_POWER_STAT) == 0 &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
break;
case EfiUsbPortReset:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_SET_PORT_RESET);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);
RetryTimes++;
} while ((OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_RESET_STAT_CHANGE) == 0 ||
OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_RESET_STAT) == 1) &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
OhciSetRootHubPortStatus (Ohc, PortNumber, RH_PORT_RESET_STAT_CHANGE);
break;
case EfiUsbPortEnable:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_SET_PORT_ENABLE);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);;
RetryTimes++;
} while (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_ENABLE_STAT) == 0 &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
break;
case EfiUsbPortSuspend:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_SET_PORT_SUSPEND);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);;
RetryTimes++;
} while (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_SUSPEND_STAT) == 0 &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
break;
default:
return EFI_INVALID_PARAMETER;
}
return Status;
}
/**
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
OhciClearRootHubPortFeature (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_HOST_CONTROLLER_PPI *This,
IN UINT8 PortNumber,
IN EFI_USB_PORT_FEATURE PortFeature
)
{
USB_OHCI_HC_DEV *Ohc;
EFI_STATUS Status;
UINT8 NumOfPorts;
UINTN RetryTimes;
OhciGetRootHubNumOfPorts (PeiServices, This, &NumOfPorts);
if (PortNumber >= NumOfPorts) {
return EFI_INVALID_PARAMETER;
}
Ohc = PEI_RECOVERY_USB_OHC_DEV_FROM_EHCI_THIS (This);
Status = EFI_SUCCESS;
switch (PortFeature) {
case EfiUsbPortEnable:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_CLEAR_PORT_ENABLE);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);
RetryTimes++;
} while (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_ENABLE_STAT) == 1 &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
break;
case EfiUsbPortSuspend:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_CLEAR_SUSPEND_STATUS);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);
RetryTimes++;
} while (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_SUSPEND_STAT) == 1 &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
break;
case EfiUsbPortReset:
break;
case EfiUsbPortPower:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_CLEAR_PORT_POWER);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);
RetryTimes++;
} while (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_POWER_STAT) == 1 &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
break;
case EfiUsbPortConnectChange:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_CONNECT_STATUS_CHANGE);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);
RetryTimes++;
} while (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_CONNECT_STATUS_CHANGE) == 1 &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
break;
case EfiUsbPortResetChange:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_PORT_RESET_STAT_CHANGE);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);
RetryTimes++;
} while (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_RESET_STAT_CHANGE) == 1 &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
break;
case EfiUsbPortEnableChange:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_PORT_ENABLE_STAT_CHANGE);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);
RetryTimes++;
} while (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_ENABLE_STAT_CHANGE) == 1 &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
break;
case EfiUsbPortSuspendChange:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_PORT_SUSPEND_STAT_CHANGE);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);
RetryTimes++;
} while (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_PORT_SUSPEND_STAT_CHANGE) == 1 &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
break;
case EfiUsbPortOverCurrentChange:
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_OC_INDICATOR_CHANGE);
//
// Verify the state
//
RetryTimes = 0;
do {
MicroSecondDelay (HC_1_MILLISECOND);
RetryTimes++;
} while (OhciReadRootHubPortStatus (Ohc, PortNumber, RH_OC_INDICATOR_CHANGE) == 1 &&
RetryTimes < MAX_RETRY_TIMES);
if (RetryTimes >= MAX_RETRY_TIMES) {
return EFI_DEVICE_ERROR;
}
break;
default:
return EFI_INVALID_PARAMETER;
}
return Status;
}
/**
Provides software reset for the USB host controller.
@param This This EFI_USB_HC_PROTOCOL instance.
@param Attributes A bit mask of the reset operation to perform.
@retval EFI_SUCCESS The reset operation succeeded.
@retval EFI_INVALID_PARAMETER Attributes is not valid.
@retval EFI_UNSUPPOURTED The type of reset specified by Attributes is
not currently supported by the host controller.
@retval EFI_DEVICE_ERROR Host controller isn't halted to reset.
**/
EFI_STATUS
InitializeUsbHC (
IN EFI_PEI_SERVICES **PeiServices,
IN USB_OHCI_HC_DEV *Ohc,
IN UINT16 Attributes
)
{
EFI_STATUS Status;
UINT8 Index;
UINT8 NumOfPorts;
UINT32 PowerOnGoodTime;
UINT32 Data32;
BOOLEAN Flag = FALSE;
if ((Attributes & ~(EFI_USB_HC_RESET_GLOBAL | EFI_USB_HC_RESET_HOST_CONTROLLER)) != 0) {
return EFI_INVALID_PARAMETER;
}
Status = EFI_SUCCESS;
if ((Attributes & EFI_USB_HC_RESET_HOST_CONTROLLER) != 0) {
MicroSecondDelay (50 * HC_1_MILLISECOND);
Status = OhciSetHcCommandStatus (Ohc, HC_RESET, HC_RESET);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
MicroSecondDelay (50 * HC_1_MILLISECOND);
//
// Wait for host controller reset.
//
PowerOnGoodTime = 50;
do {
MicroSecondDelay (HC_1_MILLISECOND);
Data32 = OhciGetOperationalReg (Ohc, HC_COMMAND_STATUS );
if ((Data32 & HC_RESET) == 0) {
Flag = TRUE;
break;
}
}while(PowerOnGoodTime--);
if (!Flag){
return EFI_DEVICE_ERROR;
}
}
OhciSetFrameInterval (Ohc, FRAME_INTERVAL, 0x2edf);
if ((Attributes & EFI_USB_HC_RESET_GLOBAL) != 0) {
Status = OhciSetHcControl (Ohc, HC_FUNCTIONAL_STATE, HC_STATE_RESET);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
MicroSecondDelay (50 * HC_1_MILLISECOND);
}
//
// Initialize host controller operational registers
//
OhciSetFrameInterval (Ohc, FS_LARGEST_DATA_PACKET, 0x2778);
OhciSetFrameInterval (Ohc, FRAME_INTERVAL, 0x2edf);
OhciSetPeriodicStart (Ohc, 0x2a2f);
OhciSetHcControl (Ohc, CONTROL_BULK_RATIO, 0x0);
OhciSetHcCommandStatus (Ohc, CONTROL_LIST_FILLED | BULK_LIST_FILLED, 0);
OhciSetRootHubDescriptor (Ohc, RH_PSWITCH_MODE, 0);
OhciSetRootHubDescriptor (Ohc, RH_NO_PSWITCH | RH_NOC_PROT, 1);
//OhciSetRootHubDescriptor (Hc, RH_PSWITCH_MODE | RH_NO_PSWITCH, 0);
//OhciSetRootHubDescriptor (Hc, RH_PSWITCH_MODE | RH_NOC_PROT, 1);
OhciSetRootHubDescriptor (Ohc, RH_DEV_REMOVABLE, 0);
OhciSetRootHubDescriptor (Ohc, RH_PORT_PWR_CTRL_MASK, 0xffff);
OhciSetRootHubStatus (Ohc, RH_LOCAL_PSTAT_CHANGE);
OhciSetRootHubPortStatus (Ohc, 0, RH_SET_PORT_POWER);
OhciGetRootHubNumOfPorts (PeiServices, &Ohc->UsbHostControllerPpi, &NumOfPorts);
for (Index = 0; Index < NumOfPorts; Index++) {
if (!EFI_ERROR (OhciSetRootHubPortFeature (PeiServices, &Ohc->UsbHostControllerPpi, Index, EfiUsbPortReset))) {
MicroSecondDelay (200 * HC_1_MILLISECOND);
OhciClearRootHubPortFeature (PeiServices, &Ohc->UsbHostControllerPpi, Index, EfiUsbPortReset);
MicroSecondDelay (HC_1_MILLISECOND);
OhciSetRootHubPortFeature (PeiServices, &Ohc->UsbHostControllerPpi, Index, EfiUsbPortEnable);
MicroSecondDelay (HC_1_MILLISECOND);
}
}
Ohc->MemPool = UsbHcInitMemPool(TRUE, 0);
if(Ohc->MemPool == NULL) {
return EFI_OUT_OF_RESOURCES;
}
OhciSetMemoryPointer (Ohc, HC_CONTROL_HEAD, NULL);
OhciSetMemoryPointer (Ohc, HC_BULK_HEAD, NULL);
OhciSetHcControl (Ohc, CONTROL_ENABLE | BULK_ENABLE, 1);
OhciSetHcControl (Ohc, HC_FUNCTIONAL_STATE, HC_STATE_OPERATIONAL);
MicroSecondDelay (50 * HC_1_MILLISECOND);
//
// Wait till first SOF occurs, and then clear it
//
while (OhciGetHcInterruptStatus (Ohc, START_OF_FRAME) == 0);
OhciClearInterruptStatus (Ohc, START_OF_FRAME);
MicroSecondDelay (HC_1_MILLISECOND);
return EFI_SUCCESS;
}
/**
Submits control transfer to a target USB device.
Calls underlying OhciControlTransfer to do work. This wrapper routine required
on Quark so that USB DMA transfers do not cause an IMR violation.
@param PeiServices The pointer of EFI_PEI_SERVICES.
@param This The pointer of PEI_USB_HOST_CONTROLLER_PPI.
@param DeviceAddress The target device address.
@param DeviceSpeed Target device speed.
@param MaximumPacketLength Maximum packet size the default control transfer
endpoint is capable of sending or receiving.
@param Request USB device request to send.
@param TransferDirection Specifies the data direction for the data stage.
@param Data Data buffer to be transmitted or received from USB device.
@param DataLength The size (in bytes) of the data buffer.
@param TimeOut Indicates the maximum timeout, in millisecond.
@param TransferResult Return the result of this control transfer.
@retval EFI_SUCCESS Transfer was completed successfully.
@retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resources.
@retval EFI_INVALID_PARAMETER Some parameters are invalid.
@retval EFI_TIMEOUT Transfer failed due to timeout.
@retval EFI_DEVICE_ERROR Transfer failed due to host controller or device error.
**/
EFI_STATUS
EFIAPI
RedirectOhciControlTransfer (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_HOST_CONTROLLER_PPI *This,
IN UINT8 DeviceAddress,
IN UINT8 DeviceSpeed,
IN UINT8 MaxPacketLength,
IN EFI_USB_DEVICE_REQUEST *Request,
IN EFI_USB_DATA_DIRECTION TransferDirection,
IN OUT VOID *Data,
IN OUT UINTN *DataLength,
IN UINTN TimeOut,
OUT UINT32 *TransferResult
)
{
EFI_STATUS Status;
EFI_USB_DEVICE_REQUEST *NewRequest;
VOID *NewData;
UINT8 *Alloc;
//
// Allocate memory external to IMR protected region for transfer data.
//
Status = PeiServicesAllocatePool (
sizeof(EFI_USB_DEVICE_REQUEST) + *DataLength,
(VOID **) &Alloc
);
ASSERT_EFI_ERROR (Status);
//
// Setup pointers to transfer buffers.
//
NewRequest = (EFI_USB_DEVICE_REQUEST *) Alloc;
Alloc += sizeof(EFI_USB_DEVICE_REQUEST);
NewData = (VOID *) Alloc;
//
// Copy callers request packet into transfer request packet.
//
if (Request != NULL) {
CopyMem (NewRequest,Request,sizeof(EFI_USB_DEVICE_REQUEST));
} else {
NewRequest = NULL;
}
//
// Copy callers data into transfer data buffer.
//
if (Data != NULL) {
if (DataLength > 0) {
CopyMem (NewData,Data,*DataLength);
}
} else {
NewData = NULL;
}
//
// Call underlying OhciControlTransfer to do work.
//
Status = OhciControlTransfer (
PeiServices,
This,
DeviceAddress,
DeviceSpeed,
MaxPacketLength,
NewRequest,
TransferDirection,
NewData,
DataLength,
TimeOut,
TransferResult
);
//
// Copy transfer buffer back into callers buffer.
//
if (Data != NULL && *DataLength > 0) {
CopyMem (Data, NewData, *DataLength);
}
return Status;
}
/**
Submits bulk transfer to a bulk endpoint of a USB device.
Calls underlying OhciBulkTransfer to do work. This wrapper routine required
on Quark so that USB DMA transfers do not cause an IMR violation.
@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 MaxiPacketLength Maximum packet size the endpoint is capable of
sending or receiving.
@param Data A pointers to the buffers of data to transmit
from or receive into.
@param DataLength The lenght of the data buffer.
@param DataToggle On input, the initial data toggle for the transfer;
On output, it is updated to to next data toggle to use of
the subsequent bulk transfer.
@param TimeOut Indicates the maximum time, in millisecond, which the
transfer is allowed to complete.
@param TransferResult A pointer to the detailed result information of the
bulk transfer.
@retval EFI_SUCCESS The transfer was completed successfully.
@retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource.
@retval EFI_INVALID_PARAMETER Parameters are invalid.
@retval EFI_TIMEOUT The transfer failed due to timeout.
@retval EFI_DEVICE_ERROR The transfer failed due to host controller error.
**/
EFI_STATUS
EFIAPI
RedirectOhciBulkTransfer (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_HOST_CONTROLLER_PPI *This,
IN UINT8 DeviceAddress,
IN UINT8 EndPointAddress,
IN UINT8 MaxPacketLength,
IN OUT VOID *Data,
IN OUT UINTN *DataLength,
IN OUT UINT8 *DataToggle,
IN UINTN TimeOut,
OUT UINT32 *TransferResult
)
{
EFI_STATUS Status;
UINT8 *NewData;
//
// Allocate memory external to IMR protected region for transfer data.
//
Status = PeiServicesAllocatePool (
*DataLength,
(VOID **) &NewData
);
ASSERT_EFI_ERROR (Status);
//
// Copy callers data into transfer buffer.
//
if (Data != NULL) {
if (DataLength > 0) {
CopyMem (NewData,Data,*DataLength);
}
} else {
NewData = NULL;
}
//
// Call underlying OhciBulkTransfer to do work.
//
Status = OhciBulkTransfer (
PeiServices,
This,
DeviceAddress,
EndPointAddress,
MaxPacketLength,
NewData,
DataLength,
DataToggle,
TimeOut,
TransferResult
);
//
// Copy transfer buffer back into callers buffer.
//
if (Data != NULL && *DataLength > 0) {
CopyMem (Data, NewData, *DataLength);
}
return Status;
}
/**
@param FileHandle Handle of the file being invoked.
@param PeiServices Describes the list of possible PEI Services.
@retval EFI_SUCCESS PPI successfully installed.
**/
EFI_STATUS
OhcPeimEntry (
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_OHCI_HC_DEV *Ohc;
EFI_PHYSICAL_ADDRESS TempPtr;
//
// Shadow this PEIM to run from memory
//
if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) {
return EFI_SUCCESS;
}
Status = PeiServicesLocatePpi (
&gPeiUsbControllerPpiGuid,
0,
NULL,
(VOID **) &ChipSetUsbControllerPpi
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
Index = 0;
while (TRUE) {
Status = ChipSetUsbControllerPpi->GetUsbController (
(EFI_PEI_SERVICES **) PeiServices,
ChipSetUsbControllerPpi,
Index,
&ControllerType,
&BaseAddress
);
//
// When status is error, meant no controller is found
//
if (EFI_ERROR (Status)) {
break;
}
//
// This PEIM is for OHC type controller.
//
if (ControllerType != PEI_OHCI_CONTROLLER) {
Index++;
continue;
}
MemPages = sizeof (USB_OHCI_HC_DEV) / PAGESIZE + 1;
Status = PeiServicesAllocatePages (
EfiBootServicesCode,
MemPages,
&TempPtr
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_INFO, "OhcPeimEntry: Fail to allocate buffer for the %dth OHCI ControllerPpi\n", Index));
return EFI_OUT_OF_RESOURCES;
}
ZeroMem((VOID *)(UINTN)TempPtr, MemPages*PAGESIZE);
Ohc = (USB_OHCI_HC_DEV *) ((UINTN) TempPtr);
Ohc->Signature = USB_OHCI_HC_DEV_SIGNATURE;
Ohc->UsbHostControllerBaseAddress = (UINT32) BaseAddress;
//
// Initialize Uhc's hardware
//
Status = InitializeUsbHC (
(EFI_PEI_SERVICES **)PeiServices,
Ohc,
EFI_USB_HC_RESET_GLOBAL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_INFO, "OhcPeimEntry: Fail to init %dth OHCI ControllerPpi\n", Index));
return Status;
}
//
// Control & Bulk transfer services are accessed via their Redirect
// routine versions on Quark so that USB DMA transfers do not cause an
// IMR violation.
//
Ohc->UsbHostControllerPpi.ControlTransfer = RedirectOhciControlTransfer;
Ohc->UsbHostControllerPpi.BulkTransfer = RedirectOhciBulkTransfer;
Ohc->UsbHostControllerPpi.GetRootHubPortNumber = OhciGetRootHubNumOfPorts;
Ohc->UsbHostControllerPpi.GetRootHubPortStatus = OhciGetRootHubPortStatus;
Ohc->UsbHostControllerPpi.SetRootHubPortFeature = OhciSetRootHubPortFeature;
Ohc->UsbHostControllerPpi.ClearRootHubPortFeature = OhciClearRootHubPortFeature;
Ohc->PpiDescriptor.Flags = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST);
Ohc->PpiDescriptor.Guid = &gPeiUsbHostControllerPpiGuid;
Ohc->PpiDescriptor.Ppi = &Ohc->UsbHostControllerPpi;
Status = PeiServicesInstallPpi (&Ohc->PpiDescriptor);
if (EFI_ERROR (Status)) {
Index++;
continue;
}
Index++;
}
return EFI_SUCCESS;
}