mirror of https://github.com/acidanthera/audk.git
2680 lines
72 KiB
C
2680 lines
72 KiB
C
/*++
|
|
|
|
Copyright (c) 2006, 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.
|
|
|
|
Module Name:
|
|
|
|
Ehci.c
|
|
|
|
Abstract:
|
|
|
|
|
|
Revision History
|
|
--*/
|
|
|
|
|
|
#include "Ehci.h"
|
|
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED UINTN gEHCDebugLevel = EFI_D_ERROR;
|
|
GLOBAL_REMOVE_IF_UNREFERENCED UINTN gEHCErrorLevel = EFI_D_ERROR;
|
|
|
|
|
|
//
|
|
// Ehci Driver Global Variables
|
|
//
|
|
EFI_DRIVER_BINDING_PROTOCOL gEhciDriverBinding = {
|
|
EhciDriverBindingSupported,
|
|
EhciDriverBindingStart,
|
|
EhciDriverBindingStop,
|
|
0xa,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciDriverBindingSupported (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Test to see if this driver supports ControllerHandle. Any ControllerHandle
|
|
that has Usb2HcProtocol installed will be supported.
|
|
|
|
Arguments:
|
|
|
|
This - Protocol instance pointer.
|
|
Controlle - Handle of device to test
|
|
RemainingDevicePath - Not used
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS This driver supports this device.
|
|
EFI_UNSUPPORTED This driver does not support this device.
|
|
|
|
--*/
|
|
{
|
|
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)) {
|
|
goto exit;
|
|
}
|
|
|
|
Status = PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
CLASSC,
|
|
sizeof (USB_CLASSC) / sizeof (UINT8),
|
|
&UsbClassCReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
Status = EFI_UNSUPPORTED;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Test whether the controller belongs to Ehci type
|
|
//
|
|
if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) ||
|
|
(UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) ||
|
|
(UsbClassCReg.PI != PCI_CLASSC_PI_EHCI)
|
|
) {
|
|
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
Status = EFI_UNSUPPORTED;
|
|
goto exit;
|
|
}
|
|
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
exit:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciDriverBindingStart (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Starting the Usb EHCI Driver
|
|
|
|
Arguments:
|
|
|
|
This - Protocol instance pointer.
|
|
Controller - Handle of device to test
|
|
RemainingDevicePath - Not used
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS supports this device.
|
|
EFI_UNSUPPORTED do not support this device.
|
|
EFI_DEVICE_ERROR cannot be started due to device Error
|
|
EFI_OUT_OF_RESOURCES cannot allocate resources
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT8 MaxSpeed;
|
|
UINT8 PortNumber;
|
|
UINT8 Is64BitCapable;
|
|
UINT64 Supports;
|
|
|
|
//
|
|
// Open the PciIo Protocol
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
(VOID **) &PciIo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Enable the USB Host Controller
|
|
//
|
|
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)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto close_pciio_protocol;
|
|
}
|
|
|
|
//
|
|
// Allocate memory for EHC private data structure
|
|
//
|
|
HcDev = AllocateZeroPool (sizeof (USB2_HC_DEV));
|
|
if (NULL == HcDev) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto close_pciio_protocol;
|
|
}
|
|
|
|
//
|
|
// Init EFI_USB2_HC_PROTOCOL interface and private data structure
|
|
//
|
|
HcDev->Usb2Hc.GetCapability = EhciGetCapability;
|
|
HcDev->Usb2Hc.Reset = EhciReset;
|
|
HcDev->Usb2Hc.GetState = EhciGetState;
|
|
HcDev->Usb2Hc.SetState = EhciSetState;
|
|
HcDev->Usb2Hc.ControlTransfer = EhciControlTransfer;
|
|
HcDev->Usb2Hc.BulkTransfer = EhciBulkTransfer;
|
|
HcDev->Usb2Hc.AsyncInterruptTransfer = EhciAsyncInterruptTransfer;
|
|
HcDev->Usb2Hc.SyncInterruptTransfer = EhciSyncInterruptTransfer;
|
|
HcDev->Usb2Hc.IsochronousTransfer = EhciIsochronousTransfer;
|
|
HcDev->Usb2Hc.AsyncIsochronousTransfer = EhciAsyncIsochronousTransfer;
|
|
HcDev->Usb2Hc.GetRootHubPortStatus = EhciGetRootHubPortStatus;
|
|
HcDev->Usb2Hc.SetRootHubPortFeature = EhciSetRootHubPortFeature;
|
|
HcDev->Usb2Hc.ClearRootHubPortFeature = EhciClearRootHubPortFeature;
|
|
HcDev->Usb2Hc.MajorRevision = 0x1;
|
|
HcDev->Usb2Hc.MinorRevision = 0x1;
|
|
|
|
HcDev->AsyncRequestList = NULL;
|
|
HcDev->ControllerNameTable = NULL;
|
|
HcDev->Signature = USB2_HC_DEV_SIGNATURE;
|
|
HcDev->PciIo = PciIo;
|
|
|
|
//
|
|
// Install USB2_HC_PROTOCOL
|
|
//
|
|
Status = gBS->InstallProtocolInterface (
|
|
&Controller,
|
|
&gEfiUsb2HcProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
&HcDev->Usb2Hc
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto free_pool;
|
|
}
|
|
|
|
//
|
|
// Get Capability Register Length
|
|
//
|
|
Status = GetCapabilityLen (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto uninstall_usb2hc_protocol;
|
|
}
|
|
|
|
ClearLegacySupport (HcDev);
|
|
HostReset (HcDev);
|
|
|
|
DEBUG_CODE (
|
|
DumpEHCIPortsStatus (HcDev);
|
|
);
|
|
|
|
//
|
|
// Create and Init Perodic Frame List
|
|
//
|
|
Status = EhciGetCapability (
|
|
&HcDev->Usb2Hc,
|
|
&MaxSpeed,
|
|
&PortNumber,
|
|
&Is64BitCapable
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto uninstall_usb2hc_protocol;
|
|
}
|
|
HcDev->Is64BitCapable = Is64BitCapable;
|
|
|
|
//
|
|
// Create and Init Perodic Frame List
|
|
//
|
|
Status = InitialPeriodicFrameList (
|
|
HcDev,
|
|
EHCI_MAX_FRAME_LIST_LENGTH
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto uninstall_usb2hc_protocol;
|
|
}
|
|
|
|
//
|
|
// Init memory pool management
|
|
//
|
|
Status = InitialMemoryManagement (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto deinit_perodic_frame_list;
|
|
}
|
|
|
|
Status = CreateNULLQH (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto deinit_perodic_frame_list;
|
|
}
|
|
//
|
|
// Create AsyncRequest Polling Timer
|
|
//
|
|
Status = CreatePollingTimer (HcDev, (EFI_EVENT_NOTIFY) AsyncRequestMoniter);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto deinit_null_qh;
|
|
}
|
|
|
|
//
|
|
// Default Maxximum Interrupt Interval is 8,
|
|
// it means that 8 micro frame = 1ms
|
|
//
|
|
|
|
//
|
|
// Start the Host Controller
|
|
//
|
|
if (IsEhcHalted (HcDev)) {
|
|
Status = StartScheduleExecution (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto deinit_timer;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set all ports routing to EHC
|
|
//
|
|
Status = SetPortRoutingEhc (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto deinit_timer;
|
|
}
|
|
|
|
//
|
|
// Component name protocol
|
|
//
|
|
Status = AddUnicodeString (
|
|
"eng",
|
|
gEhciComponentName.SupportedLanguages,
|
|
&HcDev->ControllerNameTable,
|
|
L"Usb Enhanced Host Controller"
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto deinit_timer;
|
|
}
|
|
|
|
goto exit;
|
|
|
|
//
|
|
// Error handle process
|
|
//
|
|
deinit_timer:
|
|
DestoryPollingTimer (HcDev);
|
|
deinit_null_qh:
|
|
DestroyNULLQH(HcDev);
|
|
DeinitialMemoryManagement (HcDev);
|
|
deinit_perodic_frame_list:
|
|
DeinitialPeriodicFrameList (HcDev);
|
|
uninstall_usb2hc_protocol:
|
|
gBS->UninstallProtocolInterface (
|
|
Controller,
|
|
&gEfiUsb2HcProtocolGuid,
|
|
&HcDev->Usb2Hc
|
|
);
|
|
free_pool:
|
|
gBS->FreePool (HcDev);
|
|
close_pciio_protocol:
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
exit:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciDriverBindingStop (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN UINTN NumberOfChildren,
|
|
IN EFI_HANDLE *ChildHandleBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stop this driver on ControllerHandle. Support stoping any child handles
|
|
created by this driver.
|
|
|
|
Arguments:
|
|
|
|
This - Protocol instance pointer.
|
|
Controller - Handle of device to stop driver on
|
|
NumberOfChildren - Number of Children in the ChildHandleBuffer
|
|
ChildHandleBuffer - List of handles for the children we need to stop.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS Success
|
|
EFI_DEVICE_ERROR Fail
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_USB2_HC_PROTOCOL *Usb2Hc;
|
|
USB2_HC_DEV *HcDev;
|
|
UINT64 Supports;
|
|
|
|
//
|
|
// Test whether the Controller handler passed in is a valid
|
|
// Usb controller handle that should be supported, if not,
|
|
// return the error status directly
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiUsb2HcProtocolGuid,
|
|
(VOID **) &Usb2Hc,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
HcDev = USB2_HC_DEV_FROM_THIS (Usb2Hc);
|
|
|
|
//
|
|
// free all the controller related memory and uninstall UHCI Protocol.
|
|
//
|
|
Status = gBS->UninstallProtocolInterface (
|
|
Controller,
|
|
&gEfiUsb2HcProtocolGuid,
|
|
Usb2Hc
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Set Host Controller state as halt
|
|
//
|
|
Status = Usb2Hc->SetState (
|
|
Usb2Hc,
|
|
EfiUsbHcStateHalt
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Stop AsyncRequest Polling Timer
|
|
//
|
|
Status = StopPollingTimer (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Destroy Asynchronous Request Event
|
|
//
|
|
DestoryPollingTimer (HcDev);
|
|
|
|
//
|
|
// Destroy Perodic Frame List
|
|
//
|
|
DeinitialPeriodicFrameList (HcDev);
|
|
|
|
//
|
|
// Destroy NULLQH
|
|
//
|
|
DestroyNULLQH (HcDev);
|
|
|
|
//
|
|
// Deinit Ehci pool memory management
|
|
//
|
|
DeinitialMemoryManagement (HcDev);
|
|
|
|
//
|
|
// Denint Unicode String Table
|
|
//
|
|
FreeUnicodeStringTable (HcDev->ControllerNameTable);
|
|
|
|
//
|
|
// Disable the USB Host Controller
|
|
//
|
|
Status = HcDev->PciIo->Attributes (
|
|
HcDev->PciIo,
|
|
EfiPciIoAttributeOperationSupported,
|
|
0,
|
|
&Supports
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Supports &= EFI_PCI_DEVICE_ENABLE;
|
|
Status = HcDev->PciIo->Attributes (
|
|
HcDev->PciIo,
|
|
EfiPciIoAttributeOperationDisable,
|
|
Supports,
|
|
NULL
|
|
);
|
|
}
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
gBS->FreePool (HcDev);
|
|
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
exit:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciGetCapability (
|
|
IN EFI_USB2_HC_PROTOCOL *This,
|
|
OUT UINT8 *MaxSpeed,
|
|
OUT UINT8 *PortNumber,
|
|
OUT UINT8 *Is64BitCapable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves the capablility of root hub ports.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB_HC_PROTOCOL instance.
|
|
MaxSpeed - A pointer to the number of the host controller.
|
|
PortNumber - A pointer to the number of the root hub ports.
|
|
Is64BitCapable - A pointer to the flag for whether controller supports
|
|
64-bit memory addressing.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS host controller capability were retrieved successfully.
|
|
EFI_INVALID_PARAMETER MaxSpeed or PortNumber or Is64BitCapable is NULL.
|
|
EFI_DEVICE_ERROR An error was encountered while attempting to retrieve the capabilities.
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
UINT32 HcStructParamsAddr;
|
|
UINT32 HcStructParamsReg;
|
|
UINT32 HcCapParamsAddr;
|
|
UINT32 HcCapParamsReg;
|
|
|
|
if (MaxSpeed == NULL || PortNumber == NULL || Is64BitCapable == NULL) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
HcStructParamsAddr = HCSPARAMS;
|
|
HcCapParamsAddr = HCCPARAMS;
|
|
HcDev = USB2_HC_DEV_FROM_THIS (This);
|
|
|
|
Status = ReadEhcCapabiltiyReg (
|
|
HcDev,
|
|
HcStructParamsAddr,
|
|
&HcStructParamsReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
Status = ReadEhcCapabiltiyReg (
|
|
HcDev,
|
|
HcCapParamsAddr,
|
|
&HcCapParamsReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
*MaxSpeed = EFI_USB_SPEED_HIGH;
|
|
*PortNumber = (UINT8) (HcStructParamsReg & HCSP_NPORTS);
|
|
*Is64BitCapable = (UINT8) (HcCapParamsReg & HCCP_64BIT);
|
|
|
|
exit:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciReset (
|
|
IN EFI_USB2_HC_PROTOCOL *This,
|
|
IN UINT16 Attributes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Provides software reset for the USB host controller.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB2_HC_PROTOCOL instance.
|
|
Attributes - A bit mask of the reset operation to perform.
|
|
See below for a list of the supported bit mask values.
|
|
|
|
#define EFI_USB_HC_RESET_GLOBAL 0x0001
|
|
#define EFI_USB_HC_RESET_HOST_CONTROLLER 0x0002
|
|
#define EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG 0x0004
|
|
#define EFI_USB_HC_RESET_HOST_WITH_DEBUG 0x0008
|
|
|
|
EFI_USB_HC_RESET_GLOBAL
|
|
If this bit is set, a global reset signal will be sent to the USB bus.
|
|
This resets all of the USB bus logic, including the USB host
|
|
controller hardware and all the devices attached on the USB bus.
|
|
EFI_USB_HC_RESET_HOST_CONTROLLER
|
|
If this bit is set, the USB host controller hardware will be reset.
|
|
No reset signal will be sent to the USB bus.
|
|
EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG
|
|
If this bit is set, a global reset signal will be sent to the USB bus.
|
|
This resets all of the USB bus logic, including the USB host
|
|
controller hardware and all the devices attached on the USB bus.
|
|
If this is an EHCI controller and the debug port has configured, then
|
|
this is will still reset the host controller.
|
|
EFI_USB_HC_RESET_HOST_WITH_DEBUG
|
|
If this bit is set, the USB host controller hardware will be reset.
|
|
If this is an EHCI controller and the debug port has been configured,
|
|
then this will still reset the host controller.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS
|
|
The reset operation succeeded.
|
|
EFI_INVALID_PARAMETER
|
|
Attributes is not valid.
|
|
EFI_UNSUPPOURTED
|
|
The type of reset specified by Attributes is not currently supported by
|
|
the host controller hardware.
|
|
EFI_ACCESS_DENIED
|
|
Reset operation is rejected due to the debug port being configured and
|
|
active; only EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG or
|
|
EFI_USB_HC_RESET_HOST_WITH_DEBUG reset Atrributes can be used to
|
|
perform reset operation for this host controller.
|
|
EFI_DEVICE_ERROR
|
|
An error was encountered while attempting to perform
|
|
the reset operation.
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
UINTN FrameIndex;
|
|
FRAME_LIST_ENTRY *FrameEntryPtr;
|
|
|
|
HcDev = USB2_HC_DEV_FROM_THIS (This);
|
|
|
|
switch (Attributes) {
|
|
|
|
case EFI_USB_HC_RESET_GLOBAL:
|
|
|
|
//
|
|
// Same behavior as Host Controller Reset
|
|
//
|
|
|
|
case EFI_USB_HC_RESET_HOST_CONTROLLER:
|
|
|
|
//
|
|
// Host Controller must be Halt when Reset it
|
|
//
|
|
if (IsEhcHalted (HcDev)) {
|
|
Status = ResetEhc (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
//
|
|
// Set to zero by Host Controller when reset process completes
|
|
//
|
|
Status = WaitForEhcReset (HcDev, EHCI_GENERIC_TIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_TIMEOUT;
|
|
goto exit;
|
|
}
|
|
} else {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// only asynchronous interrupt transfers are always alive on the bus, need to cleanup
|
|
//
|
|
CleanUpAllAsyncRequestTransfer (HcDev);
|
|
Status = ClearEhcAllStatus (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Set appropriate 4G Segment Selector
|
|
//
|
|
Status = SetCtrlDataStructSeg (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Init Perodic List Base Addr and Frame List
|
|
//
|
|
Status = SetFrameListBaseAddr (
|
|
HcDev,
|
|
(UINT32)GET_0B_TO_31B (HcDev->PeriodicFrameListBuffer)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
FrameEntryPtr = (FRAME_LIST_ENTRY *) HcDev->PeriodicFrameListBuffer;
|
|
for (FrameIndex = 0; FrameIndex < HcDev->PeriodicFrameListLength; FrameIndex++) {
|
|
FrameEntryPtr->LinkTerminate = TRUE;
|
|
FrameEntryPtr++;
|
|
}
|
|
|
|
//
|
|
// Start the Host Controller
|
|
//
|
|
if (IsEhcHalted (HcDev)) {
|
|
Status = StartScheduleExecution (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set all ports routing to EHC
|
|
//
|
|
Status = SetPortRoutingEhc (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
break;
|
|
|
|
case EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG:
|
|
|
|
Status = EFI_UNSUPPORTED;
|
|
break;
|
|
|
|
case EFI_USB_HC_RESET_HOST_WITH_DEBUG:
|
|
|
|
Status = EFI_UNSUPPORTED;
|
|
break;
|
|
|
|
default:
|
|
Status = EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
exit:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciGetState (
|
|
IN EFI_USB2_HC_PROTOCOL *This,
|
|
OUT EFI_USB_HC_STATE *State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves current state of the USB host controller.
|
|
|
|
Arguments:
|
|
|
|
This A pointer to the EFI_USB2_HC_PROTOCOL instance.
|
|
State A pointer to the EFI_USB_HC_STATE data structure that
|
|
indicates current state of the USB host controller.
|
|
Type EFI_USB_HC_STATE is defined below.
|
|
|
|
typedef enum {
|
|
EfiUsbHcStateHalt,
|
|
EfiUsbHcStateOperational,
|
|
EfiUsbHcStateSuspend,
|
|
EfiUsbHcStateMaximum
|
|
} EFI_USB_HC_STATE;
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS
|
|
The state information of the host controller was returned in State.
|
|
EFI_INVALID_PARAMETER
|
|
State is NULL.
|
|
EFI_DEVICE_ERROR
|
|
An error was encountered while attempting to retrieve the
|
|
host controller's current state.
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
UINT32 UsbStatusAddr;
|
|
UINT32 UsbStatusReg;
|
|
|
|
if (State == NULL) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
UsbStatusAddr = USBSTS;
|
|
HcDev = USB2_HC_DEV_FROM_THIS (This);
|
|
|
|
Status = ReadEhcOperationalReg (
|
|
HcDev,
|
|
UsbStatusAddr,
|
|
&UsbStatusReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
if (UsbStatusReg & USBSTS_HCH) {
|
|
*State = EfiUsbHcStateHalt;
|
|
} else {
|
|
*State = EfiUsbHcStateOperational;
|
|
}
|
|
|
|
exit:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciSetState (
|
|
IN EFI_USB2_HC_PROTOCOL *This,
|
|
IN EFI_USB_HC_STATE State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the USB host controller to a specific state.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB2_HC_PROTOCOL instance.
|
|
State - Indicates the state of the host controller that will be set.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS
|
|
The USB host controller was successfully placed in the state
|
|
specified by State.
|
|
EFI_INVALID_PARAMETER
|
|
State is invalid.
|
|
EFI_DEVICE_ERROR
|
|
Failed to set the state specified by State due to device error.
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
UINT32 UsbCommandAddr;
|
|
UINT32 UsbCommandReg;
|
|
EFI_USB_HC_STATE CurrentState;
|
|
|
|
UsbCommandAddr = USBCMD;
|
|
HcDev = USB2_HC_DEV_FROM_THIS (This);
|
|
|
|
Status = EhciGetState (This, &CurrentState);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
switch (State) {
|
|
|
|
case EfiUsbHcStateHalt:
|
|
|
|
if (EfiUsbHcStateHalt == CurrentState) {
|
|
Status = EFI_SUCCESS;
|
|
goto exit;
|
|
} else if (EfiUsbHcStateOperational == CurrentState) {
|
|
Status = ReadEhcOperationalReg (
|
|
HcDev,
|
|
UsbCommandAddr,
|
|
&UsbCommandReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
UsbCommandReg &= ~USBCMD_RS;
|
|
Status = WriteEhcOperationalReg (
|
|
HcDev,
|
|
UsbCommandAddr,
|
|
UsbCommandReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
//
|
|
// Ensure the HC is in halt status after send the stop command
|
|
//
|
|
Status = WaitForEhcHalt (HcDev, EHCI_GENERIC_TIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_TIMEOUT;
|
|
goto exit;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EfiUsbHcStateOperational:
|
|
|
|
if (IsEhcSysError (HcDev)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
if (EfiUsbHcStateOperational == CurrentState) {
|
|
Status = EFI_SUCCESS;
|
|
goto exit;
|
|
} else if (EfiUsbHcStateHalt == CurrentState) {
|
|
//
|
|
// Set Host Controller Run
|
|
//
|
|
Status = ReadEhcOperationalReg (
|
|
HcDev,
|
|
UsbCommandAddr,
|
|
&UsbCommandReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
UsbCommandReg |= USBCMD_RS;
|
|
Status = WriteEhcOperationalReg (
|
|
HcDev,
|
|
UsbCommandAddr,
|
|
UsbCommandReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EfiUsbHcStateSuspend:
|
|
|
|
Status = EFI_UNSUPPORTED;
|
|
break;
|
|
|
|
default:
|
|
|
|
Status = EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
exit:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciGetRootHubPortStatus (
|
|
IN EFI_USB2_HC_PROTOCOL *This,
|
|
IN UINT8 PortNumber,
|
|
OUT EFI_USB_PORT_STATUS *PortStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves the current status of a USB root hub port.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB2_HC_PROTOCOL.
|
|
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.
|
|
PortStatus - A pointer to the current port status bits and
|
|
port status change bits.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS The status of the USB root hub port specified
|
|
by PortNumber was returned in PortStatus.
|
|
EFI_INVALID_PARAMETER PortNumber is invalid.
|
|
EFI_DEVICE_ERROR Can't read register
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
UINT32 PortStatusControlAddr;
|
|
UINT32 PortStatusControlReg;
|
|
UINT8 MaxSpeed;
|
|
UINT8 TotalPortNumber;
|
|
UINT8 Is64BitCapable;
|
|
|
|
if (PortStatus == NULL) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
EhciGetCapability (
|
|
This,
|
|
&MaxSpeed,
|
|
&TotalPortNumber,
|
|
&Is64BitCapable
|
|
);
|
|
|
|
if (PortNumber >= TotalPortNumber) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
HcDev = USB2_HC_DEV_FROM_THIS (This);
|
|
PortStatusControlAddr = (UINT32) (PORTSC + (4 * PortNumber));
|
|
|
|
//
|
|
// Clear port status
|
|
//
|
|
PortStatus->PortStatus = 0;
|
|
PortStatus->PortChangeStatus = 0;
|
|
|
|
Status = ReadEhcOperationalReg (
|
|
HcDev,
|
|
PortStatusControlAddr,
|
|
&PortStatusControlReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Fill Port Status bits
|
|
//
|
|
|
|
//
|
|
// Current Connect Status
|
|
//
|
|
if (PORTSC_CCS & PortStatusControlReg) {
|
|
PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION;
|
|
}
|
|
//
|
|
// Port Enabled/Disabled
|
|
//
|
|
if (PORTSC_PED & PortStatusControlReg) {
|
|
PortStatus->PortStatus |= USB_PORT_STAT_ENABLE;
|
|
}
|
|
//
|
|
// Port Suspend
|
|
//
|
|
if (PORTSC_SUSP & PortStatusControlReg) {
|
|
PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND;
|
|
}
|
|
//
|
|
// Over-current Active
|
|
//
|
|
if (PORTSC_OCA & PortStatusControlReg) {
|
|
PortStatus->PortStatus |= USB_PORT_STAT_OVERCURRENT;
|
|
}
|
|
//
|
|
// Port Reset
|
|
//
|
|
if (PORTSC_PR & PortStatusControlReg) {
|
|
PortStatus->PortStatus |= USB_PORT_STAT_RESET;
|
|
}
|
|
//
|
|
// Port Power
|
|
//
|
|
if (PORTSC_PP & PortStatusControlReg) {
|
|
PortStatus->PortStatus |= USB_PORT_STAT_POWER;
|
|
}
|
|
//
|
|
// Port Owner
|
|
//
|
|
if (PORTSC_PO & PortStatusControlReg) {
|
|
PortStatus->PortStatus |= USB_PORT_STAT_OWNER;
|
|
}
|
|
//
|
|
// Identify device speed
|
|
//
|
|
if (PORTSC_LS_KSTATE & PortStatusControlReg) {
|
|
//
|
|
// Low Speed Device Attached
|
|
//
|
|
PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED;
|
|
} else {
|
|
//
|
|
// Not Low Speed Device Attached
|
|
//
|
|
if ((PORTSC_CCS & PortStatusControlReg) && (PORTSC_CSC & PortStatusControlReg)) {
|
|
HcDev->DeviceSpeed[PortNumber] = (UINT16) (IsHighSpeedDevice (This, PortNumber) ? USB_PORT_STAT_HIGH_SPEED : 0);
|
|
}
|
|
PortStatus->PortStatus = (UINT16) (PortStatus->PortStatus | HcDev->DeviceSpeed[PortNumber]);
|
|
}
|
|
//
|
|
// Fill Port Status Change bits
|
|
//
|
|
//
|
|
// Connect Status Change
|
|
//
|
|
if (PORTSC_CSC & PortStatusControlReg) {
|
|
PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION;
|
|
}
|
|
//
|
|
// Port Enabled/Disabled Change
|
|
//
|
|
if (PORTSC_PEDC & PortStatusControlReg) {
|
|
PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE;
|
|
}
|
|
//
|
|
// Port Over Current Change
|
|
//
|
|
if (PORTSC_OCC & PortStatusControlReg) {
|
|
PortStatus->PortChangeStatus |= USB_PORT_STAT_C_OVERCURRENT;
|
|
}
|
|
|
|
exit:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciSetRootHubPortFeature (
|
|
IN EFI_USB2_HC_PROTOCOL *This,
|
|
IN UINT8 PortNumber,
|
|
IN EFI_USB_PORT_FEATURE PortFeature
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets a feature for the specified root hub port.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB2_HC_PROTOCOL.
|
|
PortNumber - Specifies the root hub port whose feature
|
|
is requested to be set.
|
|
PortFeature - Indicates the feature selector associated
|
|
with the feature set request.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS
|
|
The feature specified by PortFeature was set for the
|
|
USB root hub port specified by PortNumber.
|
|
EFI_INVALID_PARAMETER
|
|
PortNumber is invalid or PortFeature is invalid.
|
|
EFI_DEVICE_ERROR
|
|
Can't read register
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
UINT32 PortStatusControlAddr;
|
|
UINT32 PortStatusControlReg;
|
|
UINT8 MaxSpeed;
|
|
UINT8 TotalPortNumber;
|
|
UINT8 Is64BitCapable;
|
|
|
|
EhciGetCapability (
|
|
This,
|
|
&MaxSpeed,
|
|
&TotalPortNumber,
|
|
&Is64BitCapable
|
|
);
|
|
|
|
if (PortNumber >= TotalPortNumber) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
HcDev = USB2_HC_DEV_FROM_THIS (This);
|
|
PortStatusControlAddr = (UINT32) (PORTSC + (4 * PortNumber));
|
|
|
|
Status = ReadEhcOperationalReg (
|
|
HcDev,
|
|
PortStatusControlAddr,
|
|
&PortStatusControlReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
switch (PortFeature) {
|
|
|
|
case EfiUsbPortEnable:
|
|
|
|
//
|
|
// Sofeware can't set this bit, Port can only be enable by the Host Controller
|
|
// as a part of the reset and enable
|
|
//
|
|
PortStatusControlReg &= 0xffffffd5;
|
|
PortStatusControlReg |= PORTSC_PED;
|
|
break;
|
|
|
|
case EfiUsbPortSuspend:
|
|
|
|
PortStatusControlReg &= 0xffffffd5;
|
|
PortStatusControlReg |= PORTSC_SUSP;
|
|
break;
|
|
|
|
case EfiUsbPortReset:
|
|
|
|
//
|
|
// Make sure Host Controller not halt before reset it
|
|
//
|
|
if (IsEhcHalted (HcDev)) {
|
|
Status = StartScheduleExecution (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
Status = WaitForEhcNotHalt (HcDev, EHCI_GENERIC_TIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((gEHCDebugLevel, "EHCI: WaitForEhcNotHalt TimeOut\n"));
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
}
|
|
PortStatusControlReg &= 0xffffffd5;
|
|
PortStatusControlReg |= PORTSC_PR;
|
|
//
|
|
// Set one to PortReset bit must also set zero to PortEnable bit
|
|
//
|
|
PortStatusControlReg &= ~PORTSC_PED;
|
|
break;
|
|
|
|
case EfiUsbPortPower:
|
|
|
|
//
|
|
// No support, no operation
|
|
//
|
|
goto exit;
|
|
|
|
case EfiUsbPortOwner:
|
|
|
|
PortStatusControlReg &= 0xffffffd5;
|
|
PortStatusControlReg |= PORTSC_PO;
|
|
break;
|
|
|
|
default:
|
|
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
Status = WriteEhcOperationalReg (
|
|
HcDev,
|
|
PortStatusControlAddr,
|
|
PortStatusControlReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
exit:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciClearRootHubPortFeature (
|
|
IN EFI_USB2_HC_PROTOCOL *This,
|
|
IN UINT8 PortNumber,
|
|
IN EFI_USB_PORT_FEATURE PortFeature
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clears a feature for the specified root hub port.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB2_HC_PROTOCOL instance.
|
|
PortNumber - Specifies the root hub port whose feature
|
|
is requested to be cleared.
|
|
PortFeature - Indicates the feature selector associated with the
|
|
feature clear request.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS
|
|
The feature specified by PortFeature was cleared for the
|
|
USB root hub port specified by PortNumber.
|
|
EFI_INVALID_PARAMETER
|
|
PortNumber is invalid or PortFeature is invalid.
|
|
EFI_DEVICE_ERROR
|
|
Can't read register
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
UINT32 PortStatusControlAddr;
|
|
UINT32 PortStatusControlReg;
|
|
UINT8 MaxSpeed;
|
|
UINT8 TotalPortNumber;
|
|
UINT8 Is64BitCapable;
|
|
|
|
EhciGetCapability (
|
|
This,
|
|
&MaxSpeed,
|
|
&TotalPortNumber,
|
|
&Is64BitCapable
|
|
);
|
|
|
|
if (PortNumber >= TotalPortNumber) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
HcDev = USB2_HC_DEV_FROM_THIS (This);
|
|
PortStatusControlAddr = (UINT32) (PORTSC + (4 * PortNumber));
|
|
|
|
Status = ReadEhcOperationalReg (
|
|
HcDev,
|
|
PortStatusControlAddr,
|
|
&PortStatusControlReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
switch (PortFeature) {
|
|
|
|
case EfiUsbPortEnable:
|
|
|
|
//
|
|
// Clear PORT_ENABLE feature means disable port.
|
|
//
|
|
PortStatusControlReg &= 0xffffffd5;
|
|
PortStatusControlReg &= ~PORTSC_PED;
|
|
break;
|
|
|
|
case EfiUsbPortSuspend:
|
|
|
|
//
|
|
// A write of zero to this bit is ignored by the host controller.
|
|
// The host controller will unconditionally set this bit to a zero when:
|
|
// 1. software sets the Forct Port Resume bit to a zero from a one.
|
|
// 2. software sets the Port Reset bit to a one frome a zero.
|
|
//
|
|
PortStatusControlReg &= 0xffffffd5;
|
|
PortStatusControlReg &= ~PORTSC_FPR;
|
|
break;
|
|
|
|
case EfiUsbPortReset:
|
|
|
|
//
|
|
// Clear PORT_RESET means clear the reset signal.
|
|
//
|
|
PortStatusControlReg &= 0xffffffd5;
|
|
PortStatusControlReg &= ~PORTSC_PR;
|
|
break;
|
|
|
|
case EfiUsbPortPower:
|
|
|
|
//
|
|
// No support, no operation
|
|
//
|
|
goto exit;
|
|
|
|
case EfiUsbPortOwner:
|
|
|
|
//
|
|
// Clear port owner means this port owned by EHC
|
|
//
|
|
PortStatusControlReg &= 0xffffffd5;
|
|
PortStatusControlReg &= ~PORTSC_PO;
|
|
break;
|
|
|
|
case EfiUsbPortConnectChange:
|
|
|
|
//
|
|
// Clear connect status change
|
|
//
|
|
PortStatusControlReg &= 0xffffffd5;
|
|
PortStatusControlReg |= PORTSC_CSC;
|
|
break;
|
|
|
|
case EfiUsbPortEnableChange:
|
|
|
|
//
|
|
// Clear enable status change
|
|
//
|
|
PortStatusControlReg &= 0xffffffd5;
|
|
PortStatusControlReg |= PORTSC_PEDC;
|
|
break;
|
|
|
|
case EfiUsbPortSuspendChange:
|
|
|
|
//
|
|
// No related bit, no operation
|
|
//
|
|
goto exit;
|
|
|
|
case EfiUsbPortOverCurrentChange:
|
|
|
|
//
|
|
// Clear PortOverCurrent change
|
|
//
|
|
PortStatusControlReg &= 0xffffffd5;
|
|
PortStatusControlReg |= PORTSC_OCC;
|
|
break;
|
|
|
|
case EfiUsbPortResetChange:
|
|
|
|
//
|
|
// No related bit, no operation
|
|
//
|
|
goto exit;
|
|
|
|
default:
|
|
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
Status = WriteEhcOperationalReg (
|
|
HcDev,
|
|
PortStatusControlAddr,
|
|
PortStatusControlReg
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciControlTransfer (
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submits control transfer to a target USB device.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB2_HC_PROTOCOL instance.
|
|
DeviceAddress - Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
DeviceSpeed - Indicates target device speed.
|
|
MaximumPacketLength - Indicates the maximum packet size that the
|
|
default control transfer endpoint is capable of
|
|
sending or receiving.
|
|
Request - A pointer to the USB device request that will be sent
|
|
to the USB device.
|
|
TransferDirection - Specifies the data direction for the transfer.
|
|
There are three values available, DataIn, DataOut
|
|
and NoData.
|
|
Data - A pointer to the buffer of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
DataLength - Indicates the size, in bytes, of the data buffer
|
|
specified by Data.
|
|
TimeOut - Indicates the maximum time, in microseconds,
|
|
which the transfer is allowed to complete.
|
|
Translator - A pointr to the transaction translator data.
|
|
TransferResult - A pointer to the detailed result information generated
|
|
by this control transfer.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS
|
|
The control transfer was completed successfully.
|
|
EFI_OUT_OF_RESOURCES
|
|
The control transfer could not be completed due to a lack of resources.
|
|
EFI_INVALID_PARAMETER
|
|
Some parameters are invalid.
|
|
EFI_TIMEOUT
|
|
The control transfer failed due to timeout.
|
|
EFI_DEVICE_ERROR
|
|
The control transfer failed due to host controller or device error.
|
|
Caller should check TranferResult for detailed error information.
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
UINT8 PktId;
|
|
EHCI_QH_ENTITY *QhPtr;
|
|
EHCI_QTD_ENTITY *ControlQtdsPtr;
|
|
UINT8 *DataCursor;
|
|
VOID *DataMap;
|
|
UINT8 *RequestCursor;
|
|
VOID *RequestMap;
|
|
|
|
QhPtr = NULL;
|
|
ControlQtdsPtr = NULL;
|
|
DataCursor = NULL;
|
|
DataMap = NULL;
|
|
RequestCursor = NULL;
|
|
RequestMap = NULL;
|
|
HcDev = USB2_HC_DEV_FROM_THIS (This);
|
|
|
|
//
|
|
// Parameters Checking
|
|
//
|
|
if (TransferDirection != EfiUsbDataIn &&
|
|
TransferDirection != EfiUsbDataOut &&
|
|
TransferDirection != EfiUsbNoData
|
|
) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (EfiUsbNoData == TransferDirection) {
|
|
if (NULL != Data || 0 != *DataLength) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
} else {
|
|
if (NULL == Data || 0 == *DataLength) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (Request == NULL || TransferResult == NULL) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (EFI_USB_SPEED_LOW == DeviceSpeed) {
|
|
if (MaximumPacketLength != 8) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
} else if (MaximumPacketLength != 8 &&
|
|
MaximumPacketLength != 16 &&
|
|
MaximumPacketLength != 32 &&
|
|
MaximumPacketLength != 64
|
|
) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// If errors exist that cause host controller halt,
|
|
// then return EFI_DEVICE_ERROR.
|
|
//
|
|
if (IsEhcHalted (HcDev) || IsEhcSysError (HcDev)) {
|
|
ClearEhcAllStatus (HcDev);
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Map the Request for bus master access.
|
|
// BusMasterRead means cpu write
|
|
//
|
|
Status = MapRequestBuffer (
|
|
HcDev,
|
|
Request,
|
|
&RequestCursor,
|
|
&RequestMap
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Map the source data buffer for bus master access.
|
|
//
|
|
Status = MapDataBuffer (
|
|
HcDev,
|
|
TransferDirection,
|
|
Data,
|
|
DataLength,
|
|
&PktId,
|
|
&DataCursor,
|
|
&DataMap
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto unmap_request;
|
|
}
|
|
|
|
//
|
|
// Create and init control Qh
|
|
//
|
|
Status = CreateControlQh (
|
|
HcDev,
|
|
DeviceAddress,
|
|
DeviceSpeed,
|
|
MaximumPacketLength,
|
|
Translator,
|
|
&QhPtr
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto unmap_data;
|
|
}
|
|
|
|
//
|
|
// Create and init control Qtds
|
|
//
|
|
Status = CreateControlQtds (
|
|
HcDev,
|
|
PktId,
|
|
RequestCursor,
|
|
DataCursor,
|
|
*DataLength,
|
|
Translator,
|
|
&ControlQtdsPtr
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto destory_qh;
|
|
}
|
|
|
|
//
|
|
// Link Qtds to Qh
|
|
//
|
|
LinkQtdToQh (QhPtr, ControlQtdsPtr);
|
|
|
|
ClearEhcAllStatus (HcDev);
|
|
|
|
//
|
|
// Link Qh and Qtds to Async Schedule List
|
|
//
|
|
Status = LinkQhToAsyncList (HcDev, QhPtr);
|
|
if (EFI_ERROR (Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto destory_qtds;
|
|
}
|
|
|
|
//
|
|
// Poll Qh-Qtds execution and get result.
|
|
// detail status is returned
|
|
//
|
|
Status = ExecuteTransfer (
|
|
HcDev,
|
|
TRUE,
|
|
QhPtr,
|
|
DataLength,
|
|
0,
|
|
TimeOut,
|
|
TransferResult
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto destory_qtds;
|
|
}
|
|
|
|
//
|
|
// If has errors that cause host controller halt,
|
|
// then return EFI_DEVICE_ERROR directly.
|
|
//
|
|
if (IsEhcHalted (HcDev) || IsEhcSysError (HcDev)) {
|
|
*TransferResult |= EFI_USB_ERR_SYSTEM;
|
|
}
|
|
|
|
ClearEhcAllStatus (HcDev);
|
|
|
|
destory_qtds:
|
|
UnlinkQhFromAsyncList (HcDev, QhPtr);
|
|
DestoryQtds (HcDev, ControlQtdsPtr);
|
|
destory_qh:
|
|
DestoryQh (HcDev, QhPtr);
|
|
unmap_data:
|
|
HcDev->PciIo->Unmap (HcDev->PciIo, DataMap);
|
|
unmap_request:
|
|
HcDev->PciIo->Unmap (HcDev->PciIo, RequestMap);
|
|
exit:
|
|
HcDev->PciIo->Flush (HcDev->PciIo);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciBulkTransfer (
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submits bulk transfer to a bulk endpoint of a USB device.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB2_HC_PROTOCOL instance.
|
|
DeviceAddress - Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
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.
|
|
DeviceSpeed - Indicates device speed. The supported values are EFI_USB_SPEED_FULL
|
|
and EFI_USB_SPEED_HIGH.
|
|
MaximumPacketLength - Indicates the maximum packet size the target endpoint
|
|
is capable of sending or receiving.
|
|
DataBuffersNumber - Number of data buffers prepared for the transfer.
|
|
Data - Array of pointers to the buffers of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
DataLength - When input, indicates the size, in bytes, of the data buffer
|
|
specified by Data. When output, indicates the actually
|
|
transferred data size.
|
|
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.
|
|
Translator - A pointr to the transaction translator data.
|
|
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.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS
|
|
The bulk transfer was completed successfully.
|
|
EFI_OUT_OF_RESOURCES
|
|
The bulk transfer could not be submitted due to lack of resource.
|
|
EFI_INVALID_PARAMETER
|
|
Some parameters are invalid.
|
|
EFI_TIMEOUT
|
|
The bulk transfer failed due to timeout.
|
|
EFI_DEVICE_ERROR
|
|
The bulk transfer failed due to host controller or device error.
|
|
Caller should check TranferResult for detailed error information.
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
UINT8 PktId;
|
|
EHCI_QH_ENTITY *QhPtr;
|
|
EHCI_QTD_ENTITY *BulkQtdsPtr;
|
|
UINT8 *DataCursor;
|
|
VOID *DataMap;
|
|
EFI_USB_DATA_DIRECTION TransferDirection;
|
|
|
|
QhPtr = NULL;
|
|
BulkQtdsPtr = NULL;
|
|
DataCursor = NULL;
|
|
DataMap = NULL;
|
|
HcDev = USB2_HC_DEV_FROM_THIS (This);
|
|
|
|
//
|
|
// Parameters Checking
|
|
//
|
|
if (NULL == DataLength ||
|
|
NULL == Data ||
|
|
NULL == Data[0] ||
|
|
NULL == TransferResult
|
|
) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (*DataLength == 0) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (1 != *DataToggle && 0 != *DataToggle) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (EFI_USB_SPEED_LOW == DeviceSpeed) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (EFI_USB_SPEED_FULL == DeviceSpeed) {
|
|
if (MaximumPacketLength > 64) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (EFI_USB_SPEED_HIGH == DeviceSpeed) {
|
|
if (MaximumPacketLength > 512) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if has errors that cause host controller halt,
|
|
// then return EFI_DEVICE_ERROR directly.
|
|
//
|
|
if (IsEhcHalted (HcDev) || IsEhcSysError (HcDev)) {
|
|
ClearEhcAllStatus (HcDev);
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
Status = ClearEhcAllStatus (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// construct QH and TD data structures,
|
|
// and link them together
|
|
//
|
|
if (EndPointAddress & 0x80) {
|
|
TransferDirection = EfiUsbDataIn;
|
|
} else {
|
|
TransferDirection = EfiUsbDataOut;
|
|
}
|
|
|
|
Status = MapDataBuffer (
|
|
HcDev,
|
|
TransferDirection,
|
|
Data[0],
|
|
DataLength,
|
|
&PktId,
|
|
&DataCursor,
|
|
&DataMap
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Create and init Bulk Qh
|
|
//
|
|
Status = CreateBulkQh (
|
|
HcDev,
|
|
DeviceAddress,
|
|
EndPointAddress,
|
|
DeviceSpeed,
|
|
*DataToggle,
|
|
MaximumPacketLength,
|
|
Translator,
|
|
&QhPtr
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto unmap_data;
|
|
}
|
|
|
|
//
|
|
// Create and init Bulk Qtds
|
|
//
|
|
Status = CreateBulkOrInterruptQtds (
|
|
HcDev,
|
|
PktId,
|
|
DataCursor,
|
|
*DataLength,
|
|
Translator,
|
|
&BulkQtdsPtr
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto destory_qh;
|
|
}
|
|
|
|
//
|
|
// Link Qtds to Qh
|
|
//
|
|
LinkQtdToQh (QhPtr, BulkQtdsPtr);
|
|
|
|
ClearEhcAllStatus (HcDev);
|
|
|
|
//
|
|
// Link Qh and qtds to Async Schedule List
|
|
//
|
|
Status = LinkQhToAsyncList (HcDev, QhPtr);
|
|
if (EFI_ERROR (Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto destory_qtds;
|
|
}
|
|
|
|
//
|
|
// Poll QH-TDs execution and get result.
|
|
// detail status is returned
|
|
//
|
|
Status = ExecuteTransfer (
|
|
HcDev,
|
|
FALSE,
|
|
QhPtr,
|
|
DataLength,
|
|
DataToggle,
|
|
TimeOut,
|
|
TransferResult
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto destory_qtds;
|
|
}
|
|
|
|
//
|
|
// if has errors that cause host controller halt,
|
|
// then return EFI_DEVICE_ERROR directly.
|
|
//
|
|
if (IsEhcHalted (HcDev) || IsEhcSysError (HcDev)) {
|
|
*TransferResult |= EFI_USB_ERR_SYSTEM;
|
|
}
|
|
|
|
ClearEhcAllStatus (HcDev);
|
|
|
|
destory_qtds:
|
|
UnlinkQhFromAsyncList (HcDev, QhPtr);
|
|
DestoryQtds (HcDev, BulkQtdsPtr);
|
|
destory_qh:
|
|
DestoryQh (HcDev, QhPtr);
|
|
unmap_data:
|
|
HcDev->PciIo->Unmap (HcDev->PciIo, DataMap);
|
|
exit:
|
|
HcDev->PciIo->Flush (HcDev->PciIo);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciAsyncInterruptTransfer (
|
|
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 OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submits an asynchronous interrupt transfer to an
|
|
interrupt endpoint of a USB device.
|
|
Translator parameter doesn't exist in UEFI2.0 spec, but it will be updated
|
|
in the following specification version.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB2_HC_PROTOCOL instance.
|
|
DeviceAddress - Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
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.
|
|
DeviceSpeed - Indicates device speed.
|
|
MaximumPacketLength - Indicates the maximum packet size the target endpoint
|
|
is capable of sending or receiving.
|
|
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.
|
|
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.
|
|
PollingInterval - Indicates the interval, in milliseconds, that the
|
|
asynchronous interrupt transfer is polled.
|
|
This parameter is required when IsNewTransfer is TRUE.
|
|
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.
|
|
Translator - A pointr to the transaction translator data.
|
|
CallBackFunction - The Callback function.This function is called at the
|
|
rate specified by PollingInterval.This parameter is
|
|
only required when IsNewTransfer is TRUE.
|
|
Context - The context that is passed to the CallBackFunction.
|
|
- This is an optional parameter and may be NULL.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS
|
|
The asynchronous interrupt transfer request has been successfully
|
|
submitted or canceled.
|
|
EFI_INVALID_PARAMETER
|
|
Some parameters are invalid.
|
|
EFI_OUT_OF_RESOURCES
|
|
The request could not be completed due to a lack of resources.
|
|
EFI_DEVICE_ERROR
|
|
Can't read register
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
UINT8 PktId;
|
|
EHCI_QH_ENTITY *QhPtr;
|
|
EHCI_QTD_ENTITY *InterruptQtdsPtr;
|
|
UINT8 *DataPtr;
|
|
UINT8 *DataCursor;
|
|
VOID *DataMap;
|
|
UINTN MappedLength;
|
|
EHCI_ASYNC_REQUEST *AsyncRequestPtr;
|
|
EFI_TPL OldTpl;
|
|
|
|
QhPtr = NULL;
|
|
InterruptQtdsPtr = NULL;
|
|
DataPtr = NULL;
|
|
DataCursor = NULL;
|
|
DataMap = NULL;
|
|
AsyncRequestPtr = NULL;
|
|
HcDev = USB2_HC_DEV_FROM_THIS (This);
|
|
|
|
//
|
|
// Parameters Checking
|
|
//
|
|
if (!IsDataInTransfer (EndPointAddress)) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (IsNewTransfer) {
|
|
if (0 == DataLength) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (*DataToggle != 1 && *DataToggle != 0) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (PollingInterval > 255 || PollingInterval < 1) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if has errors that cause host controller halt,
|
|
// then return EFI_DEVICE_ERROR directly.
|
|
//
|
|
if (IsEhcHalted (HcDev) || IsEhcSysError (HcDev)) {
|
|
ClearEhcAllStatus (HcDev);
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
Status = ClearEhcAllStatus (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Delete Async interrupt transfer request
|
|
//
|
|
if (!IsNewTransfer) {
|
|
|
|
OldTpl = gBS->RaiseTPL (EFI_TPL_NOTIFY);
|
|
|
|
Status = DeleteAsyncRequestTransfer (
|
|
HcDev,
|
|
DeviceAddress,
|
|
EndPointAddress,
|
|
DataToggle
|
|
);
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
Status = EhciAllocatePool (
|
|
HcDev,
|
|
(UINT8 **) &AsyncRequestPtr,
|
|
sizeof (EHCI_ASYNC_REQUEST)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
Status = EhciAllocatePool (HcDev, &DataPtr, DataLength);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto free_request;
|
|
}
|
|
|
|
MappedLength = DataLength;
|
|
Status = MapDataBuffer (
|
|
HcDev,
|
|
EfiUsbDataIn,
|
|
DataPtr,
|
|
&MappedLength,
|
|
&PktId,
|
|
&DataCursor,
|
|
&DataMap
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto free_data;
|
|
}
|
|
|
|
//
|
|
// Create and init Interrupt Qh
|
|
//
|
|
Status = CreateInterruptQh (
|
|
HcDev,
|
|
DeviceAddress,
|
|
EndPointAddress,
|
|
DeviceSpeed,
|
|
*DataToggle,
|
|
MaximumPacketLength,
|
|
PollingInterval,
|
|
Translator,
|
|
&QhPtr
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto unmap_data;
|
|
}
|
|
|
|
//
|
|
// Create and init Interrupt Qtds
|
|
//
|
|
Status = CreateBulkOrInterruptQtds (
|
|
HcDev,
|
|
PktId,
|
|
DataCursor,
|
|
MappedLength,
|
|
Translator,
|
|
&InterruptQtdsPtr
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto destory_qh;
|
|
}
|
|
|
|
//
|
|
// Link Qtds to Qh
|
|
//
|
|
LinkQtdToQh (QhPtr, InterruptQtdsPtr);
|
|
|
|
//
|
|
// Init AsyncRequest Entry
|
|
//
|
|
AsyncRequestPtr->Context = Context;
|
|
AsyncRequestPtr->CallBackFunc = CallBackFunction;
|
|
AsyncRequestPtr->TransferType = ASYNC_INTERRUPT_TRANSFER;
|
|
AsyncRequestPtr->QhPtr = QhPtr;
|
|
AsyncRequestPtr->Prev = NULL;
|
|
AsyncRequestPtr->Next = NULL;
|
|
|
|
if (NULL == HcDev->AsyncRequestList) {
|
|
Status = StartPollingTimer (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
CleanUpAllAsyncRequestTransfer (HcDev);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Link Entry to AsyncRequest List
|
|
//
|
|
LinkToAsyncReqeust (HcDev, AsyncRequestPtr);
|
|
|
|
ClearEhcAllStatus (HcDev);
|
|
|
|
Status = DisablePeriodicSchedule (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
Status = WaitForPeriodicScheduleDisable (HcDev, EHCI_GENERIC_TIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_TIMEOUT;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Link Qh and Qtds to Periodic Schedule List
|
|
//
|
|
LinkQhToPeriodicList (HcDev, QhPtr);
|
|
|
|
Status = EnablePeriodicSchedule (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
Status = WaitForPeriodicScheduleEnable (HcDev, EHCI_GENERIC_TIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_TIMEOUT;
|
|
goto exit;
|
|
}
|
|
|
|
if (IsEhcHalted (HcDev)) {
|
|
Status = StartScheduleExecution (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
HcDev->PciIo->Flush (HcDev->PciIo);
|
|
goto exit;
|
|
|
|
destory_qh:
|
|
DestoryQh (HcDev, QhPtr);
|
|
free_data:
|
|
EhciFreePool (HcDev, DataPtr, DataLength);
|
|
free_request:
|
|
EhciFreePool (
|
|
HcDev,
|
|
(UINT8 *) AsyncRequestPtr,
|
|
sizeof (EHCI_ASYNC_REQUEST)
|
|
);
|
|
unmap_data:
|
|
HcDev->PciIo->Unmap (HcDev->PciIo, DataMap);
|
|
exit:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciSyncInterruptTransfer (
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submits synchronous interrupt transfer to an interrupt endpoint
|
|
of a USB device.
|
|
Translator parameter doesn't exist in UEFI2.0 spec, but it will be updated
|
|
in the following specification version.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB2_HC_PROTOCOL instance.
|
|
DeviceAddress - Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
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.
|
|
DeviceSpeed - Indicates device speed.
|
|
MaximumPacketLength - Indicates the maximum packet size the target endpoint
|
|
is capable of sending or receiving.
|
|
Data - A pointer to the buffer of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
DataLength - On input, the size, in bytes, of the data buffer specified
|
|
by Data. On output, the number of bytes transferred.
|
|
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.
|
|
TimeOut - Indicates the maximum time, in microseconds, which the
|
|
transfer is allowed to complete.
|
|
Translator - A pointr to the transaction translator data.
|
|
TransferResult - A pointer to the detailed result information from
|
|
the synchronous interrupt transfer.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS
|
|
The synchronous interrupt transfer was completed successfully.
|
|
EFI_OUT_OF_RESOURCES
|
|
The synchronous interrupt transfer could not be submitted due
|
|
to lack of resource.
|
|
EFI_INVALID_PARAMETER
|
|
Some parameters are invalid.
|
|
EFI_TIMEOUT
|
|
The synchronous interrupt transfer failed due to timeout.
|
|
EFI_DEVICE_ERROR
|
|
The synchronous interrupt transfer failed due to host controller
|
|
or device error. Caller should check TranferResult for detailed
|
|
error information.
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
USB2_HC_DEV *HcDev;
|
|
UINT8 PktId;
|
|
EHCI_QH_ENTITY *QhPtr;
|
|
EHCI_QTD_ENTITY *InterruptQtdsPtr;
|
|
UINT8 *DataCursor;
|
|
VOID *DataMap;
|
|
|
|
QhPtr = NULL;
|
|
InterruptQtdsPtr = NULL;
|
|
DataCursor = NULL;
|
|
DataMap = NULL;
|
|
HcDev = USB2_HC_DEV_FROM_THIS (This);
|
|
|
|
//
|
|
// Parameters Checking
|
|
//
|
|
if (DataLength == NULL ||
|
|
Data == NULL ||
|
|
TransferResult == NULL
|
|
) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (!IsDataInTransfer (EndPointAddress)) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (0 == *DataLength) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (*DataToggle != 1 && *DataToggle != 0) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (EFI_USB_SPEED_LOW == DeviceSpeed && 8 != MaximumPacketLength) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (EFI_USB_SPEED_FULL == DeviceSpeed && MaximumPacketLength > 64) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (EFI_USB_SPEED_HIGH == DeviceSpeed && MaximumPacketLength > 3072) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// if has errors that cause host controller halt,
|
|
// then return EFI_DEVICE_ERROR directly.
|
|
//
|
|
if (IsEhcHalted (HcDev) || IsEhcSysError (HcDev)) {
|
|
ClearEhcAllStatus (HcDev);
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
Status = ClearEhcAllStatus (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
Status = MapDataBuffer (
|
|
HcDev,
|
|
EfiUsbDataIn,
|
|
Data,
|
|
DataLength,
|
|
&PktId,
|
|
&DataCursor,
|
|
&DataMap
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Create and init Interrupt Qh
|
|
//
|
|
Status = CreateInterruptQh (
|
|
HcDev,
|
|
DeviceAddress,
|
|
EndPointAddress,
|
|
DeviceSpeed,
|
|
*DataToggle,
|
|
MaximumPacketLength,
|
|
0,
|
|
Translator,
|
|
&QhPtr
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto unmap_data;
|
|
}
|
|
|
|
//
|
|
// Create and init Interrupt Qtds
|
|
//
|
|
Status = CreateBulkOrInterruptQtds (
|
|
HcDev,
|
|
PktId,
|
|
DataCursor,
|
|
*DataLength,
|
|
Translator,
|
|
&InterruptQtdsPtr
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto destory_qh;
|
|
}
|
|
|
|
//
|
|
// Link Qtds to Qh
|
|
//
|
|
LinkQtdToQh (QhPtr, InterruptQtdsPtr);
|
|
|
|
ClearEhcAllStatus (HcDev);
|
|
|
|
Status = DisablePeriodicSchedule (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
Status = WaitForPeriodicScheduleDisable (HcDev, EHCI_GENERIC_TIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_TIMEOUT;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Link Qh and Qtds to Periodic Schedule List
|
|
//
|
|
LinkQhToPeriodicList (HcDev, QhPtr);
|
|
|
|
Status = EnablePeriodicSchedule (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
Status = WaitForPeriodicScheduleEnable (HcDev, EHCI_GENERIC_TIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_TIMEOUT;
|
|
goto exit;
|
|
}
|
|
|
|
if (IsEhcHalted (HcDev)) {
|
|
Status = StartScheduleExecution (HcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Poll QH-TDs execution and get result.
|
|
// detail status is returned
|
|
//
|
|
Status = ExecuteTransfer (
|
|
HcDev,
|
|
FALSE,
|
|
QhPtr,
|
|
DataLength,
|
|
DataToggle,
|
|
TimeOut,
|
|
TransferResult
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto destory_qtds;
|
|
}
|
|
|
|
//
|
|
// if has errors that cause host controller halt,
|
|
// then return EFI_DEVICE_ERROR directly.
|
|
//
|
|
if (IsEhcHalted (HcDev) || IsEhcSysError (HcDev)) {
|
|
*TransferResult |= EFI_USB_ERR_SYSTEM;
|
|
}
|
|
|
|
ClearEhcAllStatus (HcDev);
|
|
|
|
destory_qtds:
|
|
UnlinkQhFromPeriodicList (HcDev, QhPtr, 0);
|
|
DestoryQtds (HcDev, InterruptQtdsPtr);
|
|
destory_qh:
|
|
DestoryQh (HcDev, QhPtr);
|
|
unmap_data:
|
|
HcDev->PciIo->Unmap (HcDev->PciIo, DataMap);
|
|
exit:
|
|
HcDev->PciIo->Flush (HcDev->PciIo);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciIsochronousTransfer (
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submits isochronous transfer to a target USB device.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB2_HC_PROTOCOL instance.
|
|
DeviceAddress - Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
EndPointAddress - End point address
|
|
DeviceSpeed - Indicates device speed.
|
|
MaximumPacketLength - Indicates the maximum packet size that the
|
|
default control transfer endpoint is capable of
|
|
sending or receiving.
|
|
DataBuffersNumber - Number of data buffers prepared for the transfer.
|
|
Data - Array of pointers to the buffers of data that will be
|
|
transmitted to USB device or received from USB device.
|
|
DataLength - Indicates the size, in bytes, of the data buffer
|
|
specified by Data.
|
|
Translator - A pointr to the transaction translator data.
|
|
TransferResult - A pointer to the detailed result information generated
|
|
by this control transfer.
|
|
|
|
Returns:
|
|
|
|
EFI_UNSUPPORTED
|
|
|
|
--*/
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EhciAsyncIsochronousTransfer (
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submits Async isochronous transfer to a target USB device.
|
|
|
|
Arguments:
|
|
|
|
This - A pointer to the EFI_USB2_HC_PROTOCOL instance.
|
|
DeviceAddress - Represents the address of the target device on the USB,
|
|
which is assigned during USB enumeration.
|
|
EndPointAddress - End point address
|
|
DeviceSpeed - Indicates device speed.
|
|
MaximumPacketLength - Indicates the maximum packet size that the
|
|
default control transfer endpoint is capable of
|
|
sending or receiving.
|
|
DataBuffersNumber - Number of data buffers prepared for the transfer.
|
|
Data - Array of pointers to the buffers of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
Translator - A pointr to the transaction translator data.
|
|
IsochronousCallBack - When the transfer complete, the call back function will be called
|
|
Context - Pass to the call back function as parameter
|
|
|
|
Returns:
|
|
|
|
EFI_UNSUPPORTED
|
|
|
|
--*/
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|