mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-24 11:45:27 +01:00
7c0aa811ec
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
2519 lines
85 KiB
C
2519 lines
85 KiB
C
/** @file
|
|
This file contains the implementation of Usb Hc Protocol.
|
|
|
|
Copyright(c) 2013 Intel Corporation. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in
|
|
the documentation and/or other materials provided with the
|
|
distribution.
|
|
* Neither the name of Intel Corporation nor the names of its
|
|
contributors may be used to endorse or promote products derived
|
|
from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**/
|
|
|
|
|
|
#include "Ohci.h"
|
|
|
|
/**
|
|
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
|
|
EFIAPI
|
|
OhciReset (
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
IN UINT16 Attributes
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
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;
|
|
Ohc = USB_OHCI_HC_DEV_FROM_THIS (This);
|
|
|
|
if ((Attributes & EFI_USB_HC_RESET_HOST_CONTROLLER) != 0) {
|
|
gBS->Stall (50 * 1000);
|
|
Status = OhciSetHcCommandStatus (Ohc, HC_RESET, HC_RESET);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
gBS->Stall (50 * 1000);
|
|
//
|
|
// Wait for host controller reset.
|
|
//
|
|
PowerOnGoodTime = 50;
|
|
do {
|
|
gBS->Stall (1 * 1000);
|
|
Data32 = OhciGetOperationalReg (Ohc->PciIo, HC_COMMAND_STATUS );
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
if ((Data32 & HC_RESET) == 0) {
|
|
Flag = TRUE;
|
|
break;
|
|
}
|
|
}while(PowerOnGoodTime--);
|
|
if (!Flag){
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
}
|
|
OhciFreeIntTransferMemory (Ohc);
|
|
Status = OhciInitializeInterruptList (Ohc);
|
|
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;
|
|
}
|
|
gBS->Stall (50 * 1000);
|
|
}
|
|
//
|
|
// 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, 0x3);
|
|
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 (This, &NumOfPorts);
|
|
for (Index = 0; Index < NumOfPorts; Index++) {
|
|
if (!EFI_ERROR (OhciSetRootHubPortFeature (This, Index, EfiUsbPortReset))) {
|
|
gBS->Stall (200 * 1000);
|
|
OhciClearRootHubPortFeature (This, Index, EfiUsbPortReset);
|
|
gBS->Stall (1000);
|
|
OhciSetRootHubPortFeature (This, Index, EfiUsbPortEnable);
|
|
gBS->Stall (1000);
|
|
}
|
|
}
|
|
OhciSetMemoryPointer (Ohc, HC_HCCA, Ohc->HccaMemoryBlock);
|
|
OhciSetMemoryPointer (Ohc, HC_CONTROL_HEAD, NULL);
|
|
OhciSetMemoryPointer (Ohc, HC_BULK_HEAD, NULL);
|
|
OhciSetHcControl (Ohc, PERIODIC_ENABLE | CONTROL_ENABLE | BULK_ENABLE, 1); /*ISOCHRONOUS_ENABLE*/
|
|
OhciSetHcControl (Ohc, HC_FUNCTIONAL_STATE, HC_STATE_OPERATIONAL);
|
|
gBS->Stall (50*1000);
|
|
//
|
|
// Wait till first SOF occurs, and then clear it
|
|
//
|
|
while (OhciGetHcInterruptStatus (Ohc, START_OF_FRAME) == 0);
|
|
OhciClearInterruptStatus (Ohc, START_OF_FRAME);
|
|
gBS->Stall (1000);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Retrieve the current state of the USB host controller.
|
|
|
|
@param This This EFI_USB_HC_PROTOCOL instance.
|
|
@param State Variable to return the current host controller
|
|
state.
|
|
|
|
@retval EFI_SUCCESS Host controller state was returned in State.
|
|
@retval EFI_INVALID_PARAMETER State is NULL.
|
|
@retval EFI_DEVICE_ERROR An error was encountered while attempting to
|
|
retrieve the host controller's current state.
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciGetState (
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
OUT EFI_USB_HC_STATE *State
|
|
)
|
|
{
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
UINT32 FuncState;
|
|
|
|
if (State == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Ohc = USB_OHCI_HC_DEV_FROM_THIS (This);
|
|
|
|
FuncState = OhciGetHcControl (Ohc, HC_FUNCTIONAL_STATE);
|
|
|
|
switch (FuncState) {
|
|
case HC_STATE_RESET:
|
|
case HC_STATE_RESUME:
|
|
*State = EfiUsbHcStateHalt;
|
|
break;
|
|
|
|
case HC_STATE_OPERATIONAL:
|
|
*State = EfiUsbHcStateOperational;
|
|
break;
|
|
|
|
case HC_STATE_SUSPEND:
|
|
*State = EfiUsbHcStateSuspend;
|
|
break;
|
|
|
|
default:
|
|
ASSERT (FALSE);
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Sets the USB host controller to a specific state.
|
|
|
|
@param This This EFI_USB_HC_PROTOCOL instance.
|
|
@param State The state of the host controller that will be set.
|
|
|
|
@retval EFI_SUCCESS The USB host controller was successfully placed
|
|
in the state specified by State.
|
|
@retval EFI_INVALID_PARAMETER State is invalid.
|
|
@retval EFI_DEVICE_ERROR Failed to set the state due to device error.
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciSetState(
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
IN EFI_USB_HC_STATE State
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
|
|
Ohc = USB_OHCI_HC_DEV_FROM_THIS(This);
|
|
|
|
switch (State) {
|
|
case EfiUsbHcStateHalt:
|
|
Status = OhciSetHcControl (Ohc, HC_FUNCTIONAL_STATE, HC_STATE_RESET);
|
|
break;
|
|
|
|
case EfiUsbHcStateOperational:
|
|
Status = OhciSetHcControl (Ohc, HC_FUNCTIONAL_STATE, HC_STATE_OPERATIONAL);
|
|
break;
|
|
|
|
case EfiUsbHcStateSuspend:
|
|
Status = OhciSetHcControl (Ohc, HC_FUNCTIONAL_STATE, HC_STATE_SUSPEND);
|
|
break;
|
|
|
|
default:
|
|
Status = EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
gBS->Stall (1000);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
Submits control transfer to a target USB device.
|
|
|
|
@param This A pointer to the EFI_USB_HC_PROTOCOL instance.
|
|
@param DeviceAddress Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
@param IsSlowDevice Indicates whether the target device is slow device
|
|
or full-speed device.
|
|
@param MaxPaketLength Indicates the maximum packet size that the
|
|
default control transfer endpoint is capable of
|
|
sending or receiving.
|
|
@param Request A pointer to the USB device request that will be sent
|
|
to the USB device.
|
|
@param TransferDirection Specifies the data direction for the transfer.
|
|
There are three values available, DataIn, DataOut
|
|
and NoData.
|
|
@param Data A pointer to the buffer of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
@param DataLength Indicates the size, in bytes, of the data buffer
|
|
specified by Data.
|
|
@param TimeOut Indicates the maximum time, in microseconds,
|
|
which the transfer is allowed to complete.
|
|
@param TransferResult A pointer to the detailed result information generated
|
|
by this control transfer.
|
|
|
|
@retval EFI_SUCCESS The control transfer was completed successfully.
|
|
@retval EFI_OUT_OF_RESOURCES The control transfer could not be completed due to a lack of resources.
|
|
@retval EFI_INVALID_PARAMETER Some parameters are invalid.
|
|
@retval EFI_TIMEOUT The control transfer failed due to timeout.
|
|
@retval EFI_DEVICE_ERROR The control transfer failed due to host controller or device error.
|
|
Caller should check TranferResult for detailed error information.
|
|
|
|
--*/
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciControlTransfer (
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
IN UINT8 DeviceAddress,
|
|
IN BOOLEAN IsSlowDevice,
|
|
IN UINT8 MaxPacketLength,
|
|
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_OHCI_HC_DEV *Ohc;
|
|
ED_DESCRIPTOR *HeadEd;
|
|
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;
|
|
OHCI_ED_RESULT EdResult;
|
|
|
|
EFI_PCI_IO_PROTOCOL_OPERATION MapOp;
|
|
|
|
UINTN ActualSendLength;
|
|
UINTN LeftLength;
|
|
UINT8 DataToggle;
|
|
|
|
VOID *ReqMapping = NULL;
|
|
UINTN ReqMapLength = 0;
|
|
EFI_PHYSICAL_ADDRESS ReqMapPhyAddr = 0;
|
|
|
|
VOID *DataMapping = NULL;
|
|
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)) ||
|
|
(IsSlowDevice && MaxPacketLength != 8) ||
|
|
(MaxPacketLength != 8 && MaxPacketLength != 16 &&
|
|
MaxPacketLength != 32 && MaxPacketLength != 64)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (*DataLength > MAX_BYTES_PER_TD) {
|
|
DEBUG ((EFI_D_ERROR, "OhciControlTransfer: Request data size is too large\r\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Ohc = USB_OHCI_HC_DEV_FROM_THIS(This);
|
|
|
|
if (TransferDirection == EfiUsbDataIn) {
|
|
DataPidDir = TD_IN_PID;
|
|
StatusPidDir = TD_OUT_PID;
|
|
} else {
|
|
DataPidDir = TD_OUT_PID;
|
|
StatusPidDir = TD_IN_PID;
|
|
}
|
|
|
|
Status = OhciSetHcControl (Ohc, CONTROL_ENABLE, 0);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: fail to disable CONTROL_ENABLE\r\n"));
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
Status = OhciSetHcCommandStatus (Ohc, CONTROL_LIST_FILLED, 0);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: fail to disable CONTROL_LIST_FILLED\r\n"));
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
gBS->Stall(20 * 1000);
|
|
|
|
OhciSetMemoryPointer (Ohc, HC_CONTROL_HEAD, NULL);
|
|
Ed = OhciCreateED (Ohc);
|
|
if (Ed == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate ED buffer\r\n"));
|
|
goto CTRL_EXIT;
|
|
}
|
|
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, IsSlowDevice);
|
|
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, 0);
|
|
OhciSetEDField (Ed, ED_TDTAIL_PTR, 0);
|
|
OhciSetEDField (Ed, ED_NEXT_EDPTR, 0);
|
|
HeadEd = OhciAttachEDToList (Ohc, CONTROL_LIST, Ed, NULL);
|
|
//
|
|
// Setup Stage
|
|
//
|
|
if(Request != NULL) {
|
|
ReqMapLength = sizeof(EFI_USB_DEVICE_REQUEST);
|
|
MapOp = EfiPciIoOperationBusMasterRead;
|
|
Status = Ohc->PciIo->Map (Ohc->PciIo, MapOp, (UINT8 *)Request, &ReqMapLength, &ReqMapPhyAddr, &ReqMapping);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to Map Request Buffer\r\n"));
|
|
goto FREE_ED_BUFF;
|
|
}
|
|
}
|
|
SetupTd = OhciCreateTD (Ohc);
|
|
if (SetupTd == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate Setup TD buffer\r\n"));
|
|
goto UNMAP_SETUP_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, (UINT32)ReqMapPhyAddr);
|
|
OhciSetTDField (SetupTd, TD_NEXT_PTR, 0);
|
|
OhciSetTDField (SetupTd, TD_BUFFER_END_PTR, (UINT32)(ReqMapPhyAddr + sizeof (EFI_USB_DEVICE_REQUEST) - 1));
|
|
SetupTd->ActualSendLength = sizeof (EFI_USB_DEVICE_REQUEST);
|
|
SetupTd->DataBuffer = (UINT32)(UINTN)ReqMapPhyAddr;
|
|
SetupTd->NextTDPointer = 0;
|
|
|
|
if (TransferDirection == EfiUsbDataIn) {
|
|
MapOp = EfiPciIoOperationBusMasterWrite;
|
|
} else {
|
|
MapOp = EfiPciIoOperationBusMasterRead;
|
|
}
|
|
DataMapLength = *DataLength;
|
|
if ((Data != NULL) && (DataMapLength != 0)) {
|
|
Status = Ohc->PciIo->Map (Ohc->PciIo, MapOp, Data, &DataMapLength, &DataMapPhyAddr, &DataMapping);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail To Map Data Buffer\r\n"));
|
|
goto FREE_TD_BUFF;
|
|
}
|
|
}
|
|
//
|
|
//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 buffer for Data Stage TD\r\n"));
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto UNMAP_DATA_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, 0);
|
|
DataTd->ActualSendLength = (UINT32)ActualSendLength;
|
|
DataTd->DataBuffer = (UINT32)(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 buffer for Status Stage TD\r\n"));
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto UNMAP_DATA_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, 0);
|
|
OhciSetTDField (StatusTd, TD_NEXT_PTR, 0);
|
|
OhciSetTDField (StatusTd, TD_BUFFER_END_PTR, 0);
|
|
StatusTd->ActualSendLength = 0;
|
|
StatusTd->DataBuffer = 0;
|
|
StatusTd->NextTDPointer = 0;
|
|
OhciLinkTD (HeadTd, StatusTd);
|
|
//
|
|
// Empty Stage
|
|
//
|
|
EmptyTd = OhciCreateTD (Ohc);
|
|
if (EmptyTd == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto UNMAP_DATA_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 = 0;
|
|
EmptyTd->NextTDPointer = 0;
|
|
OhciLinkTD (HeadTd, EmptyTd);
|
|
Ed->TdTailPointer = (UINT32)(UINTN)EmptyTd;
|
|
OhciAttachTDListToED (Ed, HeadTd);
|
|
//
|
|
// For debugging, dump ED & TD buffer befor transferring
|
|
//
|
|
//
|
|
//OhciDumpEdTdInfo (Ohc, Ed, HeadTd, TRUE);
|
|
//
|
|
OhciSetEDField (Ed, ED_SKIP, 0);
|
|
Status = OhciSetHcControl (Ohc, CONTROL_ENABLE, 1);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: fail to enable CONTROL_ENABLE\r\n"));
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto UNMAP_DATA_BUFF;
|
|
}
|
|
Status = OhciSetHcCommandStatus (Ohc, CONTROL_LIST_FILLED, 1);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: fail to enable CONTROL_LIST_FILLED\r\n"));
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto UNMAP_DATA_BUFF;
|
|
}
|
|
gBS->Stall(20 * 1000);
|
|
|
|
|
|
TimeCount = 0;
|
|
Status = CheckIfDone (Ohc, CONTROL_LIST, Ed, HeadTd, &EdResult);
|
|
|
|
while (Status == EFI_NOT_READY && TimeCount <= TimeOut) {
|
|
gBS->Stall (1000);
|
|
TimeCount++;
|
|
Status = CheckIfDone (Ohc, CONTROL_LIST, Ed, HeadTd, &EdResult);
|
|
}
|
|
//
|
|
// For debugging, dump ED & TD buffer after transferring
|
|
//
|
|
//OhciDumpEdTdInfo (Ohc, Ed, HeadTd, FALSE);
|
|
//
|
|
*TransferResult = ConvertErrorCode (EdResult.ErrorCode);
|
|
|
|
if (EdResult.ErrorCode != TD_NO_ERROR) {
|
|
if (EdResult.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;
|
|
} else {
|
|
DEBUG ((EFI_D_INFO, "Control transfer successed\r\n"));
|
|
}
|
|
|
|
UNMAP_DATA_BUFF:
|
|
OhciSetEDField (Ed, ED_SKIP, 1);
|
|
if (HeadEd == Ed) {
|
|
OhciSetMemoryPointer (Ohc, HC_CONTROL_HEAD, NULL);
|
|
} else {
|
|
HeadEd->NextED = Ed->NextED;
|
|
}
|
|
if(DataMapping != NULL) {
|
|
Ohc->PciIo->Unmap(Ohc->PciIo, DataMapping);
|
|
}
|
|
|
|
FREE_TD_BUFF:
|
|
while (HeadTd) {
|
|
DataTd = HeadTd;
|
|
HeadTd = (TD_DESCRIPTOR *)(UINTN)(HeadTd->NextTDPointer);
|
|
UsbHcFreeMem(Ohc->MemPool, DataTd, sizeof(TD_DESCRIPTOR));
|
|
}
|
|
|
|
UNMAP_SETUP_BUFF:
|
|
if(ReqMapping != NULL) {
|
|
Ohc->PciIo->Unmap(Ohc->PciIo, ReqMapping);
|
|
}
|
|
|
|
FREE_ED_BUFF:
|
|
UsbHcFreeMem(Ohc->MemPool, Ed, sizeof(ED_DESCRIPTOR));
|
|
|
|
CTRL_EXIT:
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
Submits bulk transfer to a bulk endpoint of a USB device.
|
|
|
|
@param This A pointer to the EFI_USB_HC_PROTOCOL instance.
|
|
@param DeviceAddress Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
@param EndPointAddress The combination of an endpoint number and an
|
|
endpoint direction of the target USB device.
|
|
Each endpoint address supports data transfer in
|
|
one direction except the control endpoint
|
|
(whose default endpoint address is 0).
|
|
It is the caller's responsibility to make sure that
|
|
the EndPointAddress represents a bulk endpoint.
|
|
@param MaximumPacketLength Indicates the maximum packet size the target endpoint
|
|
is capable of sending or receiving.
|
|
@param Data A pointer to the buffer of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
@param DataLength When input, indicates the size, in bytes, of the data buffer
|
|
specified by Data. When output, indicates the actually
|
|
transferred data size.
|
|
@param DataToggle A pointer to the data toggle value. On input, it indicates
|
|
the initial data toggle value the bulk transfer should adopt;
|
|
on output, it is updated to indicate the data toggle value
|
|
of the subsequent bulk transfer.
|
|
@param TimeOut Indicates the maximum time, in microseconds, which the
|
|
transfer is allowed to complete.
|
|
TransferResult A pointer to the detailed result information of the
|
|
bulk transfer.
|
|
|
|
@retval EFI_SUCCESS The bulk transfer was completed successfully.
|
|
@retval EFI_OUT_OF_RESOURCES The bulk transfer could not be submitted due to lack of resource.
|
|
@retval EFI_INVALID_PARAMETER Some parameters are invalid.
|
|
@retval EFI_TIMEOUT The bulk transfer failed due to timeout.
|
|
@retval EFI_DEVICE_ERROR The bulk transfer failed due to host controller or device error.
|
|
Caller should check TranferResult for detailed error information.
|
|
|
|
**/
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciBulkTransfer(
|
|
IN EFI_USB_HC_PROTOCOL *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 *HeadEd;
|
|
ED_DESCRIPTOR *Ed;
|
|
UINT8 EdDir;
|
|
UINT32 DataPidDir;
|
|
TD_DESCRIPTOR *HeadTd;
|
|
TD_DESCRIPTOR *DataTd;
|
|
TD_DESCRIPTOR *EmptyTd;
|
|
EFI_STATUS Status;
|
|
EFI_USB_DATA_DIRECTION TransferDirection;
|
|
UINT8 EndPointNum;
|
|
UINTN TimeCount;
|
|
OHCI_ED_RESULT EdResult;
|
|
|
|
EFI_PCI_IO_PROTOCOL_OPERATION MapOp;
|
|
VOID *Mapping;
|
|
UINTN MapLength;
|
|
EFI_PHYSICAL_ADDRESS MapPyhAddr;
|
|
UINTN LeftLength;
|
|
UINTN ActualSendLength;
|
|
BOOLEAN FirstTD;
|
|
|
|
Mapping = NULL;
|
|
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 = USB_OHCI_HC_DEV_FROM_THIS (This);
|
|
|
|
if ((EndPointAddress & 0x80) != 0) {
|
|
TransferDirection = EfiUsbDataIn;
|
|
EdDir = ED_IN_DIR;
|
|
DataPidDir = TD_IN_PID;
|
|
MapOp = EfiPciIoOperationBusMasterWrite;
|
|
} else {
|
|
TransferDirection = EfiUsbDataOut;
|
|
EdDir = ED_OUT_DIR;
|
|
DataPidDir = TD_OUT_PID;
|
|
MapOp = EfiPciIoOperationBusMasterRead;
|
|
}
|
|
|
|
EndPointNum = (EndPointAddress & 0xF);
|
|
EdResult.NextToggle = *DataToggle;
|
|
|
|
Status = OhciSetHcControl (Ohc, BULK_ENABLE, 0);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: fail to disable BULK_ENABLE\r\n"));
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
Status = OhciSetHcCommandStatus (Ohc, BULK_LIST_FILLED, 0);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: fail to disable BULK_LIST_FILLED\r\n"));
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
gBS->Stall(20 * 1000);
|
|
|
|
OhciSetMemoryPointer (Ohc, HC_BULK_HEAD, NULL);
|
|
|
|
Ed = OhciCreateED (Ohc);
|
|
if (Ed == NULL) {
|
|
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, 0);
|
|
OhciSetEDField (Ed, ED_TDTAIL_PTR, 0);
|
|
OhciSetEDField (Ed, ED_NEXT_EDPTR, 0);
|
|
HeadEd = OhciAttachEDToList (Ohc, BULK_LIST, Ed, NULL);
|
|
|
|
if(Data != NULL) {
|
|
MapLength = *DataLength;
|
|
Status = Ohc->PciIo->Map (Ohc->PciIo, MapOp, (UINT8 *)Data, &MapLength, &MapPyhAddr, &Mapping);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((EFI_D_INFO, "OhciBulkTransfer: Fail to Map Data Buffer for Bulk\r\n"));
|
|
goto FREE_ED_BUFF;
|
|
}
|
|
}
|
|
//
|
|
//Data Stage
|
|
//
|
|
LeftLength = MapLength;
|
|
ActualSendLength = MapLength;
|
|
HeadTd = NULL;
|
|
FirstTD = TRUE;
|
|
while (LeftLength > 0) {
|
|
ActualSendLength = LeftLength;
|
|
if (LeftLength > MaxPacketLength) {
|
|
ActualSendLength = MaxPacketLength;
|
|
}
|
|
DataTd = OhciCreateTD (Ohc);
|
|
if (DataTd == NULL) {
|
|
DEBUG ((EFI_D_INFO, "OhciBulkTransfer: Fail to allocate buffer for Data Stage TD\r\n"));
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FREE_OHCI_TDBUFF;
|
|
}
|
|
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) MapPyhAddr);
|
|
OhciSetTDField (DataTd, TD_BUFFER_END_PTR, (UINT32)(MapPyhAddr + ActualSendLength - 1));
|
|
OhciSetTDField (DataTd, TD_NEXT_PTR, 0);
|
|
DataTd->ActualSendLength = (UINT32)ActualSendLength;
|
|
DataTd->DataBuffer = (UINT32)(UINTN)MapPyhAddr;
|
|
DataTd->NextTDPointer = 0;
|
|
if (FirstTD) {
|
|
HeadTd = DataTd;
|
|
FirstTD = FALSE;
|
|
} else {
|
|
OhciLinkTD (HeadTd, DataTd);
|
|
}
|
|
*DataToggle ^= 1;
|
|
MapPyhAddr += ActualSendLength;
|
|
LeftLength -= ActualSendLength;
|
|
}
|
|
//
|
|
// Empty Stage
|
|
//
|
|
EmptyTd = OhciCreateTD (Ohc);
|
|
if (EmptyTd == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
DEBUG ((EFI_D_INFO, "OhciBulkTransfer: Fail to allocate buffer for Empty TD\r\n"));
|
|
goto FREE_OHCI_TDBUFF;
|
|
}
|
|
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 = 0;
|
|
EmptyTd->NextTDPointer = 0;
|
|
OhciLinkTD (HeadTd, EmptyTd);
|
|
Ed->TdTailPointer = (UINT32)(UINTN)EmptyTd;
|
|
OhciAttachTDListToED (Ed, HeadTd);
|
|
|
|
OhciSetEDField (Ed, ED_SKIP, 0);
|
|
Status = OhciSetHcCommandStatus (Ohc, BULK_LIST_FILLED, 1);
|
|
if (EFI_ERROR(Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to enable BULK_LIST_FILLED\r\n"));
|
|
goto FREE_OHCI_TDBUFF;
|
|
}
|
|
Status = OhciSetHcControl (Ohc, BULK_ENABLE, 1);
|
|
if (EFI_ERROR(Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to enable BULK_ENABLE\r\n"));
|
|
goto FREE_OHCI_TDBUFF;
|
|
}
|
|
gBS->Stall(20 * 1000);
|
|
|
|
TimeCount = 0;
|
|
Status = CheckIfDone (Ohc, BULK_LIST, Ed, HeadTd, &EdResult);
|
|
while (Status == EFI_NOT_READY && TimeCount <= TimeOut) {
|
|
gBS->Stall (1000);
|
|
TimeCount++;
|
|
Status = CheckIfDone (Ohc, BULK_LIST, Ed, HeadTd, &EdResult);
|
|
}
|
|
|
|
*TransferResult = ConvertErrorCode (EdResult.ErrorCode);
|
|
|
|
if (EdResult.ErrorCode != TD_NO_ERROR) {
|
|
if (EdResult.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"));
|
|
*DataToggle = EdResult.NextToggle;
|
|
}
|
|
*DataLength = 0;
|
|
} else {
|
|
DEBUG ((EFI_D_INFO, "Bulk transfer successed\r\n"));
|
|
}
|
|
//*DataToggle = (UINT8) OhciGetEDField (Ed, ED_DTTOGGLE);
|
|
|
|
FREE_OHCI_TDBUFF:
|
|
OhciSetEDField (Ed, ED_SKIP, 1);
|
|
if (HeadEd == Ed) {
|
|
OhciSetMemoryPointer (Ohc, HC_BULK_HEAD, NULL);
|
|
}else {
|
|
HeadEd->NextED = Ed->NextED;
|
|
}
|
|
while (HeadTd) {
|
|
DataTd = HeadTd;
|
|
HeadTd = (TD_DESCRIPTOR *)(UINTN)(HeadTd->NextTDPointer);
|
|
UsbHcFreeMem(Ohc->MemPool, DataTd, sizeof(TD_DESCRIPTOR));
|
|
}
|
|
|
|
if(Mapping != NULL) {
|
|
Ohc->PciIo->Unmap(Ohc->PciIo, Mapping);
|
|
}
|
|
|
|
FREE_ED_BUFF:
|
|
UsbHcFreeMem(Ohc->MemPool, Ed, sizeof(ED_DESCRIPTOR));
|
|
|
|
return Status;
|
|
}
|
|
/**
|
|
|
|
Submits an interrupt transfer to an interrupt endpoint of a USB device.
|
|
|
|
@param Ohc Device private data
|
|
@param DeviceAddress Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
@param EndPointAddress The combination of an endpoint number and an endpoint
|
|
direction of the target USB device. Each endpoint address
|
|
supports data transfer in one direction except the
|
|
control endpoint (whose default endpoint address is 0).
|
|
It is the caller's responsibility to make sure that
|
|
the EndPointAddress represents an interrupt endpoint.
|
|
@param IsSlowDevice Indicates whether the target device is slow device
|
|
or full-speed device.
|
|
@param MaxPacketLength Indicates the maximum packet size the target endpoint
|
|
is capable of sending or receiving.
|
|
@param IsNewTransfer If TRUE, an asynchronous interrupt pipe is built between
|
|
the host and the target interrupt endpoint.
|
|
If FALSE, the specified asynchronous interrupt pipe
|
|
is canceled.
|
|
@param DataToggle A pointer to the data toggle value. On input, it is valid
|
|
when IsNewTransfer is TRUE, and it indicates the initial
|
|
data toggle value the asynchronous interrupt transfer
|
|
should adopt.
|
|
On output, it is valid when IsNewTransfer is FALSE,
|
|
and it is updated to indicate the data toggle value of
|
|
the subsequent asynchronous interrupt transfer.
|
|
@param PollingInterval Indicates the interval, in milliseconds, that the
|
|
asynchronous interrupt transfer is polled.
|
|
This parameter is required when IsNewTransfer is TRUE.
|
|
@param UCBuffer Uncacheable buffer
|
|
@param DataLength Indicates the length of data to be received at the
|
|
rate specified by PollingInterval from the target
|
|
asynchronous interrupt endpoint. This parameter
|
|
is only required when IsNewTransfer is TRUE.
|
|
@param CallBackFunction The Callback function.This function is called at the
|
|
rate specified by PollingInterval.This parameter is
|
|
only required when IsNewTransfer is TRUE.
|
|
@param Context The context that is passed to the CallBackFunction.
|
|
This is an optional parameter and may be NULL.
|
|
@param IsPeriodic Periodic interrupt or not
|
|
@param OutputED The correspoding ED carried out
|
|
@param OutputTD The correspoding TD carried out
|
|
|
|
|
|
@retval EFI_SUCCESS The asynchronous interrupt transfer request has been successfully
|
|
submitted or canceled.
|
|
@retval EFI_INVALID_PARAMETER Some parameters are invalid.
|
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
OhciInterruptTransfer (
|
|
IN USB_OHCI_HC_DEV *Ohc,
|
|
IN UINT8 DeviceAddress,
|
|
IN UINT8 EndPointAddress,
|
|
IN BOOLEAN IsSlowDevice,
|
|
IN UINT8 MaxPacketLength,
|
|
IN BOOLEAN IsNewTransfer,
|
|
IN OUT UINT8 *DataToggle OPTIONAL,
|
|
IN UINTN PollingInterval OPTIONAL,
|
|
IN VOID *UCBuffer OPTIONAL,
|
|
IN UINTN DataLength OPTIONAL,
|
|
IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction OPTIONAL,
|
|
IN VOID *Context OPTIONAL,
|
|
IN BOOLEAN IsPeriodic OPTIONAL,
|
|
OUT ED_DESCRIPTOR **OutputED OPTIONAL,
|
|
OUT TD_DESCRIPTOR **OutputTD OPTIONAL
|
|
)
|
|
{
|
|
ED_DESCRIPTOR *Ed;
|
|
UINT8 EdDir;
|
|
ED_DESCRIPTOR *HeadEd;
|
|
TD_DESCRIPTOR *HeadTd;
|
|
TD_DESCRIPTOR *DataTd;
|
|
TD_DESCRIPTOR *EmptTd;
|
|
UINTN Depth;
|
|
UINTN Index;
|
|
EFI_STATUS Status;
|
|
UINT8 EndPointNum;
|
|
UINT32 DataPidDir;
|
|
EFI_USB_DATA_DIRECTION TransferDirection;
|
|
INTERRUPT_CONTEXT_ENTRY *Entry;
|
|
EFI_TPL OldTpl;
|
|
BOOLEAN FirstTD;
|
|
|
|
VOID *Mapping;
|
|
UINTN MapLength;
|
|
EFI_PHYSICAL_ADDRESS MapPyhAddr;
|
|
UINTN LeftLength;
|
|
UINTN ActualSendLength;
|
|
|
|
|
|
if (DataLength > MAX_BYTES_PER_TD) {
|
|
DEBUG ((EFI_D_ERROR, "OhciInterruptTransfer: Error param\r\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((EndPointAddress & 0x80) != 0) {
|
|
TransferDirection = EfiUsbDataIn;
|
|
EdDir = ED_IN_DIR;
|
|
DataPidDir = TD_IN_PID;
|
|
} else {
|
|
TransferDirection = EfiUsbDataOut;
|
|
EdDir = ED_OUT_DIR;
|
|
DataPidDir = TD_OUT_PID;
|
|
}
|
|
|
|
EndPointNum = (EndPointAddress & 0xF);
|
|
|
|
if (!IsNewTransfer) {
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
OhciSetHcControl (Ohc, PERIODIC_ENABLE, 0);
|
|
OhciFreeInterruptContext (Ohc, DeviceAddress, EndPointAddress, DataToggle);
|
|
Status = OhciFreeInterruptEdByAddr (Ohc, DeviceAddress, EndPointNum);
|
|
OhciSetHcControl (Ohc, PERIODIC_ENABLE, 1);
|
|
gBS->RestoreTPL (OldTpl);
|
|
return Status;
|
|
}
|
|
MapLength = DataLength;
|
|
Status = Ohc->PciIo->Map(
|
|
Ohc->PciIo,
|
|
EfiPciIoOperationBusMasterWrite,
|
|
UCBuffer,
|
|
&MapLength,
|
|
&MapPyhAddr,
|
|
&Mapping
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "OhciInterruptTransfer: Failt to PciIo->Map buffer \r\n"));
|
|
goto EXIT;
|
|
}
|
|
Depth = 5;
|
|
Index = 1;
|
|
while (PollingInterval >= Index * 2 && Depth > 0) {
|
|
Index *= 2;
|
|
Depth--;
|
|
}
|
|
//
|
|
//ED Stage
|
|
//
|
|
HeadEd = OhciFindMinInterruptEDList (Ohc, (UINT32)Depth);
|
|
if ((Ed = OhciFindWorkingEd (HeadEd, DeviceAddress, EndPointNum, EdDir)) != NULL) {
|
|
OhciSetEDField (Ed, ED_SKIP, 1);
|
|
} else {
|
|
Ed = OhciCreateED (Ohc);
|
|
if (Ed == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
DEBUG ((EFI_D_ERROR, "OhciInterruptTransfer: Fail to allocate buffer for ED\r\n"));
|
|
goto UNMAP_OHCI_XBUFF;
|
|
}
|
|
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, IsSlowDevice);
|
|
OhciSetEDField (Ed, ED_FORMAT, 0);
|
|
OhciSetEDField (Ed, ED_MAX_PACKET, MaxPacketLength);
|
|
OhciSetEDField (Ed, ED_PDATA | ED_ZERO | ED_HALTED | ED_DTTOGGLE, 0);
|
|
OhciSetEDField (Ed, ED_TDHEAD_PTR, 0);
|
|
OhciSetEDField (Ed, ED_TDTAIL_PTR, 0);
|
|
OhciSetEDField (Ed, ED_NEXT_EDPTR, 0);
|
|
OhciAttachEDToList (Ohc, INTERRUPT_LIST, Ed, HeadEd);
|
|
}
|
|
//
|
|
//Data Stage
|
|
//
|
|
LeftLength = MapLength;
|
|
ActualSendLength = MapLength;
|
|
HeadTd = NULL;
|
|
FirstTD = TRUE;
|
|
while (LeftLength > 0) {
|
|
ActualSendLength = LeftLength;
|
|
if (LeftLength > MaxPacketLength) {
|
|
ActualSendLength = MaxPacketLength;
|
|
}
|
|
DataTd = OhciCreateTD (Ohc);
|
|
if (DataTd == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
DEBUG ((EFI_D_ERROR, "OhciInterruptTransfer: Fail to allocate buffer for Data Stage TD\r\n"));
|
|
goto FREE_OHCI_TDBUFF;
|
|
}
|
|
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) MapPyhAddr);
|
|
OhciSetTDField (DataTd, TD_BUFFER_END_PTR, (UINT32)(MapPyhAddr + ActualSendLength - 1));
|
|
OhciSetTDField (DataTd, TD_NEXT_PTR, 0);
|
|
DataTd->ActualSendLength = (UINT32)ActualSendLength;
|
|
DataTd->DataBuffer = (UINT32)(UINTN)MapPyhAddr;
|
|
DataTd->NextTDPointer = 0;
|
|
if (FirstTD) {
|
|
HeadTd = DataTd;
|
|
FirstTD = FALSE;
|
|
} else {
|
|
OhciLinkTD (HeadTd, DataTd);
|
|
}
|
|
*DataToggle ^= 1;
|
|
MapPyhAddr += ActualSendLength;
|
|
LeftLength -= ActualSendLength;
|
|
}
|
|
|
|
EmptTd = OhciCreateTD (Ohc);
|
|
if (EmptTd == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
DEBUG ((EFI_D_ERROR, "OhciInterruptTransfer: Fail to allocate buffer for Empty Stage TD\r\n"));
|
|
goto FREE_OHCI_TDBUFF;
|
|
}
|
|
OhciSetTDField (EmptTd, TD_PDATA, 0);
|
|
OhciSetTDField (EmptTd, TD_BUFFER_ROUND, 0);
|
|
OhciSetTDField (EmptTd, TD_DIR_PID, 0);
|
|
OhciSetTDField (EmptTd, TD_DELAY_INT, 0);
|
|
//OhciSetTDField (EmptTd, TD_DT_TOGGLE, CurrentToggle);
|
|
EmptTd->Word0.DataToggle = 0;
|
|
OhciSetTDField (EmptTd, TD_ERROR_CNT, 0);
|
|
OhciSetTDField (EmptTd, TD_COND_CODE, 0);
|
|
OhciSetTDField (EmptTd, TD_CURR_BUFFER_PTR, 0);
|
|
OhciSetTDField (EmptTd, TD_BUFFER_END_PTR, 0);
|
|
OhciSetTDField (EmptTd, TD_NEXT_PTR, 0);
|
|
EmptTd->ActualSendLength = 0;
|
|
EmptTd->DataBuffer = 0;
|
|
EmptTd->NextTDPointer = 0;
|
|
OhciLinkTD (HeadTd, EmptTd);
|
|
Ed->TdTailPointer = (UINT32)(UINTN)EmptTd;
|
|
OhciAttachTDListToED (Ed, HeadTd);
|
|
|
|
if (OutputED != NULL) {
|
|
*OutputED = Ed;
|
|
}
|
|
if (OutputTD != NULL) {
|
|
*OutputTD = HeadTd;
|
|
}
|
|
|
|
if (CallBackFunction != NULL) {
|
|
Entry = AllocatePool (sizeof (INTERRUPT_CONTEXT_ENTRY));
|
|
if (Entry == NULL) {
|
|
goto FREE_OHCI_TDBUFF;
|
|
}
|
|
|
|
Entry->DeviceAddress = DeviceAddress;
|
|
Entry->EndPointAddress = EndPointAddress;
|
|
Entry->Ed = Ed;
|
|
Entry->DataTd = HeadTd;
|
|
Entry->IsSlowDevice = IsSlowDevice;
|
|
Entry->MaxPacketLength = MaxPacketLength;
|
|
Entry->PollingInterval = PollingInterval;
|
|
Entry->CallBackFunction = CallBackFunction;
|
|
Entry->Context = Context;
|
|
Entry->IsPeriodic = IsPeriodic;
|
|
Entry->UCBuffer = UCBuffer;
|
|
Entry->UCBufferMapping = Mapping;
|
|
Entry->DataLength = DataLength;
|
|
Entry->Toggle = DataToggle;
|
|
Entry->NextEntry = NULL;
|
|
OhciAddInterruptContextEntry (Ohc, Entry);
|
|
}
|
|
OhciSetEDField (Ed, ED_SKIP, 0);
|
|
|
|
if (OhciGetHcControl (Ohc, PERIODIC_ENABLE) == 0) {
|
|
Status = OhciSetHcControl (Ohc, PERIODIC_ENABLE, 1);
|
|
gBS->Stall (1000);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
FREE_OHCI_TDBUFF:
|
|
while (HeadTd) {
|
|
DataTd = HeadTd;
|
|
HeadTd = (TD_DESCRIPTOR *)(UINTN)(HeadTd->NextTDPointer);
|
|
UsbHcFreeMem(Ohc->MemPool, DataTd, sizeof(TD_DESCRIPTOR));
|
|
}
|
|
|
|
//FREE_OHCI_EDBUFF:
|
|
if ((HeadEd != Ed) && HeadEd && Ed) {
|
|
while(HeadEd->NextED != (UINT32)(UINTN)Ed) {
|
|
HeadEd = (ED_DESCRIPTOR *)(UINTN)(HeadEd->NextED);
|
|
}
|
|
HeadEd->NextED = Ed->NextED;
|
|
UsbHcFreeMem(Ohc->MemPool, Ed, sizeof(ED_DESCRIPTOR));
|
|
}
|
|
|
|
UNMAP_OHCI_XBUFF:
|
|
Ohc->PciIo->Unmap(Ohc->PciIo, Mapping);
|
|
|
|
EXIT:
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
Submits an asynchronous interrupt transfer to an interrupt endpoint of a USB device.
|
|
|
|
@param This A pointer to the EFI_USB_HC_PROTOCOL instance.
|
|
@param DeviceAddress Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
@param EndPointAddress The combination of an endpoint number and an endpoint
|
|
direction of the target USB device. Each endpoint address
|
|
supports data transfer in one direction except the
|
|
control endpoint (whose default endpoint address is 0).
|
|
It is the caller's responsibility to make sure that
|
|
the EndPointAddress represents an interrupt endpoint.
|
|
@param IsSlowDevice Indicates whether the target device is slow device
|
|
or full-speed device.
|
|
@param MaxiumPacketLength Indicates the maximum packet size the target endpoint
|
|
is capable of sending or receiving.
|
|
@param IsNewTransfer If TRUE, an asynchronous interrupt pipe is built between
|
|
the host and the target interrupt endpoint.
|
|
If FALSE, the specified asynchronous interrupt pipe
|
|
is canceled.
|
|
@param DataToggle A pointer to the data toggle value. On input, it is valid
|
|
when IsNewTransfer is TRUE, and it indicates the initial
|
|
data toggle value the asynchronous interrupt transfer
|
|
should adopt.
|
|
On output, it is valid when IsNewTransfer is FALSE,
|
|
and it is updated to indicate the data toggle value of
|
|
the subsequent asynchronous interrupt transfer.
|
|
@param PollingInterval Indicates the interval, in milliseconds, that the
|
|
asynchronous interrupt transfer is polled.
|
|
This parameter is required when IsNewTransfer is TRUE.
|
|
@param DataLength Indicates the length of data to be received at the
|
|
rate specified by PollingInterval from the target
|
|
asynchronous interrupt endpoint. This parameter
|
|
is only required when IsNewTransfer is TRUE.
|
|
@param CallBackFunction The Callback function.This function is called at the
|
|
rate specified by PollingInterval.This parameter is
|
|
only required when IsNewTransfer is TRUE.
|
|
@param Context The context that is passed to the CallBackFunction.
|
|
This is an optional parameter and may be NULL.
|
|
|
|
@retval EFI_SUCCESS The asynchronous interrupt transfer request has been successfully
|
|
submitted or canceled.
|
|
@retval EFI_INVALID_PARAMETER Some parameters are invalid.
|
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
|
|
|
|
**/
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciAsyncInterruptTransfer (
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
IN UINT8 DeviceAddress,
|
|
IN UINT8 EndPointAddress,
|
|
IN BOOLEAN IsSlowDevice,
|
|
IN UINT8 MaxPacketLength,
|
|
IN BOOLEAN IsNewTransfer,
|
|
IN OUT UINT8 *DataToggle OPTIONAL,
|
|
IN UINTN PollingInterval OPTIONAL,
|
|
IN UINTN DataLength OPTIONAL,
|
|
IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction OPTIONAL,
|
|
IN VOID *Context OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
VOID *UCBuffer;
|
|
|
|
if (DataToggle == NULL || (EndPointAddress & 0x80) == 0 ||
|
|
(IsNewTransfer && (DataLength == 0 ||
|
|
(*DataToggle != 0 && *DataToggle != 1) || (PollingInterval < 1 || PollingInterval > 255)))) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Ohc = USB_OHCI_HC_DEV_FROM_THIS (This);
|
|
if ( IsNewTransfer ) {
|
|
UCBuffer = AllocatePool(DataLength);
|
|
if (UCBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
} else {
|
|
UCBuffer = NULL;
|
|
}
|
|
Status = OhciInterruptTransfer (
|
|
Ohc,
|
|
DeviceAddress,
|
|
EndPointAddress,
|
|
IsSlowDevice,
|
|
MaxPacketLength,
|
|
IsNewTransfer,
|
|
DataToggle,
|
|
PollingInterval,
|
|
UCBuffer,
|
|
DataLength,
|
|
CallBackFunction,
|
|
Context,
|
|
TRUE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if ( UCBuffer ) {
|
|
// if (EFI_ERROR(Status)) {
|
|
gBS->FreePool (UCBuffer);
|
|
// }
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
Submits synchronous interrupt transfer to an interrupt endpoint
|
|
of a USB device.
|
|
|
|
@param This A pointer to the EFI_USB_HC_PROTOCOL instance.
|
|
@param DeviceAddress Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
@param EndPointAddress The combination of an endpoint number and an endpoint
|
|
direction of the target USB device. Each endpoint
|
|
address supports data transfer in one direction
|
|
except the control endpoint (whose default
|
|
endpoint address is 0). It is the caller's responsibility
|
|
to make sure that the EndPointAddress represents
|
|
an interrupt endpoint.
|
|
@param IsSlowDevice Indicates whether the target device is slow device
|
|
or full-speed device.
|
|
@param MaxPacketLength Indicates the maximum packet size the target endpoint
|
|
is capable of sending or receiving.
|
|
@param Data A pointer to the buffer of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
@param DataLength On input, the size, in bytes, of the data buffer specified
|
|
by Data. On output, the number of bytes transferred.
|
|
@param DataToggle A pointer to the data toggle value. On input, it indicates
|
|
the initial data toggle value the synchronous interrupt
|
|
transfer should adopt;
|
|
on output, it is updated to indicate the data toggle value
|
|
of the subsequent synchronous interrupt transfer.
|
|
@param TimeOut Indicates the maximum time, in microseconds, which the
|
|
transfer is allowed to complete.
|
|
@param TransferResult A pointer to the detailed result information from
|
|
the synchronous interrupt transfer.
|
|
|
|
@retval EFI_UNSUPPORTED This interface not available.
|
|
@retval EFI_INVALID_PARAMETER Parameters not follow spec
|
|
|
|
**/
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciSyncInterruptTransfer (
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
IN UINT8 DeviceAddress,
|
|
IN UINT8 EndPointAddress,
|
|
IN BOOLEAN IsSlowDevice,
|
|
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;
|
|
EFI_STATUS Status;
|
|
ED_DESCRIPTOR *Ed;
|
|
TD_DESCRIPTOR *HeadTd;
|
|
OHCI_ED_RESULT EdResult;
|
|
VOID *UCBuffer;
|
|
|
|
if ((EndPointAddress & 0x80) == 0 || Data == NULL || DataLength == NULL || *DataLength == 0 ||
|
|
(IsSlowDevice && MaxPacketLength > 8) || (!IsSlowDevice && MaxPacketLength > 64) ||
|
|
DataToggle == NULL || (*DataToggle != 0 && *DataToggle != 1) || TransferResult == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Ohc = USB_OHCI_HC_DEV_FROM_THIS (This);
|
|
UCBuffer = AllocatePool (*DataLength);
|
|
if (UCBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
Status = OhciInterruptTransfer (
|
|
Ohc,
|
|
DeviceAddress,
|
|
EndPointAddress,
|
|
IsSlowDevice,
|
|
MaxPacketLength,
|
|
TRUE,
|
|
DataToggle,
|
|
1,
|
|
UCBuffer,
|
|
*DataLength,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
&Ed,
|
|
&HeadTd
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = CheckIfDone (Ohc, INTERRUPT_LIST, Ed, HeadTd, &EdResult);
|
|
while (Status == EFI_NOT_READY && TimeOut > 0) {
|
|
gBS->Stall (1000);
|
|
TimeOut--;
|
|
Status = CheckIfDone (Ohc, INTERRUPT_LIST, Ed, HeadTd, &EdResult);
|
|
}
|
|
|
|
*TransferResult = ConvertErrorCode (EdResult.ErrorCode);
|
|
}
|
|
CopyMem(Data, UCBuffer, *DataLength);
|
|
Status = OhciInterruptTransfer (
|
|
Ohc,
|
|
DeviceAddress,
|
|
EndPointAddress,
|
|
IsSlowDevice,
|
|
MaxPacketLength,
|
|
FALSE,
|
|
DataToggle,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
gBS->FreePool (UCBuffer);
|
|
return Status;
|
|
}
|
|
/**
|
|
|
|
Submits isochronous transfer to a target USB device.
|
|
|
|
@param This A pointer to the EFI_USB_HC_PROTOCOL instance.
|
|
@param DeviceAddress Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
@param EndPointAddress End point address
|
|
@param MaximumPacketLength Indicates the maximum packet size that the
|
|
default control transfer endpoint is capable of
|
|
sending or receiving.
|
|
@param Data A pointer to the buffer of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
@param DataLength Indicates the size, in bytes, of the data buffer
|
|
specified by Data.
|
|
@param TransferResult A pointer to the detailed result information generated
|
|
by this control transfer.
|
|
|
|
@retval EFI_UNSUPPORTED This interface not available
|
|
@retval EFI_INVALID_PARAMETER Data is NULL or DataLength is 0 or TransferResult is NULL
|
|
|
|
**/
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciIsochronousTransfer (
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
IN UINT8 DeviceAddress,
|
|
IN UINT8 EndPointAddress,
|
|
IN UINT8 MaximumPacketLength,
|
|
IN OUT VOID *Data,
|
|
IN OUT UINTN DataLength,
|
|
OUT UINT32 *TransferResult
|
|
)
|
|
{
|
|
if (Data == NULL || DataLength == 0 || TransferResult == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
/**
|
|
|
|
Submits Async isochronous transfer to a target USB device.
|
|
|
|
@param his A pointer to the EFI_USB_HC_PROTOCOL instance.
|
|
@param DeviceAddress Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
@param EndPointAddress End point address
|
|
@param MaximumPacketLength Indicates the maximum packet size that the
|
|
default control transfer endpoint is capable of
|
|
sending or receiving.
|
|
@param Data A pointer to the buffer of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
@param IsochronousCallBack When the transfer complete, the call back function will be called
|
|
@param Context Pass to the call back function as parameter
|
|
|
|
@retval EFI_UNSUPPORTED This interface not available
|
|
@retval EFI_INVALID_PARAMETER Data is NULL or Datalength is 0
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciAsyncIsochronousTransfer (
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
IN UINT8 DeviceAddress,
|
|
IN UINT8 EndPointAddress,
|
|
IN UINT8 MaximumPacketLength,
|
|
IN OUT VOID *Data,
|
|
IN OUT UINTN DataLength,
|
|
IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack,
|
|
IN VOID *Context OPTIONAL
|
|
)
|
|
{
|
|
|
|
if (Data == NULL || DataLength == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
/**
|
|
|
|
Retrieves the number of root hub ports.
|
|
|
|
@param This A pointer to the EFI_USB_HC_PROTOCOL instance.
|
|
@param NumOfPorts A pointer to the number of the root hub ports.
|
|
|
|
@retval EFI_SUCCESS The port number was retrieved successfully.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciGetRootHubNumOfPorts (
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
OUT UINT8 *NumOfPorts
|
|
)
|
|
{
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
Ohc = USB_OHCI_HC_DEV_FROM_THIS (This);
|
|
|
|
if (NumOfPorts == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*NumOfPorts = (UINT8)OhciGetRootHubDescriptor(Ohc, RH_NUM_DS_PORTS);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
/**
|
|
|
|
Retrieves the current status of a USB root hub port.
|
|
|
|
@param This A pointer to the EFI_USB_HC_PROTOCOL.
|
|
@param PortNumber Specifies the root hub port from which the status
|
|
is to be retrieved. This value is zero-based. For example,
|
|
if a root hub has two ports, then the first port is numbered 0,
|
|
and the second port is numbered 1.
|
|
@param PortStatus A pointer to the current port status bits and
|
|
port status change bits.
|
|
|
|
@retval EFI_SUCCESS The status of the USB root hub port specified by PortNumber
|
|
was returned in PortStatus.
|
|
@retval EFI_INVALID_PARAMETER Port number not valid
|
|
**/
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciGetRootHubPortStatus (
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
IN UINT8 PortNumber,
|
|
OUT EFI_USB_PORT_STATUS *PortStatus
|
|
)
|
|
{
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
UINT8 NumOfPorts;
|
|
|
|
Ohc = USB_OHCI_HC_DEV_FROM_THIS (This);
|
|
|
|
OhciGetRootHubNumOfPorts (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 This A pointer to the EFI_USB_HC_PROTOCOL.
|
|
@param PortNumber Specifies the root hub port whose feature
|
|
is requested to be set.
|
|
@param PortFeature Indicates the feature selector associated
|
|
with the feature set request.
|
|
|
|
@retval EFI_SUCCESS The feature specified by PortFeature was set for the
|
|
USB root hub port specified by PortNumber.
|
|
@retval EFI_DEVICE_ERROR Set feature failed because of hardware issue
|
|
@retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciSetRootHubPortFeature (
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
IN UINT8 PortNumber,
|
|
IN EFI_USB_PORT_FEATURE PortFeature
|
|
)
|
|
{
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
EFI_STATUS Status;
|
|
UINT8 NumOfPorts;
|
|
UINTN RetryTimes;
|
|
|
|
OhciGetRootHubNumOfPorts (This, &NumOfPorts);
|
|
if (PortNumber >= NumOfPorts) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Ohc = USB_OHCI_HC_DEV_FROM_THIS (This);
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
|
|
switch (PortFeature) {
|
|
case EfiUsbPortPower:
|
|
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_SET_PORT_POWER);
|
|
|
|
//
|
|
// Verify the state
|
|
//
|
|
RetryTimes = 0;
|
|
do {
|
|
gBS->Stall (1000);
|
|
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 {
|
|
gBS->Stall (1000);
|
|
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 {
|
|
gBS->Stall (1000);
|
|
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 {
|
|
gBS->Stall (1000);
|
|
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 This A pointer to the EFI_USB_HC_PROTOCOL instance.
|
|
@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.
|
|
@retval EFI_DEVICE_ERROR Some error happened when clearing feature
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OhciClearRootHubPortFeature (
|
|
IN EFI_USB_HC_PROTOCOL *This,
|
|
IN UINT8 PortNumber,
|
|
IN EFI_USB_PORT_FEATURE PortFeature
|
|
)
|
|
{
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
EFI_STATUS Status;
|
|
UINT8 NumOfPorts;
|
|
UINTN RetryTimes;
|
|
|
|
|
|
OhciGetRootHubNumOfPorts (This, &NumOfPorts);
|
|
if (PortNumber >= NumOfPorts) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Ohc = USB_OHCI_HC_DEV_FROM_THIS (This);
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
switch (PortFeature) {
|
|
case EfiUsbPortEnable:
|
|
Status = OhciSetRootHubPortStatus (Ohc, PortNumber, RH_CLEAR_PORT_ENABLE);
|
|
|
|
//
|
|
// Verify the state
|
|
//
|
|
RetryTimes = 0;
|
|
do {
|
|
gBS->Stall (1000);
|
|
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 {
|
|
gBS->Stall (1000);
|
|
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 {
|
|
gBS->Stall (1000);
|
|
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 {
|
|
gBS->Stall (1000);
|
|
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 {
|
|
gBS->Stall (1000);
|
|
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 {
|
|
gBS->Stall (1000);
|
|
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 {
|
|
gBS->Stall (1000);
|
|
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 {
|
|
gBS->Stall (1000);
|
|
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;
|
|
}
|
|
|
|
EFI_DRIVER_BINDING_PROTOCOL gOhciDriverBinding = {
|
|
OHCIDriverBindingSupported,
|
|
OHCIDriverBindingStart,
|
|
OHCIDriverBindingStop,
|
|
0x10,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
/**
|
|
Entry point for EFI drivers.
|
|
|
|
@param ImageHandle EFI_HANDLE.
|
|
@param SystemTable EFI_SYSTEM_TABLE.
|
|
|
|
@retval EFI_SUCCESS Driver is successfully loaded.
|
|
@return Others Failed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OHCIDriverEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
return EfiLibInstallDriverBindingComponentName2 (
|
|
ImageHandle,
|
|
SystemTable,
|
|
&gOhciDriverBinding,
|
|
ImageHandle,
|
|
&gOhciComponentName,
|
|
&gOhciComponentName2
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
Test to see if this driver supports ControllerHandle. Any
|
|
ControllerHandle that has UsbHcProtocol installed will be supported.
|
|
|
|
@param This Protocol instance pointer.
|
|
@param Controller Handle of device to test.
|
|
@param RemainingDevicePath Not used.
|
|
|
|
@return EFI_SUCCESS This driver supports this device.
|
|
@return EFI_UNSUPPORTED This driver does not support this device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OHCIDriverBindingSupported (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
USB_CLASSC UsbClassCReg;
|
|
//
|
|
// Test whether there is PCI IO Protocol attached on the controller handle.
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
(VOID **) &PciIo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Status = PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
PCI_CLASSCODE_OFFSET,
|
|
sizeof (USB_CLASSC) / sizeof (UINT8),
|
|
&UsbClassCReg
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto ON_EXIT;
|
|
}
|
|
//
|
|
// Test whether the controller belongs to OHCI type
|
|
//
|
|
if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) ||
|
|
(UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) ||
|
|
(UsbClassCReg.ProgInterface != PCI_IF_OHCI)
|
|
) {
|
|
|
|
Status = EFI_UNSUPPORTED;
|
|
}
|
|
ON_EXIT:
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
Allocate and initialize the empty OHCI device.
|
|
|
|
@param PciIo The PCIIO to use.
|
|
@param OriginalPciAttributes The original PCI attributes.
|
|
|
|
@return Allocated OHCI device If err, return NULL.
|
|
|
|
**/
|
|
|
|
USB_OHCI_HC_DEV *
|
|
OhciAllocateDev (
|
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
|
IN UINT64 OriginalPciAttributes
|
|
)
|
|
{
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
EFI_STATUS Status;
|
|
VOID *Buf;
|
|
EFI_PHYSICAL_ADDRESS PhyAddr;
|
|
VOID *Map;
|
|
UINTN Pages;
|
|
UINTN Bytes;
|
|
|
|
Ohc = AllocateZeroPool (sizeof (USB_OHCI_HC_DEV));
|
|
if (Ohc == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Ohc->Signature = USB_OHCI_HC_DEV_SIGNATURE;
|
|
Ohc->PciIo = PciIo;
|
|
|
|
Ohc->UsbHc.Reset = OhciReset;
|
|
Ohc->UsbHc.GetState = OhciGetState;
|
|
Ohc->UsbHc.SetState = OhciSetState;
|
|
Ohc->UsbHc.ControlTransfer = OhciControlTransfer;
|
|
Ohc->UsbHc.BulkTransfer = OhciBulkTransfer;
|
|
Ohc->UsbHc.AsyncInterruptTransfer = OhciAsyncInterruptTransfer;
|
|
Ohc->UsbHc.SyncInterruptTransfer = OhciSyncInterruptTransfer;
|
|
Ohc->UsbHc.IsochronousTransfer = OhciIsochronousTransfer;
|
|
Ohc->UsbHc.AsyncIsochronousTransfer = OhciAsyncIsochronousTransfer;
|
|
Ohc->UsbHc.GetRootHubPortNumber = OhciGetRootHubNumOfPorts;
|
|
Ohc->UsbHc.GetRootHubPortStatus = OhciGetRootHubPortStatus;
|
|
Ohc->UsbHc.SetRootHubPortFeature = OhciSetRootHubPortFeature;
|
|
Ohc->UsbHc.ClearRootHubPortFeature = OhciClearRootHubPortFeature;
|
|
Ohc->UsbHc.MajorRevision = 0x1;
|
|
Ohc->UsbHc.MinorRevision = 0x1;
|
|
|
|
Ohc->OriginalPciAttributes = OriginalPciAttributes;
|
|
|
|
Ohc->HccaMemoryBlock = NULL;
|
|
Ohc->HccaMemoryMapping = NULL;
|
|
Ohc->HccaMemoryBuf = NULL;
|
|
Ohc->HccaMemoryPages = 0;
|
|
Ohc->InterruptContextList = NULL;
|
|
Ohc->ControllerNameTable = NULL;
|
|
Ohc->HouseKeeperTimer = NULL;
|
|
|
|
Ohc->MemPool = UsbHcInitMemPool(PciIo, TRUE, 0);
|
|
if(Ohc->MemPool == NULL) {
|
|
goto FREE_DEV_BUFFER;
|
|
}
|
|
|
|
Bytes = 4096;
|
|
Pages = EFI_SIZE_TO_PAGES (Bytes);
|
|
|
|
Status = PciIo->AllocateBuffer (
|
|
PciIo,
|
|
AllocateAnyPages,
|
|
EfiBootServicesData,
|
|
Pages,
|
|
&Buf,
|
|
0
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto FREE_MEM_POOL;
|
|
}
|
|
|
|
Status = PciIo->Map (
|
|
PciIo,
|
|
EfiPciIoOperationBusMasterCommonBuffer,
|
|
Buf,
|
|
&Bytes,
|
|
&PhyAddr,
|
|
&Map
|
|
);
|
|
|
|
if (EFI_ERROR (Status) || (Bytes != 4096)) {
|
|
goto FREE_MEM_PAGE;
|
|
}
|
|
|
|
Ohc->HccaMemoryBlock = (HCCA_MEMORY_BLOCK *)(UINTN)PhyAddr;
|
|
Ohc->HccaMemoryMapping = Map;
|
|
Ohc->HccaMemoryBuf = (VOID *)(UINTN)Buf;
|
|
Ohc->HccaMemoryPages = Pages;
|
|
|
|
return Ohc;
|
|
|
|
FREE_MEM_PAGE:
|
|
PciIo->FreeBuffer (PciIo, Pages, Buf);
|
|
FREE_MEM_POOL:
|
|
UsbHcFreeMemPool (Ohc->MemPool);
|
|
FREE_DEV_BUFFER:
|
|
FreePool(Ohc);
|
|
|
|
return NULL;
|
|
}
|
|
/**
|
|
|
|
Free the OHCI device and release its associated resources.
|
|
|
|
@param Ohc The OHCI device to release.
|
|
|
|
**/
|
|
VOID
|
|
OhciFreeDev (
|
|
IN USB_OHCI_HC_DEV *Ohc
|
|
)
|
|
{
|
|
OhciFreeFixedIntMemory (Ohc);
|
|
|
|
if (Ohc->ExitBootServiceEvent != NULL) {
|
|
gBS->CloseEvent (Ohc->ExitBootServiceEvent);
|
|
}
|
|
|
|
if (Ohc->HouseKeeperTimer != NULL) {
|
|
gBS->CloseEvent (Ohc->HouseKeeperTimer);
|
|
}
|
|
|
|
if (Ohc->MemPool != NULL) {
|
|
UsbHcFreeMemPool (Ohc->MemPool);
|
|
}
|
|
|
|
if (Ohc->HccaMemoryMapping != NULL ) {
|
|
Ohc->PciIo->FreeBuffer (Ohc->PciIo, Ohc->HccaMemoryPages, Ohc->HccaMemoryBuf);
|
|
}
|
|
|
|
if (Ohc->ControllerNameTable != NULL) {
|
|
FreeUnicodeStringTable (Ohc->ControllerNameTable);
|
|
}
|
|
|
|
FreePool (Ohc);
|
|
}
|
|
/**
|
|
|
|
Uninstall all Ohci Interface.
|
|
|
|
@param Controller Controller handle.
|
|
@param This Protocol instance pointer.
|
|
|
|
**/
|
|
VOID
|
|
OhciCleanDevUp (
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_USB_HC_PROTOCOL *This
|
|
)
|
|
{
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
|
|
//
|
|
// Uninstall the USB_HC and USB_HC2 protocol, then disable the controller
|
|
//
|
|
Ohc = USB_OHCI_HC_DEV_FROM_THIS (This);
|
|
|
|
OhciSetHcControl (Ohc, PERIODIC_ENABLE | CONTROL_ENABLE | ISOCHRONOUS_ENABLE | BULK_ENABLE, 0);
|
|
This->Reset (This, EFI_USB_HC_RESET_GLOBAL);
|
|
This->SetState (This, EfiUsbHcStateHalt);
|
|
|
|
gBS->UninstallProtocolInterface (
|
|
Controller,
|
|
&gEfiUsbHcProtocolGuid,
|
|
&Ohc->UsbHc
|
|
);
|
|
|
|
OhciFreeDynamicIntMemory(Ohc);
|
|
//
|
|
// Restore original PCI attributes
|
|
//
|
|
Ohc->PciIo->Attributes (
|
|
Ohc->PciIo,
|
|
EfiPciIoAttributeOperationSet,
|
|
Ohc->OriginalPciAttributes,
|
|
NULL
|
|
);
|
|
|
|
OhciFreeDev (Ohc);
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
|
|
One notified function to stop the Host Controller when gBS->ExitBootServices() called.
|
|
|
|
@param Event Pointer to this event
|
|
@param Context Event hanlder private data
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
OhcExitBootService (
|
|
EFI_EVENT Event,
|
|
VOID *Context
|
|
)
|
|
{
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
EFI_USB_HC_PROTOCOL *UsbHc;
|
|
Ohc = (USB_OHCI_HC_DEV *) Context;
|
|
|
|
UsbHc = &Ohc->UsbHc;
|
|
//
|
|
// Stop the Host Controller
|
|
//
|
|
//OhciStopHc (Ohc, OHC_GENERIC_TIMEOUT);
|
|
OhciSetHcControl (Ohc, PERIODIC_ENABLE | CONTROL_ENABLE | ISOCHRONOUS_ENABLE | BULK_ENABLE, 0);
|
|
UsbHc->Reset (UsbHc, EFI_USB_HC_RESET_GLOBAL);
|
|
UsbHc->SetState (UsbHc, EfiUsbHcStateHalt);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/**
|
|
Starting the Usb OHCI Driver.
|
|
|
|
@param This Protocol instance pointer.
|
|
@param Controller Handle of device to test.
|
|
@param RemainingDevicePath Not used.
|
|
|
|
@retval EFI_SUCCESS This driver supports this device.
|
|
@retval EFI_UNSUPPORTED This driver does not support this device.
|
|
@retval EFI_DEVICE_ERROR This driver cannot be started due to device Error.
|
|
EFI_OUT_OF_RESOURCES- Failed due to resource shortage.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OHCIDriverBindingStart (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
USB_OHCI_HC_DEV *Ohc;
|
|
UINT64 Supports;
|
|
UINT64 OriginalPciAttributes;
|
|
BOOLEAN PciAttributesSaved;
|
|
UINTN OhciSegmentNumber;
|
|
UINTN OhciBusNumber;
|
|
UINTN OhciDeviceNumber;
|
|
UINTN OhciFunctionNumber;
|
|
UINTN NumberOfControllers;
|
|
UINTN Index;
|
|
|
|
//
|
|
// Open PCIIO, then enable the HC device and turn off emulation
|
|
//
|
|
Ohc = NULL;
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
(VOID **) &PciIo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
PciAttributesSaved = FALSE;
|
|
//
|
|
// Save original PCI attributes
|
|
//
|
|
Status = PciIo->Attributes (
|
|
PciIo,
|
|
EfiPciIoAttributeOperationGet,
|
|
0,
|
|
&OriginalPciAttributes
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto CLOSE_PCIIO;
|
|
}
|
|
PciAttributesSaved = TRUE;
|
|
|
|
//
|
|
// Robustnesss improvement such as for UoL
|
|
// Default is not required.
|
|
//
|
|
//if (FeaturePcdGet (PcdTurnOffUsbLegacySupport)) {
|
|
// OhciTurnOffUsbEmulation (PciIo);
|
|
//}
|
|
|
|
Status = PciIo->Attributes (
|
|
PciIo,
|
|
EfiPciIoAttributeOperationSupported,
|
|
0,
|
|
&Supports
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Supports &= EFI_PCI_DEVICE_ENABLE;
|
|
Status = PciIo->Attributes (
|
|
PciIo,
|
|
EfiPciIoAttributeOperationEnable,
|
|
Supports,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto CLOSE_PCIIO;
|
|
}
|
|
//
|
|
//Allocate memory for OHC private data structure
|
|
//
|
|
Ohc = OhciAllocateDev(PciIo, OriginalPciAttributes);
|
|
if (Ohc == NULL){
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto CLOSE_PCIIO;
|
|
}
|
|
|
|
//Status = OhciInitializeInterruptList ( Uhc );
|
|
//if (EFI_ERROR (Status)) {
|
|
// goto FREE_OHC;
|
|
//}
|
|
|
|
//
|
|
// Set 0.1 s timer
|
|
//
|
|
Status = gBS->CreateEvent (
|
|
EVT_TIMER | EVT_NOTIFY_SIGNAL,
|
|
TPL_NOTIFY,
|
|
OhciHouseKeeper,
|
|
Ohc,
|
|
&Ohc->HouseKeeperTimer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto FREE_OHC;
|
|
}
|
|
|
|
// Polling at every 0.1s is too slow, use 0.05s like with UhciDxe
|
|
//Status = gBS->SetTimer (Ohc->HouseKeeperTimer, TimerPeriodic, 100 * 1000 * 10);
|
|
Status = gBS->SetTimer (Ohc->HouseKeeperTimer, TimerPeriodic, 50 * 1000 * 10);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto FREE_OHC;
|
|
}
|
|
//
|
|
// Before initialization on the ohci, for QuarkSCSocId, all the companion controllers
|
|
// should have bus master , memory space and io space bits enabled
|
|
//
|
|
NumberOfControllers = 4;
|
|
|
|
PciIo->GetLocation (
|
|
PciIo,
|
|
&OhciSegmentNumber,
|
|
&OhciBusNumber,
|
|
&OhciDeviceNumber,
|
|
&OhciFunctionNumber
|
|
);
|
|
|
|
for (Index = 0; Index < NumberOfControllers; Index++) {
|
|
PciOr16 (
|
|
PCI_LIB_ADDRESS (OhciBusNumber, OhciDeviceNumber, Index, PCI_COMMAND_OFFSET),
|
|
(EFI_PCI_COMMAND_IO_SPACE + EFI_PCI_COMMAND_MEMORY_SPACE + EFI_PCI_COMMAND_BUS_MASTER)
|
|
);
|
|
}
|
|
//
|
|
//Install Host Controller Protocol
|
|
//
|
|
Status = gBS->InstallProtocolInterface (
|
|
&Controller,
|
|
&gEfiUsbHcProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
&Ohc->UsbHc
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_INFO, "Install protocol error"));
|
|
goto FREE_OHC;
|
|
}
|
|
//
|
|
// Create event to stop the HC when exit boot service.
|
|
//
|
|
Status = gBS->CreateEventEx (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_NOTIFY,
|
|
OhcExitBootService,
|
|
Ohc,
|
|
&gEfiEventExitBootServicesGuid,
|
|
&Ohc->ExitBootServiceEvent
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_INFO, "Create exit boot event error"));
|
|
goto UNINSTALL_USBHC;
|
|
}
|
|
AddUnicodeString2 (
|
|
"eng",
|
|
gOhciComponentName.SupportedLanguages,
|
|
&Ohc->ControllerNameTable,
|
|
L"Usb Universal Host Controller",
|
|
TRUE
|
|
);
|
|
AddUnicodeString2 (
|
|
"en",
|
|
gOhciComponentName2.SupportedLanguages,
|
|
&Ohc->ControllerNameTable,
|
|
L"Usb Universal Host Controller",
|
|
FALSE
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
UNINSTALL_USBHC:
|
|
gBS->UninstallMultipleProtocolInterfaces (
|
|
Controller,
|
|
&gEfiUsbHcProtocolGuid,
|
|
&Ohc->UsbHc,
|
|
NULL
|
|
);
|
|
|
|
FREE_OHC:
|
|
OhciFreeDev (Ohc);
|
|
|
|
CLOSE_PCIIO:
|
|
if (PciAttributesSaved) {
|
|
//
|
|
// Restore original PCI attributes
|
|
//
|
|
PciIo->Attributes (
|
|
PciIo,
|
|
EfiPciIoAttributeOperationSet,
|
|
OriginalPciAttributes,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Stop this driver on ControllerHandle. Support stoping any child handles
|
|
created by this driver.
|
|
|
|
@param This Protocol instance pointer.
|
|
@param Controller Handle of device to stop driver on.
|
|
@param NumberOfChildren Number of Children in the ChildHandleBuffer.
|
|
@param ChildHandleBuffer List of handles for the children we need to stop.
|
|
|
|
@return EFI_SUCCESS
|
|
@return others
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OHCIDriverBindingStop (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN UINTN NumberOfChildren,
|
|
IN EFI_HANDLE *ChildHandleBuffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_USB_HC_PROTOCOL *UsbHc;
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiUsbHcProtocolGuid,
|
|
(VOID **)&UsbHc,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
OhciCleanDevUp(Controller, UsbHc);
|
|
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|