/** @file | |
The UHCI driver model and HC protocol routines. | |
Copyright (c) 2004 - 2009, Intel Corporation | |
All rights reserved. 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 "Uhci.h" | |
EFI_DRIVER_BINDING_PROTOCOL gUhciDriverBinding = { | |
UhciDriverBindingSupported, | |
UhciDriverBindingStart, | |
UhciDriverBindingStop, | |
0x20, | |
NULL, | |
NULL | |
}; | |
/** | |
Provides software reset for the USB host controller according to UEFI 2.0 spec. | |
@param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
@param Attributes A bit mask of the reset operation to perform. See | |
below for a list of the supported bit mask values. | |
@return EFI_SUCCESS The reset operation succeeded. | |
@return EFI_INVALID_PARAMETER Attributes is not valid. | |
@return EFI_UNSUPPORTED This type of reset is not currently supported. | |
@return EFI_DEVICE_ERROR Other errors. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2Reset ( | |
IN EFI_USB2_HC_PROTOCOL *This, | |
IN UINT16 Attributes | |
) | |
{ | |
USB_HC_DEV *Uhc; | |
EFI_TPL OldTpl; | |
if ((Attributes == EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG) || | |
(Attributes == EFI_USB_HC_RESET_HOST_WITH_DEBUG)) { | |
return EFI_UNSUPPORTED; | |
} | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
OldTpl = gBS->RaiseTPL (UHCI_TPL); | |
switch (Attributes) { | |
case EFI_USB_HC_RESET_GLOBAL: | |
// | |
// Stop schedule and set the Global Reset bit in the command register | |
// | |
UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); | |
UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET); | |
gBS->Stall (UHC_ROOT_PORT_RESET_STALL); | |
// | |
// Clear the Global Reset bit to zero. | |
// | |
UhciClearRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET); | |
gBS->Stall (UHC_ROOT_PORT_RECOVERY_STALL); | |
break; | |
case EFI_USB_HC_RESET_HOST_CONTROLLER: | |
// | |
// Stop schedule and set Host Controller Reset bit to 1 | |
// | |
UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); | |
UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_HCRESET); | |
gBS->Stall (UHC_ROOT_PORT_RECOVERY_STALL); | |
break; | |
default: | |
goto ON_INVAILD_PARAMETER; | |
} | |
// | |
// Delete all old transactions on the USB bus, then | |
// reinitialize the frame list | |
// | |
UhciFreeAllAsyncReq (Uhc); | |
UhciDestoryFrameList (Uhc); | |
UhciInitFrameList (Uhc); | |
gBS->RestoreTPL (OldTpl); | |
return EFI_SUCCESS; | |
ON_INVAILD_PARAMETER: | |
gBS->RestoreTPL (OldTpl); | |
return EFI_INVALID_PARAMETER; | |
} | |
/** | |
Retrieves current state of the USB host controller according to UEFI 2.0 spec. | |
@param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
@param State Variable to receive current device state. | |
@return EFI_SUCCESS The state is returned. | |
@return EFI_INVALID_PARAMETER State is not valid. | |
@return EFI_DEVICE_ERROR Other errors. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2GetState ( | |
IN CONST EFI_USB2_HC_PROTOCOL *This, | |
OUT EFI_USB_HC_STATE *State | |
) | |
{ | |
USB_HC_DEV *Uhc; | |
UINT16 UsbSts; | |
UINT16 UsbCmd; | |
if (State == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); | |
UsbSts = UhciReadReg (Uhc->PciIo, USBSTS_OFFSET); | |
if ((UsbCmd & USBCMD_EGSM) !=0 ) { | |
*State = EfiUsbHcStateSuspend; | |
} else if ((UsbSts & USBSTS_HCH) != 0) { | |
*State = EfiUsbHcStateHalt; | |
} else { | |
*State = EfiUsbHcStateOperational; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Sets the USB host controller to a specific state according to UEFI 2.0 spec. | |
@param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
@param State Indicates the state of the host controller that will | |
be set. | |
@return EFI_SUCCESS Host controller was successfully placed in the state. | |
@return EFI_INVALID_PARAMETER State is invalid. | |
@return EFI_DEVICE_ERROR Failed to set the state. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2SetState ( | |
IN EFI_USB2_HC_PROTOCOL *This, | |
IN EFI_USB_HC_STATE State | |
) | |
{ | |
EFI_USB_HC_STATE CurState; | |
USB_HC_DEV *Uhc; | |
EFI_TPL OldTpl; | |
EFI_STATUS Status; | |
UINT16 UsbCmd; | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
Status = Uhci2GetState (This, &CurState); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
if (CurState == State) { | |
return EFI_SUCCESS; | |
} | |
Status = EFI_SUCCESS; | |
OldTpl = gBS->RaiseTPL (UHCI_TPL); | |
switch (State) { | |
case EfiUsbHcStateHalt: | |
Status = UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); | |
break; | |
case EfiUsbHcStateOperational: | |
UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); | |
if (CurState == EfiUsbHcStateHalt) { | |
// | |
// Set Run/Stop bit to 1, also set the bandwidht reclamation | |
// point to 64 bytes | |
// | |
UsbCmd |= USBCMD_RS | USBCMD_MAXP; | |
UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); | |
} else if (CurState == EfiUsbHcStateSuspend) { | |
// | |
// If FGR(Force Global Resume) bit is 0, set it | |
// | |
if ((UsbCmd & USBCMD_FGR) == 0) { | |
UsbCmd |= USBCMD_FGR; | |
UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); | |
} | |
// | |
// wait 20ms to let resume complete (20ms is specified by UHCI spec) | |
// | |
gBS->Stall (UHC_FORCE_GLOBAL_RESUME_STALL); | |
// | |
// Write FGR bit to 0 and EGSM(Enter Global Suspend Mode) bit to 0 | |
// | |
UsbCmd &= ~USBCMD_FGR; | |
UsbCmd &= ~USBCMD_EGSM; | |
UsbCmd |= USBCMD_RS; | |
UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); | |
} | |
break; | |
case EfiUsbHcStateSuspend: | |
Status = Uhci2SetState (This, EfiUsbHcStateHalt); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto ON_EXIT; | |
} | |
// | |
// Set Enter Global Suspend Mode bit to 1. | |
// | |
UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); | |
UsbCmd |= USBCMD_EGSM; | |
UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); | |
break; | |
default: | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
ON_EXIT: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Retrieves capabilities of USB host controller according to UEFI 2.0 spec. | |
@param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
@param MaxSpeed A pointer to the max speed USB host controller | |
supports. | |
@param PortNumber A pointer to the number of root hub ports. | |
@param Is64BitCapable A pointer to an integer to show whether USB host | |
controller supports 64-bit memory addressing. | |
@return EFI_SUCCESS capabilities were retrieved successfully. | |
@return EFI_INVALID_PARAMETER MaxSpeed or PortNumber or Is64BitCapable is NULL. | |
@return EFI_DEVICE_ERROR An error was encountered. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2GetCapability ( | |
IN EFI_USB2_HC_PROTOCOL *This, | |
OUT UINT8 *MaxSpeed, | |
OUT UINT8 *PortNumber, | |
OUT UINT8 *Is64BitCapable | |
) | |
{ | |
USB_HC_DEV *Uhc; | |
UINT32 Offset; | |
UINT16 PortSC; | |
UINT32 Index; | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
if ((NULL == MaxSpeed) || (NULL == PortNumber) || (NULL == Is64BitCapable)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*MaxSpeed = EFI_USB_SPEED_FULL; | |
*Is64BitCapable = (UINT8) FALSE; | |
*PortNumber = 0; | |
for (Index = 0; Index < USB_MAX_ROOTHUB_PORT; Index++) { | |
Offset = USBPORTSC_OFFSET + Index * 2; | |
PortSC = UhciReadReg (Uhc->PciIo, Offset); | |
// | |
// Port status's bit 7 is reserved and always returns 1 if | |
// the port number is valid. Intel's UHCI (in EHCI controller) | |
// returns 0 in this bit if port number is invalid. Also, if | |
// PciIo IoRead returns error, 0xFFFF is returned to caller. | |
// | |
if (((PortSC & 0x80) == 0) || (PortSC == 0xFFFF)) { | |
break; | |
} | |
(*PortNumber)++; | |
} | |
Uhc->RootPorts = *PortNumber; | |
DEBUG ((EFI_D_INFO, "Uhci2GetCapability: %d ports\n", (UINT32)Uhc->RootPorts)); | |
return EFI_SUCCESS; | |
} | |
/** | |
Retrieves the current status of a USB root hub port according to UEFI 2.0 spec. | |
@param This A pointer to the EFI_USB2_HC_PROTOCOL. | |
@param PortNumber The port to get status. | |
@param PortStatus A pointer to the current port status bits and port | |
status change bits. | |
@return EFI_SUCCESS status of the USB root hub port was returned in PortStatus. | |
@return EFI_INVALID_PARAMETER PortNumber is invalid. | |
@return EFI_DEVICE_ERROR Can't read register. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2GetRootHubPortStatus ( | |
IN CONST EFI_USB2_HC_PROTOCOL *This, | |
IN CONST UINT8 PortNumber, | |
OUT EFI_USB_PORT_STATUS *PortStatus | |
) | |
{ | |
USB_HC_DEV *Uhc; | |
UINT32 Offset; | |
UINT16 PortSC; | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
if (PortStatus == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (PortNumber >= Uhc->RootPorts) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Offset = USBPORTSC_OFFSET + PortNumber * 2; | |
PortStatus->PortStatus = 0; | |
PortStatus->PortChangeStatus = 0; | |
PortSC = UhciReadReg (Uhc->PciIo, Offset); | |
if ((PortSC & USBPORTSC_CCS) != 0) { | |
PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION; | |
} | |
if ((PortSC & USBPORTSC_PED) != 0) { | |
PortStatus->PortStatus |= USB_PORT_STAT_ENABLE; | |
} | |
if ((PortSC & USBPORTSC_SUSP) != 0) { | |
DEBUG ((EFI_D_INFO, "Uhci2GetRootHubPortStatus: port %d is suspended\n", PortNumber)); | |
PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND; | |
} | |
if ((PortSC & USBPORTSC_PR) != 0) { | |
PortStatus->PortStatus |= USB_PORT_STAT_RESET; | |
} | |
if ((PortSC & USBPORTSC_LSDA) != 0) { | |
PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED; | |
} | |
// | |
// CHC will always return one in port owner bit | |
// | |
PortStatus->PortStatus |= USB_PORT_STAT_OWNER; | |
if ((PortSC & USBPORTSC_CSC) != 0) { | |
PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION; | |
} | |
if ((PortSC & USBPORTSC_PEDC) != 0) { | |
PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Sets a feature for the specified root hub port according to UEFI 2.0 spec. | |
@param This A pointer to the EFI_USB2_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. | |
@return EFI_SUCCESS PortFeature was set for the root port. | |
@return EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. | |
@return EFI_DEVICE_ERROR Can't read register. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2SetRootHubPortFeature ( | |
IN EFI_USB2_HC_PROTOCOL *This, | |
IN UINT8 PortNumber, | |
IN EFI_USB_PORT_FEATURE PortFeature | |
) | |
{ | |
USB_HC_DEV *Uhc; | |
EFI_TPL OldTpl; | |
UINT32 Offset; | |
UINT16 PortSC; | |
UINT16 Command; | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
if (PortNumber >= Uhc->RootPorts) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Offset = USBPORTSC_OFFSET + PortNumber * 2; | |
OldTpl = gBS->RaiseTPL (UHCI_TPL); | |
PortSC = UhciReadReg (Uhc->PciIo, Offset); | |
switch (PortFeature) { | |
case EfiUsbPortSuspend: | |
Command = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); | |
if ((Command & USBCMD_EGSM) == 0) { | |
// | |
// if global suspend is not active, can set port suspend | |
// | |
PortSC &= 0xfff5; | |
PortSC |= USBPORTSC_SUSP; | |
} | |
break; | |
case EfiUsbPortReset: | |
PortSC &= 0xfff5; | |
PortSC |= USBPORTSC_PR; | |
break; | |
case EfiUsbPortPower: | |
// | |
// No action | |
// | |
break; | |
case EfiUsbPortEnable: | |
PortSC &= 0xfff5; | |
PortSC |= USBPORTSC_PED; | |
break; | |
default: | |
gBS->RestoreTPL (OldTpl); | |
return EFI_INVALID_PARAMETER; | |
} | |
UhciWriteReg (Uhc->PciIo, Offset, PortSC); | |
gBS->RestoreTPL (OldTpl); | |
return EFI_SUCCESS; | |
} | |
/** | |
Clears a feature for the specified root hub port according to Uefi 2.0 spec. | |
@param This A pointer to the EFI_USB2_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. | |
@return EFI_SUCCESS PortFeature was cleared for the USB root hub port. | |
@return EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. | |
@return EFI_DEVICE_ERROR Can't read register. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2ClearRootHubPortFeature ( | |
IN EFI_USB2_HC_PROTOCOL *This, | |
IN UINT8 PortNumber, | |
IN EFI_USB_PORT_FEATURE PortFeature | |
) | |
{ | |
USB_HC_DEV *Uhc; | |
EFI_TPL OldTpl; | |
UINT32 Offset; | |
UINT16 PortSC; | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
if (PortNumber >= Uhc->RootPorts) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Offset = USBPORTSC_OFFSET + PortNumber * 2; | |
OldTpl = gBS->RaiseTPL (UHCI_TPL); | |
PortSC = UhciReadReg (Uhc->PciIo, Offset); | |
switch (PortFeature) { | |
case EfiUsbPortEnable: | |
PortSC &= 0xfff5; | |
PortSC &= ~USBPORTSC_PED; | |
break; | |
case EfiUsbPortSuspend: | |
// | |
// Cause a resume on the specified port if in suspend mode. | |
// | |
PortSC &= 0xfff5; | |
PortSC &= ~USBPORTSC_SUSP; | |
break; | |
case EfiUsbPortPower: | |
// | |
// No action | |
// | |
break; | |
case EfiUsbPortReset: | |
PortSC &= 0xfff5; | |
PortSC &= ~USBPORTSC_PR; | |
break; | |
case EfiUsbPortConnectChange: | |
PortSC &= 0xfff5; | |
PortSC |= USBPORTSC_CSC; | |
break; | |
case EfiUsbPortEnableChange: | |
PortSC &= 0xfff5; | |
PortSC |= USBPORTSC_PEDC; | |
break; | |
case EfiUsbPortSuspendChange: | |
// | |
// Root hub does not support this | |
// | |
break; | |
case EfiUsbPortOverCurrentChange: | |
// | |
// Root hub does not support this | |
// | |
break; | |
case EfiUsbPortResetChange: | |
// | |
// Root hub does not support this | |
// | |
break; | |
default: | |
gBS->RestoreTPL (OldTpl); | |
return EFI_INVALID_PARAMETER; | |
} | |
UhciWriteReg (Uhc->PciIo, Offset, PortSC); | |
gBS->RestoreTPL (OldTpl); | |
return EFI_SUCCESS; | |
} | |
/** | |
Submits control transfer to a target USB device accroding to UEFI 2.0 spec. | |
@param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
@param DeviceAddress Target device address. | |
@param DeviceSpeed Device speed. | |
@param MaximumPacketLength Maximum packet size of the target endpoint. | |
@param Request USB device request to send. | |
@param TransferDirection Data direction of the Data stage in control transfer. | |
@param Data Data to transmit/receive in data stage. | |
@param DataLength Length of the data. | |
@param TimeOut Maximum time, in microseconds, for transfer to complete. | |
@param Translator Transaction translator to be used by this device. | |
@param TransferResult Variable to receive the transfer result. | |
@return EFI_SUCCESS The control transfer was completed successfully. | |
@return EFI_OUT_OF_RESOURCES Failed due to lack of resource. | |
@return EFI_INVALID_PARAMETER Some parameters are invalid. | |
@return EFI_TIMEOUT Failed due to timeout. | |
@return EFI_DEVICE_ERROR Failed due to host controller or device error. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2ControlTransfer ( | |
IN EFI_USB2_HC_PROTOCOL *This, | |
IN UINT8 DeviceAddress, | |
IN UINT8 DeviceSpeed, | |
IN UINTN MaximumPacketLength, | |
IN EFI_USB_DEVICE_REQUEST *Request, | |
IN EFI_USB_DATA_DIRECTION TransferDirection, | |
IN OUT VOID *Data, | |
IN OUT UINTN *DataLength, | |
IN UINTN TimeOut, | |
IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, | |
OUT UINT32 *TransferResult | |
) | |
{ | |
USB_HC_DEV *Uhc; | |
UHCI_TD_SW *TDs; | |
EFI_TPL OldTpl; | |
EFI_STATUS Status; | |
UHCI_QH_RESULT QhResult; | |
UINT8 PktId; | |
UINT8 *RequestPhy; | |
VOID *RequestMap; | |
UINT8 *DataPhy; | |
VOID *DataMap; | |
BOOLEAN IsSlowDevice; | |
UINTN TransferDataLength; | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
TDs = NULL; | |
DataPhy = NULL; | |
DataMap = NULL; | |
RequestPhy = NULL; | |
RequestMap = NULL; | |
IsSlowDevice = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE); | |
// | |
// Parameters Checking | |
// | |
if (Request == NULL || TransferResult == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (IsSlowDevice && (MaximumPacketLength != 8)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) && | |
(MaximumPacketLength != 32) && (MaximumPacketLength != 64)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((TransferDirection != EfiUsbNoData) && (Data == NULL || DataLength == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (TransferDirection == EfiUsbNoData) { | |
TransferDataLength = 0; | |
} else { | |
TransferDataLength = *DataLength; | |
} | |
*TransferResult = EFI_USB_ERR_SYSTEM; | |
Status = EFI_DEVICE_ERROR; | |
// | |
// If errors exist that cause host controller halt, | |
// clear status then return EFI_DEVICE_ERROR. | |
// | |
UhciAckAllInterrupt (Uhc); | |
if (!UhciIsHcWorking (Uhc->PciIo)) { | |
return EFI_DEVICE_ERROR; | |
} | |
OldTpl = gBS->RaiseTPL (UHCI_TPL); | |
// | |
// Map the Request and data for bus master access, | |
// then create a list of TD for this transfer | |
// | |
Status = UhciMapUserRequest (Uhc, Request, &RequestPhy, &RequestMap); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
Status = UhciMapUserData (Uhc, TransferDirection, Data, DataLength, &PktId, &DataPhy, &DataMap); | |
if (EFI_ERROR (Status)) { | |
Uhc->PciIo->Unmap (Uhc->PciIo, RequestMap); | |
goto ON_EXIT; | |
} | |
TDs = UhciCreateCtrlTds ( | |
Uhc, | |
DeviceAddress, | |
PktId, | |
RequestPhy, | |
DataPhy, | |
TransferDataLength, | |
(UINT8) MaximumPacketLength, | |
IsSlowDevice | |
); | |
if (TDs == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto UNMAP_DATA; | |
} | |
// | |
// According to the speed of the end point, link | |
// the TD to corrosponding queue head, then check | |
// the execution result | |
// | |
UhciLinkTdToQh (Uhc->CtrlQh, TDs); | |
Status = UhciExecuteTransfer (Uhc, Uhc->CtrlQh, TDs, TimeOut, IsSlowDevice, &QhResult); | |
UhciUnlinkTdFromQh (Uhc->CtrlQh, TDs); | |
Uhc->PciIo->Flush (Uhc->PciIo); | |
*TransferResult = QhResult.Result; | |
if (DataLength != NULL) { | |
*DataLength = QhResult.Complete; | |
} | |
UhciDestoryTds (Uhc, TDs); | |
UNMAP_DATA: | |
Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); | |
Uhc->PciIo->Unmap (Uhc->PciIo, RequestMap); | |
ON_EXIT: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Submits bulk transfer to a bulk endpoint of a USB device. | |
@param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
@param DeviceAddress Target device address. | |
@param EndPointAddress Endpoint number and direction. | |
@param DeviceSpeed Device speed. | |
@param MaximumPacketLength Maximum packet size of the target endpoint. | |
@param DataBuffersNumber Number of data buffers prepared for the transfer. | |
@param Data Array of pointers to the buffers of data. | |
@param DataLength On input, size of the data buffer, On output, | |
actually transferred data size. | |
@param DataToggle On input, data toggle to use; On output, next data toggle. | |
@param TimeOut Maximum time out, in microseconds. | |
@param Translator A pointr to the transaction translator data. | |
@param TransferResult Variable to receive transfer result. | |
@return EFI_SUCCESS The bulk transfer was completed successfully. | |
@return EFI_OUT_OF_RESOURCES Failed due to lack of resource. | |
@return EFI_INVALID_PARAMETER Some parameters are invalid. | |
@return EFI_TIMEOUT Failed due to timeout. | |
@return EFI_DEVICE_ERROR Failed due to host controller or device error. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2BulkTransfer ( | |
IN EFI_USB2_HC_PROTOCOL *This, | |
IN UINT8 DeviceAddress, | |
IN UINT8 EndPointAddress, | |
IN UINT8 DeviceSpeed, | |
IN UINTN MaximumPacketLength, | |
IN UINT8 DataBuffersNumber, | |
IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM], | |
IN OUT UINTN *DataLength, | |
IN OUT UINT8 *DataToggle, | |
IN UINTN TimeOut, | |
IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, | |
OUT UINT32 *TransferResult | |
) | |
{ | |
EFI_USB_DATA_DIRECTION Direction; | |
EFI_TPL OldTpl; | |
USB_HC_DEV *Uhc; | |
UHCI_TD_SW *TDs; | |
UHCI_QH_SW *BulkQh; | |
UHCI_QH_RESULT QhResult; | |
EFI_STATUS Status; | |
UINT8 PktId; | |
UINT8 *DataPhy; | |
VOID *DataMap; | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
DataPhy = NULL; | |
DataMap = NULL; | |
if (DeviceSpeed == EFI_USB_SPEED_LOW) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((DataLength == NULL) || (*DataLength == 0) || (Data == NULL) || (TransferResult == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((*DataToggle != 1) && (*DataToggle != 0)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) && | |
(MaximumPacketLength != 32) && (MaximumPacketLength != 64)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*TransferResult = EFI_USB_ERR_SYSTEM; | |
Status = EFI_OUT_OF_RESOURCES; | |
// | |
// If has errors that cause host controller halt, | |
// then return EFI_DEVICE_ERROR directly. | |
// | |
UhciAckAllInterrupt (Uhc); | |
if (!UhciIsHcWorking (Uhc->PciIo)) { | |
return EFI_DEVICE_ERROR; | |
} | |
OldTpl = gBS->RaiseTPL (UHCI_TPL); | |
// | |
// Map the source data buffer for bus master access, | |
// then create a list of TDs | |
// | |
if ((EndPointAddress & 0x80) != 0) { | |
Direction = EfiUsbDataIn; | |
} else { | |
Direction = EfiUsbDataOut; | |
} | |
Status = UhciMapUserData (Uhc, Direction, *Data, DataLength, &PktId, &DataPhy, &DataMap); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
Status = EFI_OUT_OF_RESOURCES; | |
TDs = UhciCreateBulkOrIntTds ( | |
Uhc, | |
DeviceAddress, | |
EndPointAddress, | |
PktId, | |
DataPhy, | |
*DataLength, | |
DataToggle, | |
(UINT8) MaximumPacketLength, | |
FALSE | |
); | |
if (TDs == NULL) { | |
Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); | |
goto ON_EXIT; | |
} | |
// | |
// Link the TDs to bulk queue head. According to the platfore | |
// defintion of UHCI_NO_BW_RECLAMATION, BulkQh is either configured | |
// to do full speed bandwidth reclamation or not. | |
// | |
BulkQh = Uhc->BulkQh; | |
UhciLinkTdToQh (BulkQh, TDs); | |
Status = UhciExecuteTransfer (Uhc, BulkQh, TDs, TimeOut, FALSE, &QhResult); | |
UhciUnlinkTdFromQh (BulkQh, TDs); | |
Uhc->PciIo->Flush (Uhc->PciIo); | |
*TransferResult = QhResult.Result; | |
*DataToggle = QhResult.NextToggle; | |
*DataLength = QhResult.Complete; | |
UhciDestoryTds (Uhc, TDs); | |
Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); | |
ON_EXIT: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Submits an asynchronous interrupt transfer to an | |
interrupt endpoint of a USB device according to UEFI 2.0 spec. | |
@param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
@param DeviceAddress Target device address. | |
@param EndPointAddress Endpoint number and direction. | |
@param DeviceSpeed Device speed. | |
@param MaximumPacketLength Maximum packet size of the target endpoint. | |
@param IsNewTransfer If TRUE, submit a new transfer, if FALSE cancel old transfer. | |
@param DataToggle On input, data toggle to use; On output, next data toggle. | |
@param PollingInterval Interrupt poll rate in milliseconds. | |
@param DataLength On input, size of the data buffer, On output, | |
actually transferred data size. | |
@param Translator A pointr to the transaction translator data. | |
@param CallBackFunction Function to call periodically. | |
@param Context User context. | |
@return EFI_SUCCESS Transfer was submitted. | |
@return EFI_INVALID_PARAMETER Some parameters are invalid. | |
@return EFI_OUT_OF_RESOURCES Failed due to a lack of resources. | |
@return EFI_DEVICE_ERROR Can't read register. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2AsyncInterruptTransfer ( | |
IN EFI_USB2_HC_PROTOCOL *This, | |
IN UINT8 DeviceAddress, | |
IN UINT8 EndPointAddress, | |
IN UINT8 DeviceSpeed, | |
IN UINTN MaximumPacketLength, | |
IN BOOLEAN IsNewTransfer, | |
IN OUT UINT8 *DataToggle, | |
IN UINTN PollingInterval, | |
IN UINTN DataLength, | |
IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, | |
IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, | |
IN VOID *Context | |
) | |
{ | |
USB_HC_DEV *Uhc; | |
BOOLEAN IsSlowDevice; | |
UHCI_QH_SW *Qh; | |
UHCI_TD_SW *IntTds; | |
EFI_TPL OldTpl; | |
EFI_STATUS Status; | |
UINT8 *DataPtr; | |
UINT8 *DataPhy; | |
VOID *DataMap; | |
UINT8 PktId; | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
Qh = NULL; | |
IntTds = NULL; | |
DataPtr = NULL; | |
DataPhy = NULL; | |
DataMap = NULL; | |
IsSlowDevice = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE); | |
if ((EndPointAddress & 0x80) == 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Delete Async interrupt transfer request | |
// | |
if (!IsNewTransfer) { | |
OldTpl = gBS->RaiseTPL (UHCI_TPL); | |
Status = UhciRemoveAsyncReq (Uhc, DeviceAddress, EndPointAddress, DataToggle); | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
if (PollingInterval < 1 || PollingInterval > 255) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (DataLength == 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((*DataToggle != 1) && (*DataToggle != 0)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// If has errors that cause host controller halt, | |
// then return EFI_DEVICE_ERROR directly. | |
// | |
UhciAckAllInterrupt (Uhc); | |
if (!UhciIsHcWorking (Uhc->PciIo)) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Allocate and map source data buffer for bus master access. | |
// | |
DataPtr = AllocatePool (DataLength); | |
if (DataPtr == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
OldTpl = gBS->RaiseTPL (UHCI_TPL); | |
// | |
// Map the user data then create a queue head and | |
// list of TD for it. | |
// | |
Status = UhciMapUserData ( | |
Uhc, | |
EfiUsbDataIn, | |
DataPtr, | |
&DataLength, | |
&PktId, | |
&DataPhy, | |
&DataMap | |
); | |
if (EFI_ERROR (Status)) { | |
goto FREE_DATA; | |
} | |
Qh = UhciCreateQh (Uhc, PollingInterval); | |
if (Qh == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto UNMAP_DATA; | |
} | |
IntTds = UhciCreateBulkOrIntTds ( | |
Uhc, | |
DeviceAddress, | |
EndPointAddress, | |
PktId, | |
DataPhy, | |
DataLength, | |
DataToggle, | |
(UINT8) MaximumPacketLength, | |
IsSlowDevice | |
); | |
if (IntTds == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto DESTORY_QH; | |
} | |
UhciLinkTdToQh (Qh, IntTds); | |
// | |
// Save QH-TD structures to async Interrupt transfer list, | |
// for monitor interrupt transfer execution routine use. | |
// | |
Status = UhciCreateAsyncReq ( | |
Uhc, | |
Qh, | |
IntTds, | |
DeviceAddress, | |
EndPointAddress, | |
DataLength, | |
PollingInterval, | |
DataMap, | |
DataPtr, | |
CallBackFunction, | |
Context, | |
IsSlowDevice | |
); | |
if (EFI_ERROR (Status)) { | |
goto DESTORY_QH; | |
} | |
UhciLinkQhToFrameList (Uhc->FrameBase, Qh); | |
gBS->RestoreTPL (OldTpl); | |
return EFI_SUCCESS; | |
DESTORY_QH: | |
UsbHcFreeMem (Uhc->MemPool, Qh, sizeof (UHCI_QH_SW)); | |
UNMAP_DATA: | |
Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); | |
FREE_DATA: | |
gBS->FreePool (DataPtr); | |
Uhc->PciIo->Flush (Uhc->PciIo); | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Submits synchronous interrupt transfer to an interrupt endpoint | |
of a USB device according to UEFI 2.0 spec. | |
@param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
@param DeviceAddress Target device address. | |
@param EndPointAddress Endpoint number and direction. | |
@param DeviceSpeed Device speed. | |
@param MaximumPacketLength Maximum packet size of the target endpoint. | |
@param Data Array of pointers to the buffers of data. | |
@param DataLength On input, size of the data buffer, On output, | |
actually transferred data size. | |
@param DataToggle On input, data toggle to use; On output, next data toggle. | |
@param TimeOut Maximum time out, in microseconds. | |
@param Translator A pointr to the transaction translator data. | |
@param TransferResult Variable to receive transfer result. | |
@return EFI_SUCCESS The transfer was completed successfully. | |
@return EFI_OUT_OF_RESOURCES Failed due to lack of resource. | |
@return EFI_INVALID_PARAMETER Some parameters are invalid. | |
@return EFI_TIMEOUT Failed due to timeout. | |
@return EFI_DEVICE_ERROR Failed due to host controller or device error. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2SyncInterruptTransfer ( | |
IN EFI_USB2_HC_PROTOCOL *This, | |
IN UINT8 DeviceAddress, | |
IN UINT8 EndPointAddress, | |
IN UINT8 DeviceSpeed, | |
IN UINTN MaximumPacketLength, | |
IN OUT VOID *Data, | |
IN OUT UINTN *DataLength, | |
IN OUT UINT8 *DataToggle, | |
IN UINTN TimeOut, | |
IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, | |
OUT UINT32 *TransferResult | |
) | |
{ | |
EFI_STATUS Status; | |
USB_HC_DEV *Uhc; | |
UHCI_TD_SW *TDs; | |
UHCI_QH_RESULT QhResult; | |
EFI_TPL OldTpl; | |
UINT8 *DataPhy; | |
VOID *DataMap; | |
UINT8 PktId; | |
BOOLEAN IsSlowDevice; | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
DataPhy = NULL; | |
DataMap = NULL; | |
TDs = NULL; | |
if (DeviceSpeed == EFI_USB_SPEED_HIGH) { | |
return EFI_INVALID_PARAMETER; | |
} | |
IsSlowDevice = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE); | |
if ((DataLength == NULL) || (Data == NULL) || (TransferResult == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((EndPointAddress & 0x80) == 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((*DataToggle != 1) && (*DataToggle != 0)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((*DataLength == 0) || (MaximumPacketLength > 64)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (IsSlowDevice && (MaximumPacketLength > 8)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*TransferResult = EFI_USB_ERR_SYSTEM; | |
Status = EFI_DEVICE_ERROR; | |
UhciAckAllInterrupt (Uhc); | |
if (!UhciIsHcWorking (Uhc->PciIo)) { | |
return Status; | |
} | |
OldTpl = gBS->RaiseTPL (UHCI_TPL); | |
// | |
// Map the source data buffer for bus master access. | |
// Create Tds list, then link it to the UHC's interrupt list | |
// | |
Status = UhciMapUserData ( | |
Uhc, | |
EfiUsbDataIn, | |
Data, | |
DataLength, | |
&PktId, | |
&DataPhy, | |
&DataMap | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
TDs = UhciCreateBulkOrIntTds ( | |
Uhc, | |
DeviceAddress, | |
EndPointAddress, | |
PktId, | |
DataPhy, | |
*DataLength, | |
DataToggle, | |
(UINT8) MaximumPacketLength, | |
IsSlowDevice | |
); | |
if (TDs == NULL) { | |
Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
UhciLinkTdToQh (Uhc->SyncIntQh, TDs); | |
Status = UhciExecuteTransfer (Uhc, Uhc->SyncIntQh, TDs, TimeOut, IsSlowDevice, &QhResult); | |
UhciUnlinkTdFromQh (Uhc->SyncIntQh, TDs); | |
Uhc->PciIo->Flush (Uhc->PciIo); | |
*TransferResult = QhResult.Result; | |
*DataToggle = QhResult.NextToggle; | |
*DataLength = QhResult.Complete; | |
UhciDestoryTds (Uhc, TDs); | |
Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); | |
ON_EXIT: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Submits isochronous transfer to a target USB device according to UEFI 2.0 spec. | |
@param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
@param DeviceAddress Target device address. | |
@param EndPointAddress Endpoint number and direction. | |
@param DeviceSpeed Device speed. | |
@param MaximumPacketLength Maximum packet size of the target endpoint. | |
@param DataBuffersNumber Number of data buffers prepared for the transfer. | |
@param Data Array of pointers to the buffers of data. | |
@param DataLength On input, size of the data buffer, On output, | |
actually transferred data size. | |
@param Translator A pointr to the transaction translator data. | |
@param TransferResult Variable to receive transfer result. | |
@return EFI_UNSUPPORTED | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2IsochronousTransfer ( | |
IN EFI_USB2_HC_PROTOCOL *This, | |
IN UINT8 DeviceAddress, | |
IN UINT8 EndPointAddress, | |
IN UINT8 DeviceSpeed, | |
IN UINTN MaximumPacketLength, | |
IN UINT8 DataBuffersNumber, | |
IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], | |
IN UINTN DataLength, | |
IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, | |
OUT UINT32 *TransferResult | |
) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
/** | |
Submits Async isochronous transfer to a target USB device according to UEFI 2.0 spec. | |
@param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
@param DeviceAddress Target device address. | |
@param EndPointAddress Endpoint number and direction. | |
@param DeviceSpeed Device speed. | |
@param MaximumPacketLength Maximum packet size of the target endpoint. | |
@param DataBuffersNumber Number of data buffers prepared for the transfer. | |
@param Data Array of pointers to the buffers of data. | |
@param DataLength On input, size of the data buffer, On output, | |
actually transferred data size. | |
@param Translator A pointr to the transaction translator data. | |
@param IsochronousCallBack Function to call when the transfer complete. | |
@param Context Pass to the call back function as parameter. | |
@return EFI_UNSUPPORTED | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Uhci2AsyncIsochronousTransfer ( | |
IN EFI_USB2_HC_PROTOCOL *This, | |
IN UINT8 DeviceAddress, | |
IN UINT8 EndPointAddress, | |
IN UINT8 DeviceSpeed, | |
IN UINTN MaximumPacketLength, | |
IN UINT8 DataBuffersNumber, | |
IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], | |
IN UINTN DataLength, | |
IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, | |
IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack, | |
IN VOID *Context | |
) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
/** | |
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 | |
UhciDriverEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
return EfiLibInstallDriverBindingComponentName2 ( | |
ImageHandle, | |
SystemTable, | |
&gUhciDriverBinding, | |
ImageHandle, | |
&gUhciComponentName, | |
&gUhciComponentName2 | |
); | |
} | |
/** | |
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 | |
UhciDriverBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS OpenStatus; | |
EFI_STATUS Status; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
USB_CLASSC UsbClassCReg; | |
// | |
// Test whether there is PCI IO Protocol attached on the controller handle. | |
// | |
OpenStatus = gBS->OpenProtocol ( | |
Controller, | |
&gEfiPciIoProtocolGuid, | |
(VOID **) &PciIo, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (OpenStatus)) { | |
return OpenStatus; | |
} | |
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 UHCI type | |
// | |
if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || | |
(UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) || | |
(UsbClassCReg.PI != PCI_IF_UHCI) | |
) { | |
Status = EFI_UNSUPPORTED; | |
} | |
ON_EXIT: | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiPciIoProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
return Status; | |
} | |
/** | |
Allocate and initialize the empty UHCI device. | |
@param PciIo The PCIIO to use. | |
@param OriginalPciAttributes The original PCI attributes. | |
@return Allocated UHCI device. If err, return NULL. | |
**/ | |
USB_HC_DEV * | |
UhciAllocateDev ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT64 OriginalPciAttributes | |
) | |
{ | |
USB_HC_DEV *Uhc; | |
EFI_STATUS Status; | |
Uhc = AllocateZeroPool (sizeof (USB_HC_DEV)); | |
if (Uhc == NULL) { | |
return NULL; | |
} | |
// | |
// This driver supports both USB_HC_PROTOCOL and USB2_HC_PROTOCOL. | |
// USB_HC_PROTOCOL is for EFI 1.1 backward compability. | |
// | |
Uhc->Signature = USB_HC_DEV_SIGNATURE; | |
Uhc->Usb2Hc.GetCapability = Uhci2GetCapability; | |
Uhc->Usb2Hc.Reset = Uhci2Reset; | |
Uhc->Usb2Hc.GetState = Uhci2GetState; | |
Uhc->Usb2Hc.SetState = Uhci2SetState; | |
Uhc->Usb2Hc.ControlTransfer = Uhci2ControlTransfer; | |
Uhc->Usb2Hc.BulkTransfer = Uhci2BulkTransfer; | |
Uhc->Usb2Hc.AsyncInterruptTransfer = Uhci2AsyncInterruptTransfer; | |
Uhc->Usb2Hc.SyncInterruptTransfer = Uhci2SyncInterruptTransfer; | |
Uhc->Usb2Hc.IsochronousTransfer = Uhci2IsochronousTransfer; | |
Uhc->Usb2Hc.AsyncIsochronousTransfer = Uhci2AsyncIsochronousTransfer; | |
Uhc->Usb2Hc.GetRootHubPortStatus = Uhci2GetRootHubPortStatus; | |
Uhc->Usb2Hc.SetRootHubPortFeature = Uhci2SetRootHubPortFeature; | |
Uhc->Usb2Hc.ClearRootHubPortFeature = Uhci2ClearRootHubPortFeature; | |
Uhc->Usb2Hc.MajorRevision = 0x1; | |
Uhc->Usb2Hc.MinorRevision = 0x1; | |
Uhc->PciIo = PciIo; | |
Uhc->OriginalPciAttributes = OriginalPciAttributes; | |
Uhc->MemPool = UsbHcInitMemPool (PciIo, TRUE, 0); | |
if (Uhc->MemPool == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_ERROR; | |
} | |
InitializeListHead (&Uhc->AsyncIntList); | |
Status = gBS->CreateEvent ( | |
EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
UhciMonitorAsyncReqList, | |
Uhc, | |
&Uhc->AsyncIntMonitor | |
); | |
if (EFI_ERROR (Status)) { | |
UsbHcFreeMemPool (Uhc->MemPool); | |
goto ON_ERROR; | |
} | |
return Uhc; | |
ON_ERROR: | |
FreePool (Uhc); | |
return NULL; | |
} | |
/** | |
Free the UHCI device and release its associated resources. | |
@param Uhc The UHCI device to release. | |
**/ | |
VOID | |
UhciFreeDev ( | |
IN USB_HC_DEV *Uhc | |
) | |
{ | |
if (Uhc->AsyncIntMonitor != NULL) { | |
gBS->CloseEvent (Uhc->AsyncIntMonitor); | |
} | |
if (Uhc->ExitBootServiceEvent != NULL) { | |
gBS->CloseEvent (Uhc->ExitBootServiceEvent); | |
} | |
if (Uhc->MemPool != NULL) { | |
UsbHcFreeMemPool (Uhc->MemPool); | |
} | |
if (Uhc->CtrlNameTable != NULL) { | |
FreeUnicodeStringTable (Uhc->CtrlNameTable); | |
} | |
FreePool (Uhc); | |
} | |
/** | |
Uninstall all Uhci Interface. | |
@param Controller Controller handle. | |
@param This Protocol instance pointer. | |
**/ | |
VOID | |
UhciCleanDevUp ( | |
IN EFI_HANDLE Controller, | |
IN EFI_USB2_HC_PROTOCOL *This | |
) | |
{ | |
USB_HC_DEV *Uhc; | |
// | |
// Uninstall the USB_HC and USB_HC2 protocol, then disable the controller | |
// | |
Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); | |
gBS->UninstallProtocolInterface ( | |
Controller, | |
&gEfiUsb2HcProtocolGuid, | |
&Uhc->Usb2Hc | |
); | |
UhciFreeAllAsyncReq (Uhc); | |
UhciDestoryFrameList (Uhc); | |
// | |
// Restore original PCI attributes | |
// | |
Uhc->PciIo->Attributes ( | |
Uhc->PciIo, | |
EfiPciIoAttributeOperationSet, | |
Uhc->OriginalPciAttributes, | |
NULL | |
); | |
UhciFreeDev (Uhc); | |
} | |
/** | |
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 | |
UhcExitBootService ( | |
EFI_EVENT Event, | |
VOID *Context | |
) | |
{ | |
USB_HC_DEV *Uhc; | |
Uhc = (USB_HC_DEV *) Context; | |
// | |
// Stop the Host Controller | |
// | |
UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); | |
return; | |
} | |
/** | |
Starting the Usb UHCI 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 | |
UhciDriverBindingStart ( | |
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_HC_DEV *Uhc; | |
UINT64 Supports; | |
UINT64 OriginalPciAttributes; | |
BOOLEAN PciAttributesSaved; | |
// | |
// Open PCIIO, then enable the EHC device and turn off emulation | |
// | |
Uhc = 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)) { | |
UhciTurnOffUsbEmulation (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; | |
} | |
Uhc = UhciAllocateDev (PciIo, OriginalPciAttributes); | |
if (Uhc == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto CLOSE_PCIIO; | |
} | |
// | |
// Allocate and Init Host Controller's Frame List Entry | |
// | |
Status = UhciInitFrameList (Uhc); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto FREE_UHC; | |
} | |
Status = gBS->SetTimer ( | |
Uhc->AsyncIntMonitor, | |
TimerPeriodic, | |
UHC_ASYNC_POLL_INTERVAL | |
); | |
if (EFI_ERROR (Status)) { | |
goto FREE_UHC; | |
} | |
// | |
// Install USB2_HC_PROTOCOL | |
// | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&Controller, | |
&gEfiUsb2HcProtocolGuid, | |
&Uhc->Usb2Hc, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
goto FREE_UHC; | |
} | |
// | |
// Create event to stop the HC when exit boot service. | |
// | |
Status = gBS->CreateEventEx ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
UhcExitBootService, | |
Uhc, | |
&gEfiEventExitBootServicesGuid, | |
&Uhc->ExitBootServiceEvent | |
); | |
if (EFI_ERROR (Status)) { | |
goto UNINSTALL_USBHC; | |
} | |
// | |
// Install the component name protocol | |
// | |
Uhc->CtrlNameTable = NULL; | |
AddUnicodeString2 ( | |
"eng", | |
gUhciComponentName.SupportedLanguages, | |
&Uhc->CtrlNameTable, | |
L"Usb Universal Host Controller", | |
TRUE | |
); | |
AddUnicodeString2 ( | |
"en", | |
gUhciComponentName2.SupportedLanguages, | |
&Uhc->CtrlNameTable, | |
L"Usb Universal Host Controller", | |
FALSE | |
); | |
// | |
// Start the UHCI hardware, also set its reclamation point to 64 bytes | |
// | |
UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, USBCMD_RS | USBCMD_MAXP); | |
return EFI_SUCCESS; | |
UNINSTALL_USBHC: | |
gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
&gEfiUsb2HcProtocolGuid, | |
&Uhc->Usb2Hc, | |
NULL | |
); | |
FREE_UHC: | |
UhciFreeDev (Uhc); | |
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 | |
UhciDriverBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
) | |
{ | |
EFI_USB2_HC_PROTOCOL *Usb2Hc; | |
EFI_STATUS Status; | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiUsb2HcProtocolGuid, | |
(VOID **) &Usb2Hc, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
// | |
// Test whether the Controller handler passed in is a valid | |
// Usb controller handle that should be supported, if not, | |
// return the error status directly | |
// | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
UhciCleanDevUp (Controller, Usb2Hc); | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiPciIoProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
return EFI_SUCCESS; | |
} | |