mirror of https://github.com/acidanthera/audk.git
1393 lines
47 KiB
C
1393 lines
47 KiB
C
/** @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;
|
|
}
|
|
|