diff options
author | vanjeff <vanjeff@6f19259b-4bc3-4df7-8a09-765794883524> | 2007-07-11 06:46:38 +0000 |
---|---|---|
committer | vanjeff <vanjeff@6f19259b-4bc3-4df7-8a09-765794883524> | 2007-07-11 06:46:38 +0000 |
commit | 913cb9dc645d6db47d8c2a0be0369083b8bed25d (patch) | |
tree | a90ffdbbc5aee9c08832248527add15cbd6f8b9c | |
parent | 20b1aab6096eb922e0ce8acec44abfd440756e6f (diff) | |
download | edk2-platforms-913cb9dc645d6db47d8c2a0be0369083b8bed25d.tar.xz |
Import EhciDxe and UhciDxe into MdeModulePkg.
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3191 6f19259b-4bc3-4df7-8a09-765794883524
30 files changed, 13427 insertions, 0 deletions
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/ComponentName.c b/MdeModulePkg/Bus/Pci/EhciDxe/ComponentName.c new file mode 100644 index 0000000000..5595217953 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/ComponentName.c @@ -0,0 +1,200 @@ +/** @file
+
+Copyright 2006 - 2007, 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:
+
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+#include "Ehci.h"
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+EhciComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+EhciComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle, OPTIONAL
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gEhciComponentName = {
+ EhciComponentNameGetDriverName,
+ EhciComponentNameGetControllerName,
+ "eng"
+};
+
+static EFI_UNICODE_STRING_TABLE mEhciDriverNameTable[] = {
+ { "eng", L"Usb Ehci Driver" },
+ { NULL , NULL }
+};
+
+EFI_STATUS
+EFIAPI
+EhciComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gEhciComponentName.SupportedLanguages,
+ mEhciDriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+EhciComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle, OPTIONAL
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language
+ specified by Language from the point of view of the
+ driver specified by This.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ EFI_STATUS Status;
+ USB2_HC_DEV *EhciDev;
+ EFI_USB2_HC_PROTOCOL *Usb2Hc;
+
+ //
+ // This is a device driver, so ChildHandle must be NULL.
+ //
+ if (ChildHandle != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+ //
+ // Make sure this driver is currently managing ControllerHandle
+ //
+ Status = EfiTestManagedDevice (
+ ControllerHandle,
+ gEhciDriverBinding.DriverBindingHandle,
+ &gEfiPciIoProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Get the device context
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiUsb2HcProtocolGuid,
+ (VOID **) &Usb2Hc,
+ gEhciDriverBinding.DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ EhciDev = EHC_FROM_THIS (Usb2Hc);
+
+ return LookupUnicodeString (
+ Language,
+ gEhciComponentName.SupportedLanguages,
+ EhciDev->ControllerNameTable,
+ ControllerName
+ );
+
+}
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.c b/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.c new file mode 100644 index 0000000000..093388d45f --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.c @@ -0,0 +1,1708 @@ +/** @file
+
+Copyright (c) 2006 - 2007, 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"
+
+//
+// Two arrays used to translate the EHCI port state (change)
+// to the UEFI protocol's port state (change).
+//
+USB_PORT_STATE_MAP mUsbPortStateMap[] = {
+ {PORTSC_CONN, USB_PORT_STAT_CONNECTION},
+ {PORTSC_ENABLED, USB_PORT_STAT_ENABLE},
+ {PORTSC_SUSPEND, USB_PORT_STAT_SUSPEND},
+ {PORTSC_OVERCUR, USB_PORT_STAT_OVERCURRENT},
+ {PORTSC_RESET, USB_PORT_STAT_RESET},
+ {PORTSC_POWER, USB_PORT_STAT_POWER},
+ {PORTSC_OWNER, USB_PORT_STAT_OWNER}
+};
+
+USB_PORT_STATE_MAP mUsbPortChangeMap[] = {
+ {PORTSC_CONN_CHANGE, USB_PORT_STAT_C_CONNECTION},
+ {PORTSC_ENABLE_CHANGE, USB_PORT_STAT_C_ENABLE},
+ {PORTSC_OVERCUR_CHANGE, USB_PORT_STAT_C_OVERCURRENT}
+};
+
+
+/**
+ Retrieves the capablility of root hub ports.
+
+ @param This This EFI_USB_HC_PROTOCOL instance.
+ @param MaxSpeed Max speed supported by the controller
+ @param PortNumber Number of the root hub ports.
+ @param Is64BitCapable Whether the controller supports 64-bit memory
+ addressing.
+
+ @return EFI_SUCCESS : host controller capability were retrieved successfully.
+ @return EFI_INVALID_PARAMETER : Either of the three capability pointer is NULL
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcGetCapability (
+ IN EFI_USB2_HC_PROTOCOL *This,
+ OUT UINT8 *MaxSpeed,
+ OUT UINT8 *PortNumber,
+ OUT UINT8 *Is64BitCapable
+ )
+{
+ USB2_HC_DEV *Ehc;
+ EFI_TPL OldTpl;
+
+ if ((MaxSpeed == NULL) || (PortNumber == NULL) || (Is64BitCapable == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ Ehc = EHC_FROM_THIS (This);
+
+ *MaxSpeed = EFI_USB_SPEED_HIGH;
+ *PortNumber = (UINT8) (Ehc->HcStructParams & HCSP_NPORTS);
+ *Is64BitCapable = (UINT8) (Ehc->HcCapParams & HCCP_64BIT);
+
+ EHC_DEBUG (("EhcGetCapability: %d ports, 64 bit %d\n", *PortNumber, *Is64BitCapable));
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Provides software reset for the USB host controller.
+
+ @param This This EFI_USB2_HC_PROTOCOL instance.
+ @param Attributes A bit mask of the reset operation to perform.
+
+ @return EFI_SUCCESS : The reset operation succeeded.
+ @return EFI_INVALID_PARAMETER : Attributes is not valid.
+ @return EFI_UNSUPPOURTED : The type of reset specified by Attributes is
+ @return not currently supported by the host controller.
+ @return EFI_DEVICE_ERROR : Host controller isn't halted to reset.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcReset (
+ IN EFI_USB2_HC_PROTOCOL *This,
+ IN UINT16 Attributes
+ )
+{
+ USB2_HC_DEV *Ehc;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ Ehc = EHC_FROM_THIS (This);
+
+ switch (Attributes) {
+ case EFI_USB_HC_RESET_GLOBAL:
+ //
+ // Flow through, same behavior as Host Controller Reset
+ //
+ case EFI_USB_HC_RESET_HOST_CONTROLLER:
+ //
+ // Host Controller must be Halt when Reset it
+ //
+ if (!EhcIsHalt (Ehc)) {
+ Status = EhcHaltHC (Ehc, EHC_GENERIC_TIME);
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // Clean up the asynchronous transfers, currently only
+ // interrupt supports asynchronous operation.
+ //
+ EhciDelAllAsyncIntTransfers (Ehc);
+ EhcAckAllInterrupt (Ehc);
+ EhcFreeSched (Ehc);
+
+ Status = EhcResetHC (Ehc, EHC_STALL_1_SECOND);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = EhcInitHC (Ehc);
+ break;
+
+ case EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG:
+ case EFI_USB_HC_RESET_HOST_WITH_DEBUG:
+ Status = EFI_UNSUPPORTED;
+ break;
+
+ default:
+ Status = EFI_INVALID_PARAMETER;
+ }
+
+ON_EXIT:
+ EHC_DEBUG (("EhcReset: exit status %r\n", Status));
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Retrieve the current state of the USB host controller.
+
+ @param This This EFI_USB2_HC_PROTOCOL instance.
+ @param State Variable to return the current host controller
+ state.
+
+ @return EFI_SUCCESS : Host controller state was returned in State.
+ @return EFI_INVALID_PARAMETER : State is NULL.
+ @return EFI_DEVICE_ERROR : An error was encountered while attempting to
+ @return retrieve the host controller's current state.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcGetState (
+ IN CONST EFI_USB2_HC_PROTOCOL *This,
+ OUT EFI_USB_HC_STATE *State
+ )
+{
+ EFI_TPL OldTpl;
+ USB2_HC_DEV *Ehc;
+
+ if (State == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ Ehc = EHC_FROM_THIS (This);
+
+ if (EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT)) {
+ *State = EfiUsbHcStateHalt;
+ } else {
+ *State = EfiUsbHcStateOperational;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ EHC_DEBUG (("EhcGetState: current state %d\n", *State));
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Sets the USB host controller to a specific state.
+
+ @param This This EFI_USB2_HC_PROTOCOL instance.
+ @param State The state of the host controller that will be set.
+
+ @return EFI_SUCCESS : The USB host controller was successfully placed
+ @return in the state specified by State.
+ @return EFI_INVALID_PARAMETER : State is invalid.
+ @return EFI_DEVICE_ERROR : Failed to set the state due to device error.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcSetState (
+ IN EFI_USB2_HC_PROTOCOL *This,
+ IN EFI_USB_HC_STATE State
+ )
+{
+ USB2_HC_DEV *Ehc;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ EFI_USB_HC_STATE CurState;
+
+ Status = EhcGetState (This, &CurState);
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (CurState == State) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ Ehc = EHC_FROM_THIS (This);
+
+ switch (State) {
+ case EfiUsbHcStateHalt:
+ Status = EhcHaltHC (Ehc, EHC_GENERIC_TIME);
+ break;
+
+ case EfiUsbHcStateOperational:
+ if (EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_SYS_ERROR)) {
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ Status = EhcRunHC (Ehc, EHC_GENERIC_TIME);
+ break;
+
+ case EfiUsbHcStateSuspend:
+ Status = EFI_UNSUPPORTED;
+ break;
+
+ default:
+ Status = EFI_INVALID_PARAMETER;
+ }
+
+ EHC_DEBUG (("EhcSetState: exit status %r\n", Status));
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Retrieves the current status of a USB root hub port.
+
+ @param This This EFI_USB2_HC_PROTOCOL instance.
+ @param PortNumber The root hub port to retrieve the state from. This
+ value is zero-based.
+ @param PortStatus Variable to receive the port state
+
+ @return EFI_SUCCESS : The status of the USB root hub port specified
+ @return by PortNumber was returned in PortStatus.
+ @return EFI_INVALID_PARAMETER : PortNumber is invalid.
+ @return EFI_DEVICE_ERROR : Can't read register
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcGetRootHubPortStatus (
+ IN CONST EFI_USB2_HC_PROTOCOL *This,
+ IN CONST UINT8 PortNumber,
+ OUT EFI_USB_PORT_STATUS *PortStatus
+ )
+{
+ USB2_HC_DEV *Ehc;
+ EFI_TPL OldTpl;
+ UINT32 Offset;
+ UINT32 State;
+ UINT32 TotalPort;
+ UINTN Index;
+ UINTN MapSize;
+ EFI_STATUS Status;
+
+ if (PortStatus == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+
+ Ehc = EHC_FROM_THIS (This);
+ Status = EFI_SUCCESS;
+
+ TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
+
+ if (PortNumber >= TotalPort) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Offset = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber));
+ PortStatus->PortStatus = 0;
+ PortStatus->PortChangeStatus = 0;
+
+ State = EhcReadOpReg (Ehc, Offset);
+
+ //
+ // Identify device speed. If in K state, it is low speed.
+ // If the port is enabled after reset, the device is of
+ // high speed. The USB bus driver should retrieve the actual
+ // port speed after reset.
+ //
+ if (EHC_BIT_IS_SET (State, PORTSC_LINESTATE_K)) {
+ PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED;
+
+ } else if (EHC_BIT_IS_SET (State, PORTSC_ENABLED)) {
+ PortStatus->PortStatus |= USB_PORT_STAT_HIGH_SPEED;
+ }
+
+ //
+ // Convert the EHCI port/port change state to UEFI status
+ //
+ MapSize = sizeof (mUsbPortStateMap) / sizeof (USB_PORT_STATE_MAP);
+
+ for (Index = 0; Index < MapSize; Index++) {
+ if (EHC_BIT_IS_SET (State, mUsbPortStateMap[Index].HwState)) {
+ PortStatus->PortStatus |= mUsbPortStateMap[Index].UefiState;
+ }
+ }
+
+ MapSize = sizeof (mUsbPortChangeMap) / sizeof (USB_PORT_STATE_MAP);
+
+ for (Index = 0; Index < MapSize; Index++) {
+ if (EHC_BIT_IS_SET (State, mUsbPortChangeMap[Index].HwState)) {
+ PortStatus->PortChangeStatus |= mUsbPortChangeMap[Index].UefiState;
+ }
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Sets a feature for the specified root hub port.
+
+ @param This This EFI_USB2_HC_PROTOCOL instance.
+ @param PortNumber Root hub port to set.
+ @param PortFeature Feature to set
+
+ @return EFI_SUCCESS : The feature specified by PortFeature was set
+ @return EFI_INVALID_PARAMETER : PortNumber is invalid or PortFeature is invalid.
+ @return EFI_DEVICE_ERROR : Can't read register
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcSetRootHubPortFeature (
+ IN EFI_USB2_HC_PROTOCOL *This,
+ IN UINT8 PortNumber,
+ IN EFI_USB_PORT_FEATURE PortFeature
+ )
+{
+ USB2_HC_DEV *Ehc;
+ EFI_TPL OldTpl;
+ UINT32 Offset;
+ UINT32 State;
+ UINT32 TotalPort;
+ EFI_STATUS Status;
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ Ehc = EHC_FROM_THIS (This);
+ Status = EFI_SUCCESS;
+
+ TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
+
+ if (PortNumber >= TotalPort) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Offset = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber));
+ State = EhcReadOpReg (Ehc, Offset);
+
+ //
+ // Mask off the port status change bits, these bits are
+ // write clean bit
+ //
+ State &= ~PORTSC_CHANGE_MASK;
+
+ switch (PortFeature) {
+ case EfiUsbPortEnable:
+ //
+ // Sofeware can't set this bit, Port can only be enable by
+ // EHCI as a part of the reset and enable
+ //
+ State |= PORTSC_ENABLED;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortSuspend:
+ State |= PORTSC_SUSPEND;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortReset:
+ //
+ // Make sure Host Controller not halt before reset it
+ //
+ if (EhcIsHalt (Ehc)) {
+ Status = EhcRunHC (Ehc, EHC_GENERIC_TIME);
+
+ if (EFI_ERROR (Status)) {
+ EHC_DEBUG (("EhcSetRootHubPortFeature :failed to start HC - %r\n", Status));
+ break;
+ }
+ }
+
+ //
+ // Set one to PortReset bit must also set zero to PortEnable bit
+ //
+ State |= PORTSC_RESET;
+ State &= ~PORTSC_ENABLED;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortPower:
+ //
+ // Not supported, ignore the operation
+ //
+ Status = EFI_SUCCESS;
+ break;
+
+ case EfiUsbPortOwner:
+ State |= PORTSC_OWNER;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ default:
+ Status = EFI_INVALID_PARAMETER;
+ }
+
+ON_EXIT:
+ EHC_DEBUG (("EhcSetRootHubPortFeature: exit status %r\n", Status));
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Clears a feature for the specified root hub port.
+
+ @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 : The feature specified by PortFeature was cleared
+ @return for the USB root hub port specified by PortNumber.
+ @return EFI_INVALID_PARAMETER : PortNumber is invalid or PortFeature is invalid.
+ @return EFI_DEVICE_ERROR : Can't read register
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcClearRootHubPortFeature (
+ IN EFI_USB2_HC_PROTOCOL *This,
+ IN UINT8 PortNumber,
+ IN EFI_USB_PORT_FEATURE PortFeature
+ )
+{
+ USB2_HC_DEV *Ehc;
+ EFI_TPL OldTpl;
+ UINT32 Offset;
+ UINT32 State;
+ UINT32 TotalPort;
+ EFI_STATUS Status;
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ Ehc = EHC_FROM_THIS (This);
+ Status = EFI_SUCCESS;
+
+ TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
+
+ if (PortNumber >= TotalPort) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Offset = EHC_PORT_STAT_OFFSET + (4 * PortNumber);
+ State = EhcReadOpReg (Ehc, Offset);
+ State &= ~PORTSC_CHANGE_MASK;
+
+ switch (PortFeature) {
+ case EfiUsbPortEnable:
+ //
+ // Clear PORT_ENABLE feature means disable port.
+ //
+ State &= ~PORTSC_ENABLED;
+ EhcWriteOpReg (Ehc, Offset, State);
+ 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.
+ //
+ State &= ~PORSTSC_RESUME;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortReset:
+ //
+ // Clear PORT_RESET means clear the reset signal.
+ //
+ State &= ~PORTSC_RESET;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortOwner:
+ //
+ // Clear port owner means this port owned by EHC
+ //
+ State &= ~PORTSC_OWNER;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortConnectChange:
+ //
+ // Clear connect status change
+ //
+ State |= PORTSC_CONN_CHANGE;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortEnableChange:
+ //
+ // Clear enable status change
+ //
+ State |= PORTSC_ENABLE_CHANGE;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortOverCurrentChange:
+ //
+ // Clear PortOverCurrent change
+ //
+ State |= PORTSC_OVERCUR_CHANGE;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortPower:
+ case EfiUsbPortSuspendChange:
+ case EfiUsbPortResetChange:
+ //
+ // Not supported or not related operation
+ //
+ break;
+
+ default:
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ON_EXIT:
+ EHC_DEBUG (("EhcClearRootHubPortFeature: exit status %r\n", Status));
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Submits control transfer to a target USB device.
+
+ @param This This EFI_USB2_HC_PROTOCOL instance.
+ @param DeviceAddress The target device address
+ @param DeviceSpeed Target device speed.
+ @param MaximumPacketLength Maximum packet size the default control transfer
+ endpoint is capable of sending or receiving.
+ @param Request USB device request to send
+ @param TransferDirection Specifies the data direction for the data stage
+ @param Data Data buffer to be transmitted or received from USB
+ device.
+ @param DataLength The size (in bytes) of the data buffer
+ @param TimeOut Indicates the maximum timeout, in millisecond,
+ @param Translator Transaction translator to be used by this device.
+ @param TransferResult Return the result of this control transfer.
+
+ @return EFI_SUCCESS : Transfer was completed successfully.
+ @return EFI_OUT_OF_RESOURCES : The transfer failed due to lack of resources.
+ @return EFI_INVALID_PARAMETER : Some parameters are invalid.
+ @return EFI_TIMEOUT : Transfer failed due to timeout.
+ @return EFI_DEVICE_ERROR : Transfer failed due to host controller or device error.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcControlTransfer (
+ 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
+ )
+{
+ USB2_HC_DEV *Ehc;
+ URB *Urb;
+ EFI_TPL OldTpl;
+ UINT8 Endpoint;
+ EFI_STATUS Status;
+
+ //
+ // Validate parameters
+ //
+ if ((Request == NULL) || (TransferResult == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((TransferDirection != EfiUsbDataIn) &&
+ (TransferDirection != EfiUsbDataOut) &&
+ (TransferDirection != EfiUsbNoData)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((TransferDirection == EfiUsbNoData) &&
+ ((Data != NULL) || (*DataLength != 0))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((TransferDirection != EfiUsbNoData) &&
+ ((Data == NULL) || (*DataLength == 0))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) &&
+ (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((DeviceSpeed == EFI_USB_SPEED_LOW) && (MaximumPacketLength != 8)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ Ehc = EHC_FROM_THIS (This);
+
+ Status = EFI_DEVICE_ERROR;
+ *TransferResult = EFI_USB_ERR_SYSTEM;
+
+ if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
+ EHC_ERROR (("EhcControlTransfer: HC halted at entrance\n"));
+
+ EhcAckAllInterrupt (Ehc);
+ goto ON_EXIT;
+ }
+
+ EhcAckAllInterrupt (Ehc);
+
+ //
+ // Create a new URB, insert it into the asynchronous
+ // schedule list, then poll the execution status.
+ //
+ //
+ // Encode the direction in address, although default control
+ // endpoint is bidirectional. EhcCreateUrb expects this
+ // combination of Ep addr and its direction.
+ //
+ Endpoint = 0 | ((TransferDirection == EfiUsbDataIn) ? 0x80 : 0);
+ Urb = EhcCreateUrb (
+ Ehc,
+ DeviceAddress,
+ Endpoint,
+ DeviceSpeed,
+ 0,
+ MaximumPacketLength,
+ Translator,
+ EHC_CTRL_TRANSFER,
+ Request,
+ Data,
+ *DataLength,
+ NULL,
+ NULL,
+ 1
+ );
+
+ if (Urb == NULL) {
+ EHC_ERROR (("EhcControlTransfer: failed to create URB"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ EhcLinkQhToAsync (Ehc, Urb->Qh);
+ Status = EhcExecTransfer (Ehc, Urb, TimeOut);
+ EhcUnlinkQhFromAsync (Ehc, Urb->Qh);
+
+ //
+ // Get the status from URB. The result is updated in EhcCheckUrbResult
+ // which is called by EhcExecTransfer
+ //
+ *TransferResult = Urb->Result;
+ *DataLength = Urb->Completed;
+
+ if (*TransferResult == EFI_USB_NOERROR) {
+ Status = EFI_SUCCESS;
+ }
+
+ EhcAckAllInterrupt (Ehc);
+ EhcFreeUrb (Ehc, Urb);
+
+ON_EXIT:
+ Ehc->PciIo->Flush (Ehc->PciIo);
+ gBS->RestoreTPL (OldTpl);
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcControlTransfer: error - %r, transfer - %x\n", Status, *TransferResult));
+ }
+
+ return Status;
+}
+
+
+/**
+ Submits bulk transfer to a bulk endpoint of a USB device.
+
+ @param This This EFI_USB2_HC_PROTOCOL instance.
+ @param DeviceAddress Target device address
+ @param EndPointAddress Endpoint number and its direction in bit 7. .
+ @param DeviceSpeed Device speed, Low speed device doesn't support bulk
+ transfer.
+ @param MaximumPacketLength Maximum packet size the endpoint is capable of
+ sending or receiving.
+ @param DataBuffersNumber Number of data buffers prepared for the transfer.
+ @param Data Array of pointers to the buffers of data to transmit
+ from or receive into.
+ @param DataLength The lenght of the data buffer
+ @param DataToggle On input, the initial data toggle for the transfer;
+ On output, it is updated to to next data toggle to
+ use of the subsequent bulk
+ transfer.
+ @param Translator A pointr to the transaction translator data.
+ @param TimeOut Indicates the maximum time, in millisecond, which
+ the transfer is allowed to complete.
+ @param TransferResult A pointer to the detailed result information of the
+ bulk transfer.
+
+ @return EFI_SUCCESS : The transfer was completed successfully.
+ @return EFI_OUT_OF_RESOURCES : The transfer failed due to lack of resource.
+ @return EFI_INVALID_PARAMETER : Some parameters are invalid.
+ @return EFI_TIMEOUT : The transfer failed due to timeout.
+ @return EFI_DEVICE_ERROR : The transfer failed due to host controller error.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcBulkTransfer (
+ 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
+ )
+{
+ USB2_HC_DEV *Ehc;
+ URB *Urb;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // Validate the parameters
+ //
+ if ((DataLength == NULL) || (*DataLength == 0) ||
+ (Data == NULL) || (Data[0] == NULL) || (TransferResult == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*DataToggle != 0) && (*DataToggle != 1)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((DeviceSpeed == EFI_USB_SPEED_LOW) ||
+ ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) ||
+ ((EFI_USB_SPEED_HIGH == DeviceSpeed) && (MaximumPacketLength > 512))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ Ehc = EHC_FROM_THIS (This);
+
+ *TransferResult = EFI_USB_ERR_SYSTEM;
+ Status = EFI_DEVICE_ERROR;
+
+ if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
+ EHC_ERROR (("EhcBulkTransfer: HC is halted\n"));
+
+ EhcAckAllInterrupt (Ehc);
+ goto ON_EXIT;
+ }
+
+ EhcAckAllInterrupt (Ehc);
+
+ //
+ // Create a new URB, insert it into the asynchronous
+ // schedule list, then poll the execution status.
+ //
+ Urb = EhcCreateUrb (
+ Ehc,
+ DeviceAddress,
+ EndPointAddress,
+ DeviceSpeed,
+ *DataToggle,
+ MaximumPacketLength,
+ Translator,
+ EHC_BULK_TRANSFER,
+ NULL,
+ Data[0],
+ *DataLength,
+ NULL,
+ NULL,
+ 1
+ );
+
+ if (Urb == NULL) {
+ EHC_ERROR (("EhcBulkTransfer: failed to create URB\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ EhcLinkQhToAsync (Ehc, Urb->Qh);
+ Status = EhcExecTransfer (Ehc, Urb, TimeOut);
+ EhcUnlinkQhFromAsync (Ehc, Urb->Qh);
+
+ *TransferResult = Urb->Result;
+ *DataLength = Urb->Completed;
+ *DataToggle = Urb->DataToggle;
+
+ if (*TransferResult == EFI_USB_NOERROR) {
+ Status = EFI_SUCCESS;
+ }
+
+ EhcAckAllInterrupt (Ehc);
+ EhcFreeUrb (Ehc, Urb);
+
+ON_EXIT:
+ Ehc->PciIo->Flush (Ehc->PciIo);
+ gBS->RestoreTPL (OldTpl);
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcBulkTransfer: error - %r, transfer - %x\n", Status, *TransferResult));
+ }
+
+ return Status;
+}
+
+
+/**
+ Submits an asynchronous interrupt transfer to an
+ interrupt endpoint of a USB device.
+
+ @param This This EFI_USB2_HC_PROTOCOL instance.
+ @param DeviceAddress Target device address
+ @param EndPointAddress Endpoint number and its direction encoded in bit 7
+ @param DeviceSpeed Indicates device speed.
+ @param MaximumPacketLength Maximum packet size the target endpoint is capable
+ @param IsNewTransfer If TRUE, to submit an new asynchronous interrupt
+ transfer If FALSE, to remove the specified
+ asynchronous interrupt
+ @param DataToggle On input, the initial data toggle to use; on output,
+ it is updated to indicate the next data toggle
+ @param PollingInterval The he interval, in milliseconds, that the transfer
+ is polled.
+ @param DataLength The length of data to receive at the rate specified
+ by PollingInterval.
+ @param Translator Transaction translator to use.
+ @param CallBackFunction Function to call at the rate specified by
+ PollingInterval
+ @param Context Context to CallBackFunction.
+
+ @return EFI_SUCCESS : The request has been successfully submitted or canceled.
+ @return EFI_INVALID_PARAMETER : Some parameters are invalid.
+ @return EFI_OUT_OF_RESOURCES : The request failed due to a lack of resources.
+ @return EFI_DEVICE_ERROR : The transfer failed due to host controller error.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcAsyncInterruptTransfer (
+ 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
+ )
+{
+ USB2_HC_DEV *Ehc;
+ URB *Urb;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ UINT8 *Data;
+
+ //
+ // Validate parameters
+ //
+ if (!EHCI_IS_DATAIN (EndPointAddress)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IsNewTransfer) {
+ if (DataLength == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*DataToggle != 1) && (*DataToggle != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((PollingInterval > 255) || (PollingInterval < 1)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ Ehc = EHC_FROM_THIS (This);
+
+ //
+ // Delete Async interrupt transfer request. DataToggle will return
+ // the next data toggle to use.
+ //
+ if (!IsNewTransfer) {
+ Status = EhciDelAsyncIntTransfer (Ehc, DeviceAddress, EndPointAddress, DataToggle);
+
+ EHC_DEBUG (("EhcAsyncInterruptTransfer: remove old transfer - %r\n", Status));
+ goto ON_EXIT;
+ }
+
+ Status = EFI_SUCCESS;
+
+ if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
+ EHC_ERROR (("EhcAsyncInterruptTransfer: HC is halt\n"));
+ EhcAckAllInterrupt (Ehc);
+
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ EhcAckAllInterrupt (Ehc);
+
+ Data = AllocatePool (DataLength);
+
+ if (Data == NULL) {
+ EHC_ERROR (("EhcAsyncInterruptTransfer: failed to allocate buffer\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Urb = EhcCreateUrb (
+ Ehc,
+ DeviceAddress,
+ EndPointAddress,
+ DeviceSpeed,
+ *DataToggle,
+ MaximumPacketLength,
+ Translator,
+ EHC_INT_TRANSFER_ASYNC,
+ NULL,
+ Data,
+ DataLength,
+ CallBackFunction,
+ Context,
+ PollingInterval
+ );
+
+ if (Urb == NULL) {
+ EHC_ERROR (("EhcAsyncInterruptTransfer: failed to create URB\n"));
+
+ gBS->FreePool (Data);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // New asynchronous transfer must inserted to the head.
+ // Check the comments in EhcMoniteAsyncRequests
+ //
+ EhcLinkQhToPeriod (Ehc, Urb->Qh);
+ InsertHeadList (&Ehc->AsyncIntTransfers, &Urb->UrbList);
+
+ON_EXIT:
+ Ehc->PciIo->Flush (Ehc->PciIo);
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Submits synchronous interrupt transfer to an interrupt endpoint
+ of a USB device.
+
+ @param This This EFI_USB2_HC_PROTOCOL instance.
+ @param DeviceAddress Target device address
+ @param EndPointAddress Endpoint number and its direction encoded in bit 7
+ @param DeviceSpeed Indicates device speed.
+ @param MaximumPacketLength Maximum packet size the target endpoint is capable
+ of sending or receiving.
+ @param Data Buffer of data that will be transmitted to USB
+ device or received from USB device.
+ @param DataLength On input, the size, in bytes, of the data buffer; On
+ output, the number of bytes transferred.
+ @param DataToggle On input, the initial data toggle to use; on output,
+ it is updated to indicate the next data toggle
+ @param TimeOut Maximum time, in second, to complete
+ @param Translator Transaction translator to use.
+ @param TransferResult Variable to receive the transfer result
+
+ @return EFI_SUCCESS : The transfer was completed successfully.
+ @return EFI_OUT_OF_RESOURCES : The transfer failed due to lack of resource.
+ @return EFI_INVALID_PARAMETER : Some parameters are invalid.
+ @return EFI_TIMEOUT : The transfer failed due to timeout.
+ @return EFI_DEVICE_ERROR : The failed due to host controller or device error
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcSyncInterruptTransfer (
+ 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
+ )
+{
+ USB2_HC_DEV *Ehc;
+ EFI_TPL OldTpl;
+ URB *Urb;
+ EFI_STATUS Status;
+
+ //
+ // Validates parameters
+ //
+ if ((DataLength == NULL) || (*DataLength == 0) ||
+ (Data == NULL) || (TransferResult == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!EHCI_IS_DATAIN (EndPointAddress)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*DataToggle != 1) && (*DataToggle != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((DeviceSpeed == EFI_USB_SPEED_LOW) && (MaximumPacketLength != 8)) ||
+ ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) ||
+ ((DeviceSpeed == EFI_USB_SPEED_HIGH) && (MaximumPacketLength > 3072))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ Ehc = EHC_FROM_THIS (This);
+
+ *TransferResult = EFI_USB_ERR_SYSTEM;
+ Status = EFI_DEVICE_ERROR;
+
+ if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
+ EHC_ERROR (("EhcSyncInterruptTransfer: HC is halt\n"));
+
+ EhcAckAllInterrupt (Ehc);
+ goto ON_EXIT;
+ }
+
+ EhcAckAllInterrupt (Ehc);
+
+ Urb = EhcCreateUrb (
+ Ehc,
+ DeviceAddress,
+ EndPointAddress,
+ DeviceSpeed,
+ *DataToggle,
+ MaximumPacketLength,
+ Translator,
+ EHC_INT_TRANSFER_SYNC,
+ NULL,
+ Data,
+ *DataLength,
+ NULL,
+ NULL,
+ 1
+ );
+
+ if (Urb == NULL) {
+ EHC_ERROR (("EhcSyncInterruptTransfer: failed to create URB\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ EhcLinkQhToPeriod (Ehc, Urb->Qh);
+ Status = EhcExecTransfer (Ehc, Urb, TimeOut);
+ EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);
+
+ *TransferResult = Urb->Result;
+ *DataLength = Urb->Completed;
+ *DataToggle = Urb->DataToggle;
+
+ if (*TransferResult == EFI_USB_NOERROR) {
+ Status = EFI_SUCCESS;
+ }
+
+ON_EXIT:
+ Ehc->PciIo->Flush (Ehc->PciIo);
+ gBS->RestoreTPL (OldTpl);
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcSyncInterruptTransfer: error - %r, transfer - %x\n", Status, *TransferResult));
+ }
+
+ return Status;
+}
+
+
+/**
+ Submits isochronous transfer to a target USB device.
+
+ @param This This EFI_USB2_HC_PROTOCOL instance.
+ @param DeviceAddress Target device address
+ @param EndPointAddress End point address with its direction
+ @param DeviceSpeed Device speed, Low speed device doesn't support this
+ type.
+ @param MaximumPacketLength Maximum packet size that the endpoint is capable of
+ sending or receiving.
+ @param DataBuffersNumber Number of data buffers prepared for the transfer.
+ @param Data Array of pointers to the buffers of data that will
+ be transmitted to USB device or received from USB
+ device.
+ @param DataLength The size, in bytes, of the data buffer
+ @param Translator Transaction translator to use.
+ @param TransferResult Variable to receive the transfer result
+
+ @return EFI_UNSUPPORTED : Isochronous transfer is unsupported.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcIsochronousTransfer (
+ 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.
+
+ @param This This EFI_USB2_HC_PROTOCOL instance.
+ @param DeviceAddress Target device address
+ @param EndPointAddress End point address with its direction
+ @param DeviceSpeed Device speed, Low speed device doesn't support this
+ type.
+ @param MaximumPacketLength Maximum packet size that the endpoint is capable of
+ sending or receiving.
+ @param DataBuffersNumber Number of data buffers prepared for the transfer.
+ @param Data Array of pointers to the buffers of data that will
+ be transmitted to USB device or received from USB
+ device.
+ @param DataLength The size, in bytes, of the data buffer
+ @param Translator Transaction translator to use.
+ @param IsochronousCallBack Function to be called when the transfer complete
+ @param Context Context passed to the call back function as
+ parameter
+
+ @return EFI_UNSUPPORTED : Isochronous transfer isn't supported
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EhcAsyncIsochronousTransfer (
+ 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;
+}
+
+//@MT: EFI_DRIVER_ENTRY_POINT (EhcDriverEntryPoint)
+
+EFI_STATUS
+EFIAPI
+EhcDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+Routine Description:
+
+ Entry point for EFI drivers.
+
+Arguments:
+
+ ImageHandle - EFI_HANDLE
+ SystemTable - EFI_SYSTEM_TABLE
+
+Returns:
+
+ EFI_SUCCESS Success
+ EFI_DEVICE_ERROR Fail
+
+--*/
+{
+ return EfiLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &gEhciDriverBinding,
+ ImageHandle,
+ &gEhciComponentName,
+ NULL,
+ NULL
+ );
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle. Any
+ ControllerHandle that has Usb2HcProtocol installed will
+ be supported.
+
+ @param This Protocol instance pointer.
+ @param Controlle 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
+EhcDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ USB_CLASSC UsbClassCReg;
+
+ //
+ // Test whether there is PCI IO Protocol attached on the controller handle.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ &PciIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciIoWidthUint8,
+ EHC_PCI_CLASSC,
+ sizeof (USB_CLASSC) / sizeof (UINT8),
+ &UsbClassCReg
+ );
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Test whether the controller belongs to Ehci type
+ //
+ if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) ||
+ (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) ||
+ (UsbClassCReg.PI != EHC_PCI_CLASSC_PI)) {
+
+ Status = EFI_UNSUPPORTED;
+ }
+
+ON_EXIT:
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ return Status;
+}
+
+
+/**
+ Create and initialize a USB2_HC_DEV
+
+ @param PciIo The PciIo on this device
+
+ @return The allocated and initialized USB2_HC_DEV structure
+ @return if created, otherwise NULL.
+
+**/
+STATIC
+USB2_HC_DEV *
+EhcCreateUsb2Hc (
+ IN EFI_PCI_IO_PROTOCOL *PciIo
+ )
+{
+ USB2_HC_DEV *Ehc;
+ EFI_STATUS Status;
+
+ Ehc = AllocateZeroPool (sizeof (USB2_HC_DEV));
+
+ if (Ehc == NULL) {
+ return NULL;
+ }
+
+ //
+ // Init EFI_USB2_HC_PROTOCOL interface and private data structure
+ //
+ Ehc->Signature = USB2_HC_DEV_SIGNATURE;
+
+ Ehc->Usb2Hc.GetCapability = EhcGetCapability;
+ Ehc->Usb2Hc.Reset = EhcReset;
+ Ehc->Usb2Hc.GetState = EhcGetState;
+ Ehc->Usb2Hc.SetState = EhcSetState;
+ Ehc->Usb2Hc.ControlTransfer = EhcControlTransfer;
+ Ehc->Usb2Hc.BulkTransfer = EhcBulkTransfer;
+ Ehc->Usb2Hc.AsyncInterruptTransfer = EhcAsyncInterruptTransfer;
+ Ehc->Usb2Hc.SyncInterruptTransfer = EhcSyncInterruptTransfer;
+ Ehc->Usb2Hc.IsochronousTransfer = EhcIsochronousTransfer;
+ Ehc->Usb2Hc.AsyncIsochronousTransfer = EhcAsyncIsochronousTransfer;
+ Ehc->Usb2Hc.GetRootHubPortStatus = EhcGetRootHubPortStatus;
+ Ehc->Usb2Hc.SetRootHubPortFeature = EhcSetRootHubPortFeature;
+ Ehc->Usb2Hc.ClearRootHubPortFeature = EhcClearRootHubPortFeature;
+ Ehc->Usb2Hc.MajorRevision = 0x1;
+ Ehc->Usb2Hc.MinorRevision = 0x1;
+
+ Ehc->PciIo = PciIo;
+
+ InitializeListHead (&Ehc->AsyncIntTransfers);
+
+ Ehc->HcStructParams = EhcReadCapRegister (Ehc, EHC_HCSPARAMS_OFFSET);
+ Ehc->HcCapParams = EhcReadCapRegister (Ehc, EHC_HCCPARAMS_OFFSET);
+ Ehc->CapLen = EhcReadCapRegister (Ehc, EHC_CAPLENGTH_OFFSET) & 0x0FF;
+
+ EHC_DEBUG (("EhcCreateUsb2Hc: capability length %d\n", Ehc->CapLen));
+
+ //
+ // Create AsyncRequest Polling Timer
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ EhcMoniteAsyncRequests,
+ Ehc,
+ &Ehc->PollTimer
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->FreePool (Ehc);
+ return NULL;
+ }
+
+ return Ehc;
+}
+
+
+/**
+ Starting the Usb EHCI Driver
+
+ @param This Protocol instance pointer.
+ @param Controller Handle of device to test
+ @param RemainingDevicePath Not used
+
+ @return EFI_SUCCESS : supports this device.
+ @return EFI_UNSUPPORTED : do not support this device.
+ @return EFI_DEVICE_ERROR : cannot be started due to device Error
+ @return EFI_OUT_OF_RESOURCES : cannot allocate resources
+
+**/
+EFI_STATUS
+EFIAPI
+EhcDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ USB2_HC_DEV *Ehc;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ //
+ // Open the PciIo Protocol, then enable the USB host controller
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ &PciIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcDriverBindingStart: failed to open PCI_IO\n"));
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationEnable,
+ EFI_PCI_DEVICE_ENABLE,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcDriverBindingStart: failed to enable controller\n"));
+ goto CLOSE_PCIIO;
+ }
+
+ //
+ // Create then install USB2_HC_PROTOCOL
+ //
+ Ehc = EhcCreateUsb2Hc (PciIo);
+
+ if (Ehc == NULL) {
+ EHC_ERROR (("EhcDriverBindingStart: failed to create USB2_HC\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto CLOSE_PCIIO;
+ }
+
+ Status = gBS->InstallProtocolInterface (
+ &Controller,
+ &gEfiUsb2HcProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Ehc->Usb2Hc
+ );
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcDriverBindingStart: failed to install USB2_HC Protocol\n"));
+ goto FREE_POOL;
+ }
+
+ //
+ // Robustnesss improvement such as for UoL
+ //
+ EhcClearLegacySupport (Ehc);
+ EhcResetHC (Ehc, EHC_STALL_1_SECOND);
+
+ Status = EhcInitHC (Ehc);
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcDriverBindingStart: failed to init host controller\n"));
+ goto UNINSTALL_USBHC;
+ }
+
+ //
+ // Start the asynchronous interrupt monitor
+ //
+ Status = gBS->SetTimer (Ehc->PollTimer, TimerPeriodic, EHC_ASYNC_POLL_TIME);
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcDriverBindingStart: failed to start async interrupt monitor\n"));
+
+ EhcHaltHC (Ehc, EHC_GENERIC_TIME);
+ goto UNINSTALL_USBHC;
+ }
+
+ //
+ // Install the component name protocol, don't fail the start
+ // because of something for display.
+ //
+ AddUnicodeString (
+ "eng",
+ gEhciComponentName.SupportedLanguages,
+ &Ehc->ControllerNameTable,
+ L"Enhanced Host Controller (USB 2.0)"
+ );
+
+ EHC_DEBUG (("EhcDriverBindingStart: EHCI started for controller @ %x\n", Controller));
+ return EFI_SUCCESS;
+
+UNINSTALL_USBHC:
+ gBS->UninstallProtocolInterface (
+ Controller,
+ &gEfiUsb2HcProtocolGuid,
+ &Ehc->Usb2Hc
+ );
+
+FREE_POOL:
+ EhcFreeSched (Ehc);
+ gBS->CloseEvent (Ehc->PollTimer);
+ gBS->FreePool (Ehc);
+
+CLOSE_PCIIO:
+ 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 Success
+ @return EFI_DEVICE_ERROR Fail
+
+**/
+EFI_STATUS
+EFIAPI
+EhcDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_USB2_HC_PROTOCOL *Usb2Hc;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ USB2_HC_DEV *Ehc;
+
+ //
+ // 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,
+ &Usb2Hc,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Ehc = EHC_FROM_THIS (Usb2Hc);
+ PciIo = Ehc->PciIo;
+
+ //
+ // Stop AsyncRequest Polling timer then stop the EHCI driver
+ // and uninstall the EHCI protocl.
+ //
+ gBS->SetTimer (Ehc->PollTimer, TimerCancel, EHC_ASYNC_POLL_TIME);
+ EhcHaltHC (Ehc, EHC_GENERIC_TIME);
+
+ Status = gBS->UninstallProtocolInterface (
+ Controller,
+ &gEfiUsb2HcProtocolGuid,
+ Usb2Hc
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Ehc->PollTimer != NULL) {
+ gBS->CloseEvent (Ehc->PollTimer);
+ }
+
+ EhcFreeSched (Ehc);
+
+ if (Ehc->ControllerNameTable) {
+ FreeUnicodeStringTable (Ehc->ControllerNameTable);
+ }
+
+ //
+ // Disable the USB Host Controller
+ //
+ PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationDisable,
+ EFI_PCI_DEVICE_ENABLE,
+ NULL
+ );
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->FreePool (Ehc);
+ return Status;
+}
+
+EFI_DRIVER_BINDING_PROTOCOL
+gEhciDriverBinding = {
+ EhcDriverBindingSupported,
+ EhcDriverBindingStart,
+ EhcDriverBindingStop,
+ 0x10,
+ NULL,
+ NULL
+};
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.h b/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.h new file mode 100644 index 0000000000..57c0c00084 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.h @@ -0,0 +1,152 @@ +/** @file
+
+Copyright (c) 2006 - 2007, 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.h
+
+Abstract:
+
+
+Revision History
+
+**/
+
+#ifndef _EFI_EHCI_H_
+#define _EFI_EHCI_H_
+
+//
+// The package level header files this module uses
+//
+#include <PiDxe.h>
+//
+// The protocols, PPI and GUID defintions for this module
+//
+#include <Protocol/Usb2HostController.h>
+#include <Protocol/PciIo.h>
+//
+// The Library classes this module consumes
+//
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+
+#include <IndustryStandard/Pci22.h>
+
+typedef struct _USB2_HC_DEV USB2_HC_DEV;
+
+#include "UsbHcMem.h"
+#include "EhciReg.h"
+#include "EhciUrb.h"
+#include "EhciSched.h"
+#include "EhciDebug.h"
+
+enum {
+ USB2_HC_DEV_SIGNATURE = EFI_SIGNATURE_32 ('e', 'h', 'c', 'i'),
+ EHC_STALL_1_MICROSECOND = 1,
+ EHC_STALL_1_MILLISECOND = 1000 * EHC_STALL_1_MICROSECOND,
+ EHC_STALL_1_SECOND = 1000 * EHC_STALL_1_MILLISECOND,
+
+ EHC_SET_PORT_RESET_TIME = 50 * EHC_STALL_1_MILLISECOND,
+ EHC_CLEAR_PORT_RESET_TIME = EHC_STALL_1_MILLISECOND,
+ EHC_GENERIC_TIME = 10 * EHC_STALL_1_MILLISECOND,
+ EHC_SYNC_POLL_TIME = 20 * EHC_STALL_1_MICROSECOND,
+ EHC_ASYNC_POLL_TIME = 50 * 10000UL, // The unit of time is 100us
+
+ EHC_TPL = TPL_NOTIFY,
+};
+
+//
+//Iterate through the doule linked list. NOT delete safe
+//
+#define EFI_LIST_FOR_EACH(Entry, ListHead) \
+ for(Entry = (ListHead)->ForwardLink; Entry != (ListHead); Entry = Entry->ForwardLink)
+
+//
+//Iterate through the doule linked list. This is delete-safe.
+//Don't touch NextEntry
+//
+#define EFI_LIST_FOR_EACH_SAFE(Entry, NextEntry, ListHead) \
+ for(Entry = (ListHead)->ForwardLink, NextEntry = Entry->ForwardLink;\
+ Entry != (ListHead); Entry = NextEntry, NextEntry = Entry->ForwardLink)
+
+#define EFI_LIST_CONTAINER(Entry, Type, Field) _CR(Entry, Type, Field)
+
+
+#define EHC_LOW_32BIT(Addr64) ((UINT32)(((UINTN)(Addr64)) & 0XFFFFFFFF))
+#define EHC_HIGH_32BIT(Addr64) ((UINT32)(RShiftU64((UINTN)(Addr64), 32) & 0XFFFFFFFF))
+#define EHC_BIT_IS_SET(Data, Bit) ((BOOLEAN)(((Data) & (Bit)) == (Bit)))
+
+#define EHC_REG_BIT_IS_SET(Ehc, Offset, Bit) \
+ (EHC_BIT_IS_SET(EhcReadOpReg ((Ehc), (Offset)), (Bit)))
+
+#define EHC_FROM_THIS(a) CR(a, USB2_HC_DEV, Usb2Hc, USB2_HC_DEV_SIGNATURE)
+
+typedef struct _USB2_HC_DEV {
+ UINTN Signature;
+ EFI_USB2_HC_PROTOCOL Usb2Hc;
+
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ USBHC_MEM_POOL *MemPool;
+
+ //
+ // Schedule data shared between asynchronous and periodic
+ // transfers:
+ // ShortReadStop, as its name indicates, is used to terminate
+ // the short read except the control transfer. EHCI follows
+ // the alternative next QTD point when a short read happens.
+ // For control transfer, even the short read happens, try the
+ // status stage.
+ //
+ EHC_QTD *ShortReadStop;
+ EFI_EVENT PollTimer;
+
+ //
+ // Asynchronous(bulk and control) transfer schedule data:
+ // ReclaimHead is used as the head of the asynchronous transfer
+ // list. It acts as the reclamation header.
+ //
+ EHC_QH *ReclaimHead;
+
+ //
+ // Peroidic (interrupt) transfer schedule data:
+ //
+ VOID *PeriodFrame; // Mapped as common buffer
+ VOID *PeriodFrameHost;
+ VOID *PeriodFrameMap;
+
+ EHC_QH *PeriodOne;
+ LIST_ENTRY AsyncIntTransfers;
+
+ //
+ // EHCI configuration data
+ //
+ UINT32 HcStructParams; // Cache of HC structure parameter, EHC_HCSPARAMS_OFFSET
+ UINT32 HcCapParams; // Cache of HC capability parameter, HCCPARAMS
+ UINT32 CapLen; // Capability length
+ UINT32 High32bitAddr;
+
+ //
+ // Misc
+ //
+ EFI_UNICODE_STRING_TABLE *ControllerNameTable;
+} USB2_HC_DEV;
+
+
+extern EFI_DRIVER_BINDING_PROTOCOL gEhciDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gEhciComponentName;
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.inf b/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.inf new file mode 100644 index 0000000000..2be08326cf --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.inf @@ -0,0 +1,100 @@ +#/** @file
+# Component name for module Ehci
+#
+# FIX ME!
+# Copyright (c) 2006, Intel Corporation. All right reserved.
+#
+# 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.
+#
+#
+#**/
+
+################################################################################
+#
+# Defines Section - statements that will be processed to create a Makefile.
+#
+################################################################################
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Ehci
+ FILE_GUID = BDFE430E-8F2A-4db0-9991-6F856594777E
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = EhcDriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+################################################################################
+#
+# Sources Section - list of files that are required for the build to succeed.
+#
+################################################################################
+
+[Sources.common]
+ UsbHcMem.h
+ EhciUrb.c
+ EhciReg.h
+ UsbHcMem.c
+ EhciSched.c
+ EhciDebug.c
+ EhciReg.c
+ EhciDebug.h
+ ComponentName.c
+ EhciUrb.h
+ Ehci.h
+ EhciSched.h
+ Ehci.c
+
+################################################################################
+#
+# Package Dependency Section - list of Package files that are required for
+# this module.
+#
+################################################################################
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+################################################################################
+#
+# Library Class Section - list of Library Classes that are required for
+# this module.
+#
+################################################################################
+
+[LibraryClasses]
+ MemoryAllocationLib
+ BaseLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ DebugLib
+
+
+################################################################################
+#
+# Protocol C Name Section - list of Protocol and Protocol Notify C Names
+# that this module uses or produces.
+#
+################################################################################
+
+[Protocols]
+ gEfiPciIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUsb2HcProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.msa b/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.msa new file mode 100644 index 0000000000..dec9936c8e --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.msa @@ -0,0 +1,81 @@ +<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>Ehci</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>BDFE430E-8F2A-4db0-9991-6F856594777E</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module Ehci</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2006, Intel Corporation. All right reserved.</Copyright>
+ <License>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.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>Ehci</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseMemoryLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>MemoryAllocationLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>Ehci.c</Filename>
+ <Filename>EhciSched.h</Filename>
+ <Filename>Ehci.h</Filename>
+ <Filename>EhciUrb.h</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>EhciDebug.h</Filename>
+ <Filename>EhciReg.c</Filename>
+ <Filename>EhciDebug.c</Filename>
+ <Filename>EhciSched.c</Filename>
+ <Filename>UsbHcMem.c</Filename>
+ <Filename>EhciReg.h</Filename>
+ <Filename>EhciUrb.c</Filename>
+ <Filename>UsbHcMem.h</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUsb2HcProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiPciIoProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>EhcDriverEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea>
\ No newline at end of file diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/EhciDebug.c b/MdeModulePkg/Bus/Pci/EhciDxe/EhciDebug.c new file mode 100644 index 0000000000..086cdbc9fc --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/EhciDebug.c @@ -0,0 +1,345 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciDebug.c
+
+Abstract:
+ This file provides the information dump support for EHCI when in debug mode.
+You can dynamically adjust the debug level by changing variable mEhcDebugLevel
+and mEhcErrorLevel.
+
+Revision History
+
+**/
+
+
+#include "Ehci.h"
+
+#ifdef EFI_DEBUG
+UINTN mEhcDebugMask = USB_DEBUG_FORCE_OUTPUT;
+
+
+/**
+ EHCI's debug output function. It determines whether
+ to output by the mask and level
+
+ @param Level The output level
+ @param Format The format parameters to the print
+ @param ... The variable length parameters after format
+
+ @return None
+
+**/
+VOID
+EhciDebugPrint (
+ IN UINTN Level,
+ IN CHAR8 *Format,
+ ...
+ )
+{
+
+ VA_LIST Marker;
+
+ VA_START (Marker, Format);
+
+ if (Level & mEhcDebugMask) {
+ if (mEhcDebugMask & USB_DEBUG_FORCE_OUTPUT) {
+ DebugVPrint (DEBUG_ERROR, Format, Marker);
+ } else {
+ DebugVPrint (DEBUG_INFO, Format, Marker);
+ }
+ }
+
+ VA_END (Marker);
+}
+
+
+/**
+ EHCI's debug output function. It determines whether
+ to output by the mask and level
+
+ @param Format The format parameters to the print
+ @param ... The variable length parameters after format
+
+ @return None
+
+**/
+VOID
+EhcDebug (
+ IN CHAR8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+
+ VA_START (Marker, Format);
+ DebugVPrint (DEBUG_INFO, Format, Marker);
+ VA_END (Marker);
+}
+
+
+/**
+ EHCI's error output function. It determines whether
+ to output by the mask and level
+
+ @param Format The format parameters to the print
+ @param ... The variable length parameters after format
+
+ @return None
+
+**/
+VOID
+EhcError (
+ IN CHAR8 *Format,
+ ...
+ )
+{
+
+ VA_LIST Marker;
+
+ VA_START (Marker, Format);
+ DebugVPrint (DEBUG_ERROR, Format, Marker);
+ VA_END (Marker);
+}
+
+
+/**
+ Dump the status byte in QTD/QH to a more friendly
+ format
+
+ @param State The state in the QTD/QH
+ @param Level The output level
+
+ @return None
+
+**/
+STATIC
+VOID
+EhcDumpStatus (
+ IN UINT32 State,
+ IN UINTN Level
+ )
+{
+ if (EHC_BIT_IS_SET (State, QTD_STAT_DO_PING)) {
+ EhciDebugPrint (Level, " Do_Ping");
+ } else {
+ EhciDebugPrint (Level, " Do_Out");
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_DO_CS)) {
+ EhciDebugPrint (Level, " Do_CS");
+ } else {
+ EhciDebugPrint (Level, " Do_SS");
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR)) {
+ EhciDebugPrint (Level, " Transfer_Error");
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) {
+ EhciDebugPrint (Level, " Babble_Error");
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) {
+ EhciDebugPrint (Level, " Buffer_Error");
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {
+ EhciDebugPrint (Level, " Halted");
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {
+ EhciDebugPrint (Level, " Active");
+ }
+
+ EhciDebugPrint (Level, "\n");
+}
+
+
+/**
+ Dump the fields of a QTD
+
+ @param Qtd The QTD to dump
+ @param Msg The message to print before the dump
+
+ @return None
+
+**/
+VOID
+EhcDumpQtd (
+ IN EHC_QTD *Qtd,
+ IN UINT8 *Msg
+ )
+{
+ QTD_HW *QtdHw;
+ UINTN Index;
+ UINTN Level;
+
+ Level = EHC_DEBUG_QTD;
+
+ if (Msg != NULL) {
+ EhciDebugPrint (Level, Msg);
+ }
+
+ EhciDebugPrint (Level, "Queue TD @ 0x%x, data length %d\n", Qtd, Qtd->DataLen);
+
+ QtdHw = &Qtd->QtdHw;
+
+ EhciDebugPrint (Level, "Next QTD : %x\n", QtdHw->NextQtd);
+ EhciDebugPrint (Level, "AltNext QTD : %x\n", QtdHw->AltNext);
+ EhciDebugPrint (Level, "Status : %x\n", QtdHw->Status);
+ EhcDumpStatus (QtdHw->Status, Level);
+
+ if (QtdHw->Pid == QTD_PID_SETUP) {
+ EhciDebugPrint (Level, "PID : Setup\n");
+
+ } else if (QtdHw->Pid == QTD_PID_INPUT) {
+ EhciDebugPrint (Level, "PID : IN\n");
+
+ } else if (QtdHw->Pid == QTD_PID_OUTPUT) {
+ EhciDebugPrint (Level, "PID : OUT\n");
+
+ }
+
+ EhciDebugPrint (Level, "Error Count : %d\n", QtdHw->ErrCnt);
+ EhciDebugPrint (Level, "Current Page : %d\n", QtdHw->CurPage);
+ EhciDebugPrint (Level, "IOC : %d\n", QtdHw->IOC);
+ EhciDebugPrint (Level, "Total Bytes : %d\n", QtdHw->TotalBytes);
+ EhciDebugPrint (Level, "Data Toggle : %d\n", QtdHw->DataToggle);
+
+ for (Index = 0; Index < 5; Index++) {
+ EhciDebugPrint (Level, "Page[%d] : 0x%x\n", Index, QtdHw->Page[Index]);
+ }
+}
+
+
+/**
+ Dump the queue head
+
+ @param Qh The queue head to dump
+ @param Msg The message to print before the dump
+ @param DumpBuf Whether to dump the memory buffer of the associated QTD
+
+ @return None
+
+**/
+VOID
+EhcDumpQh (
+ IN EHC_QH *Qh,
+ IN UINT8 *Msg,
+ IN BOOLEAN DumpBuf
+ )
+{
+ EHC_QTD *Qtd;
+ QH_HW *QhHw;
+ LIST_ENTRY *Entry;
+ UINTN Index;
+ UINTN Level;
+
+ Level = EHC_DEBUG_QH;
+
+ if (Msg != NULL) {
+ EhciDebugPrint (Level, Msg);
+ }
+
+ EhciDebugPrint (Level, "Queue head @ 0x%x, interval %d, next qh %x\n",
+ Qh, Qh->Interval, Qh->NextQh);
+
+ QhHw = &Qh->QhHw;
+
+ EhciDebugPrint (Level, "Hoziontal link: %x\n", QhHw->HorizonLink);
+ EhciDebugPrint (Level, "Device address: %d\n", QhHw->DeviceAddr);
+ EhciDebugPrint (Level, "Inactive : %d\n", QhHw->Inactive);
+ EhciDebugPrint (Level, "EP number : %d\n", QhHw->EpNum);
+ EhciDebugPrint (Level, "EP speed : %d\n", QhHw->EpSpeed);
+ EhciDebugPrint (Level, "DT control : %d\n", QhHw->DtCtrl);
+ EhciDebugPrint (Level, "Reclaim head : %d\n", QhHw->ReclaimHead);
+ EhciDebugPrint (Level, "Max packet len: %d\n", QhHw->MaxPacketLen);
+ EhciDebugPrint (Level, "Ctrl EP : %d\n", QhHw->CtrlEp);
+ EhciDebugPrint (Level, "Nak reload : %d\n", QhHw->NakReload);
+
+ EhciDebugPrint (Level, "SMask : %x\n", QhHw->SMask);
+ EhciDebugPrint (Level, "CMask : %x\n", QhHw->CMask);
+ EhciDebugPrint (Level, "Hub address : %d\n", QhHw->HubAddr);
+ EhciDebugPrint (Level, "Hub port : %d\n", QhHw->PortNum);
+ EhciDebugPrint (Level, "Multiplier : %d\n", QhHw->Multiplier);
+
+ EhciDebugPrint (Level, "Cur QTD : %x\n", QhHw->CurQtd);
+
+ EhciDebugPrint (Level, "Next QTD : %x\n", QhHw->NextQtd);
+ EhciDebugPrint (Level, "AltNext QTD : %x\n", QhHw->AltQtd);
+ EhciDebugPrint (Level, "Status : %x\n", QhHw->Status);
+ EhcDumpStatus (QhHw->Status, Level);
+
+ if (QhHw->Pid == QTD_PID_SETUP) {
+ EhciDebugPrint (Level, "PID : Setup\n");
+
+ } else if (QhHw->Pid == QTD_PID_INPUT) {
+ EhciDebugPrint (Level, "PID : IN\n");
+
+ } else if (QhHw->Pid == QTD_PID_OUTPUT) {
+ EhciDebugPrint (Level, "PID : OUT\n");
+ }
+
+ EhciDebugPrint (Level, "Error Count : %d\n", QhHw->ErrCnt);
+ EhciDebugPrint (Level, "Current Page : %d\n", QhHw->CurPage);
+ EhciDebugPrint (Level, "IOC : %d\n", QhHw->IOC);
+ EhciDebugPrint (Level, "Total Bytes : %d\n", QhHw->TotalBytes);
+ EhciDebugPrint (Level, "Data Toggle : %d\n", QhHw->DataToggle);
+
+ for (Index = 0; Index < 5; Index++) {
+ EhciDebugPrint (Level, "Page[%d] : 0x%x\n", Index, QhHw->Page[Index]);
+ }
+
+ EhciDebugPrint (Level, "\n");
+
+ EFI_LIST_FOR_EACH (Entry, &Qh->Qtds) {
+ Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList);
+ EhcDumpQtd (Qtd, NULL);
+
+ if (DumpBuf && (Qtd->DataLen != 0)) {
+ EhcDumpBuf (Qtd->Data, Qtd->DataLen);
+ }
+ }
+}
+
+
+/**
+ Dump the buffer in the form of hex
+
+ @param Buf The buffer to dump
+ @param Len The length of buffer
+
+ @return None
+
+**/
+VOID
+EhcDumpBuf (
+ IN UINT8 *Buf,
+ IN UINTN Len
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < Len; Index++) {
+ if (Index % 16 == 0) {
+ EhciDebugPrint (EHC_DEBUG_BUF, "\n");
+ }
+
+ EhciDebugPrint (EHC_DEBUG_BUF, "%02x ", Buf[Index]);
+ }
+
+ EhciDebugPrint (EHC_DEBUG_BUF, "\n");
+}
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/EhciDebug.h b/MdeModulePkg/Bus/Pci/EhciDxe/EhciDebug.h new file mode 100644 index 0000000000..be4bcd6556 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/EhciDebug.h @@ -0,0 +1,159 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciDebug.h
+
+Abstract:
+
+ This file contains the definination for host controller debug support routines
+
+Revision History
+
+**/
+
+#ifndef _EFI_EHCI_DEBUG_H_
+#define _EFI_EHCI_DEBUG_H_
+
+
+enum {
+ USB_DEBUG_FORCE_OUTPUT = (UINTN)(1 << 0),
+
+ EHC_DEBUG_QH = (UINTN)(1 << 8),
+ EHC_DEBUG_QTD = (UINTN)(1 << 9),
+ EHC_DEBUG_BUF = (UINTN)(1 << 10),
+};
+
+
+/**
+ EHCI's debug output function. It determines whether
+ to output by the mask and level
+
+ @param Level The output level
+ @param Format The format parameters to the print
+ @param ... The variable length parameters after format
+
+ @return None
+
+**/
+VOID
+EhciDebugPrint (
+ IN UINTN Level,
+ IN CHAR8 *Format,
+ ...
+ )
+;
+
+
+/**
+ EHCI's debug output function. It determines whether
+ to output by the mask and level
+
+ @param Format The format parameters to the print
+ @param ... The variable length parameters after format
+
+ @return None
+
+**/
+VOID
+EhcDebug (
+ IN CHAR8 *Format,
+ ...
+ )
+;
+
+
+
+/**
+ EHCI's error output function. It determines whether
+ to output by the mask and level
+
+ @param Format The format parameters to the print
+ @param ... The variable length parameters after format
+
+ @return None
+
+**/
+VOID
+EhcError (
+ IN CHAR8 *Format,
+ ...
+ )
+;
+
+
+
+/**
+ Dump the fields of a QTD
+
+ @param Qtd The QTD to dump
+ @param Msg The message to print before the dump
+
+ @return None
+
+**/
+VOID
+EhcDumpQtd (
+ IN EHC_QTD *Qtd,
+ IN UINT8 *Msg
+ )
+;
+
+
+
+/**
+ Dump the queue head
+
+ @param Qh The queue head to dump
+ @param Msg The message to print before the dump
+ @param DumpBuf Whether to dump the memory buffer of the associated QTD
+
+ @return None
+
+**/
+VOID
+EhcDumpQh (
+ IN EHC_QH *Qh,
+ IN UINT8 *Msg,
+ IN BOOLEAN DumpBuf
+ )
+;
+
+
+
+/**
+ Dump the buffer in the form of hex
+
+ @param Buf The buffer to dump
+ @param Len The length of buffer
+
+ @return None
+
+**/
+VOID
+EhcDumpBuf (
+ IN UINT8 *Buf,
+ IN UINTN Len
+ )
+;
+
+#ifdef EFI_DEBUG
+ #define EHC_DEBUG(arg) EhcDebug arg
+ #define EHC_ERROR(arg) EhcError arg
+ #define EHC_DUMP_QH(arg) EhcDumpQh arg
+#else
+ #define EHC_DEBUG(arg)
+ #define EHC_ERROR(arg)
+ #define EHC_DUMP_QH(arg)
+#endif
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/EhciReg.c b/MdeModulePkg/Bus/Pci/EhciDxe/EhciReg.c new file mode 100644 index 0000000000..aa16605556 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/EhciReg.c @@ -0,0 +1,634 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciReg.c
+
+Abstract:
+
+ The EHCI register operation routines.
+
+
+Revision History
+
+**/
+
+
+#include "Ehci.h"
+
+
+/**
+ Read EHCI capability register
+
+ @param Ehc The Ehc device
+ @param Offset Capability register address
+
+ @return The register content read
+
+**/
+UINT32
+EhcReadCapRegister (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Offset
+ )
+{
+ UINT32 Data;
+ EFI_STATUS Status;
+
+ Status = Ehc->PciIo->Mem.Read (
+ Ehc->PciIo,
+ EfiPciIoWidthUint32,
+ EHC_BAR_INDEX,
+ (UINT64) Offset,
+ 1,
+ &Data
+ );
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcReadCapRegister: Pci Io read error - %r at %d\n", Status, Offset));
+ Data = 0xFFFF;
+ }
+
+ return Data;
+}
+
+
+/**
+ Read Ehc Operation register
+
+ @param Ehc The EHCI device
+ @param Offset The operation register offset
+
+ @return The register content read
+
+**/
+UINT32
+EhcReadOpReg (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Offset
+ )
+{
+ UINT32 Data;
+ EFI_STATUS Status;
+
+ ASSERT (Ehc->CapLen != 0);
+
+ Status = Ehc->PciIo->Mem.Read (
+ Ehc->PciIo,
+ EfiPciIoWidthUint32,
+ EHC_BAR_INDEX,
+ (UINT64) (Ehc->CapLen + Offset),
+ 1,
+ &Data
+ );
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcReadOpReg: Pci Io Read error - %r at %d\n", Status, Offset));
+ Data = 0xFFFF;
+ }
+
+ return Data;
+}
+
+
+/**
+ Write the data to the EHCI operation register
+
+ @param Ehc The EHCI device
+ @param Offset EHCI operation register offset
+ @param Data The data to write
+
+ @return None
+
+**/
+VOID
+EhcWriteOpReg (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Offset,
+ IN UINT32 Data
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (Ehc->CapLen != 0);
+
+ Status = Ehc->PciIo->Mem.Write (
+ Ehc->PciIo,
+ EfiPciIoWidthUint32,
+ EHC_BAR_INDEX,
+ (UINT64) (Ehc->CapLen + Offset),
+ 1,
+ &Data
+ );
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcWriteOpReg: Pci Io Write error: %r at %d\n", Status, Offset));
+ }
+}
+
+
+/**
+ Set one bit of the operational register while keeping other bits
+
+ @param Ehc The EHCI device
+ @param Offset The offset of the operational register
+ @param Bit The bit mask of the register to set
+
+ @return None
+
+**/
+STATIC
+VOID
+EhcSetOpRegBit (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Offset,
+ IN UINT32 Bit
+ )
+{
+ UINT32 Data;
+
+ Data = EhcReadOpReg (Ehc, Offset);
+ Data |= Bit;
+ EhcWriteOpReg (Ehc, Offset, Data);
+}
+
+
+/**
+ Clear one bit of the operational register while keeping other bits
+
+ @param Ehc The EHCI device
+ @param Offset The offset of the operational register
+ @param Bit The bit mask of the register to clear
+
+ @return None
+
+**/
+STATIC
+VOID
+EhcClearOpRegBit (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Offset,
+ IN UINT32 Bit
+ )
+{
+ UINT32 Data;
+
+ Data = EhcReadOpReg (Ehc, Offset);
+ Data &= ~Bit;
+ EhcWriteOpReg (Ehc, Offset, Data);
+}
+
+
+/**
+ Wait the operation register's bit as specified by Bit
+ to become set (or clear)
+
+ @param Ehc The EHCI device
+ @param Offset The offset of the operation register
+ @param Bit The bit of the register to wait for
+ @param WaitToSet Wait the bit to set or clear
+ @param Timeout The time to wait before abort (in millisecond)
+
+ @retval EFI_SUCCESS The bit successfully changed by host controller
+ @retval EFI_TIMEOUT The time out occurred
+
+**/
+STATIC
+EFI_STATUS
+EhcWaitOpRegBit (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Offset,
+ IN UINT32 Bit,
+ IN BOOLEAN WaitToSet,
+ IN UINT32 Timeout
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < Timeout / EHC_SYNC_POLL_TIME + 1; Index++) {
+ if (EHC_REG_BIT_IS_SET (Ehc, Offset, Bit) == WaitToSet) {
+ return EFI_SUCCESS;
+ }
+
+ gBS->Stall (EHC_SYNC_POLL_TIME);
+ }
+
+ return EFI_TIMEOUT;
+}
+
+
+/**
+ Add support for UEFI Over Legacy (UoL) feature, stop
+ the legacy USB SMI support
+
+ @param Ehc The EHCI device.
+
+ @return None
+
+**/
+VOID
+EhcClearLegacySupport (
+ IN USB2_HC_DEV *Ehc
+ )
+{
+ UINT32 ExtendCap;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINT32 Value;
+ UINT32 TimeOut;
+
+ EHC_DEBUG (("EhcClearLegacySupport: called to clear legacy support\n"));
+
+ PciIo = Ehc->PciIo;
+ ExtendCap = (Ehc->HcCapParams >> 8) & 0xFF;
+
+ PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
+ PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap + 0x4, 1, &Value);
+
+ PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
+ Value |= (0x1 << 24);
+ PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
+
+ TimeOut = 40;
+ while (TimeOut--) {
+ gBS->Stall (500);
+
+ PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
+
+ if ((Value & 0x01010000) == 0x01000000) {
+ break;
+ }
+ }
+
+ PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
+ PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap + 0x4, 1, &Value);
+}
+
+
+
+/**
+ Set door bell and wait it to be ACKed by host controller.
+ This function is used to synchronize with the hardware.
+
+ @param Ehc The EHCI device
+ @param Timeout The time to wait before abort (in millisecond, ms)
+
+ @return EFI_SUCCESS : Synchronized with the hardware
+ @return EFI_TIMEOUT : Time out happened while waiting door bell to set
+
+**/
+EFI_STATUS
+EhcSetAndWaitDoorBell (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Data;
+
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_IAAD);
+
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_IAA, TRUE, Timeout);
+
+ //
+ // ACK the IAA bit in USBSTS register. Make sure other
+ // interrupt bits are not ACKed. These bits are WC (Write Clean).
+ //
+ Data = EhcReadOpReg (Ehc, EHC_USBSTS_OFFSET);
+ Data &= ~USBSTS_INTACK_MASK;
+ Data |= USBSTS_IAA;
+
+ EhcWriteOpReg (Ehc, EHC_USBSTS_OFFSET, Data);
+
+ return Status;
+}
+
+
+/**
+ Clear all the interrutp status bits, these bits
+ are Write-Clean
+
+ @param Ehc The EHCI device
+
+ @return None
+
+**/
+VOID
+EhcAckAllInterrupt (
+ IN USB2_HC_DEV *Ehc
+ )
+{
+ EhcWriteOpReg (Ehc, EHC_USBSTS_OFFSET, USBSTS_INTACK_MASK);
+}
+
+
+/**
+ Enable the periodic schedule then wait EHC to
+ actually enable it.
+
+ @param Ehc The EHCI device
+ @param Timeout The time to wait before abort (in millisecond, ms)
+
+ @return EFI_SUCCESS : The periodical schedule is enabled
+ @return EFI_TIMEOUT : Time out happened while enabling periodic schedule
+
+**/
+STATIC
+EFI_STATUS
+EhcEnablePeriodSchd (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_PERIOD);
+
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_PERIOD_ENABLED, TRUE, Timeout);
+ return Status;
+}
+
+
+
+/**
+ Disable periodic schedule
+
+ @param Ehc The EHCI device
+ @param Timeout Time to wait before abort (in millisecond, ms)
+
+ @return EFI_SUCCESS : Periodic schedule is disabled.
+ @return EFI_DEVICE_ERROR : Fail to disable periodic schedule
+
+**/
+STATIC
+EFI_STATUS
+EhcDisablePeriodSchd (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ EhcClearOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_PERIOD);
+
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_PERIOD_ENABLED, FALSE, Timeout);
+ return Status;
+}
+
+
+
+/**
+ Enable asynchrounous schedule
+
+ @param Ehc The EHCI device
+ @param Timeout Time to wait before abort
+
+ @return EFI_SUCCESS : The EHCI asynchronous schedule is enabled
+ @return Others : Failed to enable the asynchronous scheudle
+
+**/
+STATIC
+EFI_STATUS
+EhcEnableAsyncSchd (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_ASYNC);
+
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_ASYNC_ENABLED, TRUE, Timeout);
+ return Status;
+}
+
+
+
+/**
+ Disable asynchrounous schedule
+
+ @param Ehc The EHCI device
+ @param Timeout Time to wait before abort (in millisecond, ms)
+
+ @return EFI_SUCCESS : The asynchronous schedule is disabled
+ @return Others : Failed to disable the asynchronous schedule
+
+**/
+STATIC
+EFI_STATUS
+EhcDisableAsyncSchd (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ EhcClearOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_ASYNC);
+
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_ASYNC_ENABLED, FALSE, Timeout);
+ return Status;
+}
+
+
+
+/**
+ Whether Ehc is halted
+
+ @param Ehc The EHCI device
+
+ @return TRUE : The controller is halted
+ @return FALSE : It isn't halted
+
+**/
+BOOLEAN
+EhcIsHalt (
+ IN USB2_HC_DEV *Ehc
+ )
+{
+ return EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT);
+}
+
+
+/**
+ Whether system error occurred
+
+ @param Ehc The EHCI device
+
+ @return TRUE : System error happened
+ @return FALSE : No system error
+
+**/
+BOOLEAN
+EhcIsSysError (
+ IN USB2_HC_DEV *Ehc
+ )
+{
+ return EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_SYS_ERROR);
+}
+
+
+/**
+ Reset the host controller
+
+ @param Ehc The EHCI device
+ @param Timeout Time to wait before abort (in millisecond, ms)
+
+ @return EFI_SUCCESS : The host controller is reset
+ @return Others : Failed to reset the host
+
+**/
+EFI_STATUS
+EhcResetHC (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Host can only be reset when it is halt. If not so, halt it
+ //
+ if (!EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT)) {
+ Status = EhcHaltHC (Ehc, Timeout);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RESET);
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RESET, FALSE, Timeout);
+ return Status;
+}
+
+
+/**
+ Halt the host controller
+
+ @param Ehc The EHCI device
+ @param Timeout Time to wait before abort
+
+ @return EFI_SUCCESS : The EHCI is halt
+ @return EFI_TIMEOUT : Failed to halt the controller before Timeout
+
+**/
+EFI_STATUS
+EhcHaltHC (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ EhcClearOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT, TRUE, Timeout);
+ return Status;
+}
+
+
+/**
+ Set the EHCI to run
+
+ @param Ehc The EHCI device
+ @param Timeout Time to wait before abort
+
+ @return EFI_SUCCESS : The EHCI is running
+ @return Others : Failed to set the EHCI to run
+
+**/
+EFI_STATUS
+EhcRunHC (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT, FALSE, Timeout);
+ return Status;
+}
+
+
+/**
+ Initialize the HC hardware.
+ EHCI spec lists the five things to do to initialize the hardware
+ 1. Program CTRLDSSEGMENT
+ 2. Set USBINTR to enable interrupts
+ 3. Set periodic list base
+ 4. Set USBCMD, interrupt threshold, frame list size etc
+ 5. Write 1 to CONFIGFLAG to route all ports to EHCI
+
+ @param Ehc The EHCI device
+
+ @return EFI_SUCCESS : The EHCI has come out of halt state
+ @return EFI_TIMEOUT : Time out happened
+
+**/
+EFI_STATUS
+EhcInitHC (
+ IN USB2_HC_DEV *Ehc
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (EhcIsHalt (Ehc));
+
+ //
+ // Allocate the periodic frame and associated memeory
+ // management facilities if not already done.
+ //
+ if (Ehc->PeriodFrame != NULL) {
+ EhcFreeSched (Ehc);
+ }
+
+ Status = EhcInitSched (Ehc);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // 1. Program the CTRLDSSEGMENT register with the high 32 bit addr
+ //
+ EhcWriteOpReg (Ehc, EHC_CTRLDSSEG_OFFSET, Ehc->High32bitAddr);
+
+ //
+ // 2. Clear USBINTR to disable all the interrupt. UEFI works by polling
+ //
+ EhcWriteOpReg (Ehc, EHC_USBINTR_OFFSET, 0);
+
+ //
+ // 3. Program periodic frame list, already done in EhcInitSched
+ // 4. Start the Host Controller
+ //
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);
+
+ //
+ // 5. Set all ports routing to EHC
+ //
+ EhcSetOpRegBit (Ehc, EHC_CONFIG_FLAG_OFFSET, CONFIGFLAG_ROUTE_EHC);
+
+ Status = EhcEnablePeriodSchd (Ehc, EHC_GENERIC_TIME);
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcInitHC: failed to enable period schedule\n"));
+ return Status;
+ }
+
+ Status = EhcEnableAsyncSchd (Ehc, EHC_GENERIC_TIME);
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcInitHC: failed to enable async schedule\n"));
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/EhciReg.h b/MdeModulePkg/Bus/Pci/EhciDxe/EhciReg.h new file mode 100644 index 0000000000..2ac27eb39c --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/EhciReg.h @@ -0,0 +1,351 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciReg.h
+
+Abstract:
+
+ This file contains the definination for host controller register operation routines
+
+Revision History
+
+**/
+
+#ifndef _EFI_EHCI_REG_H_
+#define _EFI_EHCI_REG_H_
+
+
+enum {
+ //
+ // Capability register offset
+ //
+ EHC_CAPLENGTH_OFFSET = 0, // Capability register length offset
+ EHC_HCSPARAMS_OFFSET = 0x04, // Structural Parameters 04-07h
+ EHC_HCCPARAMS_OFFSET = 0x08, // Capability parameters offset
+
+ //
+ // Capability register bit definition
+ //
+ HCSP_NPORTS = 0x0F, // Number of root hub port
+ HCCP_64BIT = 0x01, // 64-bit addressing capability
+
+ //
+ // Operational register offset
+ //
+ EHC_USBCMD_OFFSET = 0x0, // USB command register offset
+ EHC_USBSTS_OFFSET = 0x04, // Statue register offset
+ EHC_USBINTR_OFFSET = 0x08, // USB interrutp offset
+ EHC_FRINDEX_OFFSET = 0x0C, // Frame index offset
+ EHC_CTRLDSSEG_OFFSET = 0x10, // Control data structure segment offset
+ EHC_FRAME_BASE_OFFSET = 0x14, // Frame list base address offset
+ EHC_ASYNC_HEAD_OFFSET = 0x18, // Next asynchronous list address offset
+ EHC_CONFIG_FLAG_OFFSET = 0x40, // Configure flag register offset
+ EHC_PORT_STAT_OFFSET = 0x44, // Port status/control offset
+
+ EHC_FRAME_LEN = 1024,
+
+ //
+ // Register bit definition
+ //
+ CONFIGFLAG_ROUTE_EHC = 0x01, // Route port to EHC
+
+ USBCMD_RUN = 0x01, // Run/stop
+ USBCMD_RESET = 0x02, // Start the host controller reset
+ USBCMD_ENABLE_PERIOD = 0x10, // Enable periodic schedule
+ USBCMD_ENABLE_ASYNC = 0x20, // Enable asynchronous schedule
+ USBCMD_IAAD = 0x40, // Interrupt on async advance doorbell
+
+ USBSTS_IAA = 0x20, // Interrupt on async advance
+ USBSTS_PERIOD_ENABLED = 0x4000, // Periodic schedule status
+ USBSTS_ASYNC_ENABLED = 0x8000, // Asynchronous schedule status
+ USBSTS_HALT = 0x1000, // Host controller halted
+ USBSTS_SYS_ERROR = 0x10, // Host system error
+ USBSTS_INTACK_MASK = 0x003F, // Mask for the interrupt ACK, the WC
+ // (write clean) bits in USBSTS register
+
+ PORTSC_CONN = 0x01, // Current Connect Status
+ PORTSC_CONN_CHANGE = 0x02, // Connect Status Change
+ PORTSC_ENABLED = 0x04, // Port Enable / Disable
+ PORTSC_ENABLE_CHANGE = 0x08, // Port Enable / Disable Change
+ PORTSC_OVERCUR = 0x10, // Over current Active
+ PORTSC_OVERCUR_CHANGE = 0x20, // Over current Change
+ PORSTSC_RESUME = 0x40, // Force Port Resume
+ PORTSC_SUSPEND = 0x80, // Port Suspend State
+ PORTSC_RESET = 0x100, // Port Reset
+ PORTSC_LINESTATE_K = 0x400, // Line Status K-state
+ PORTSC_LINESTATE_J = 0x800, // Line Status J-state
+ PORTSC_POWER = 0x1000, // Port Power
+ PORTSC_OWNER = 0x2000, // Port Owner
+ PORTSC_CHANGE_MASK = 0x2A, // Mask of the port change bits,
+ // they are WC (write clean)
+ //
+ // PCI Configuration Registers
+ //
+ EHC_PCI_CLASSC = 0x09,
+ EHC_PCI_CLASSC_PI = 0x20,
+ EHC_BAR_INDEX = 0, /* how many bytes away from USB_BASE to 0x10 */
+};
+
+#define EHC_LINK_TERMINATED(Link) (((Link) & 0x01) != 0)
+
+#define EHC_ADDR(High, QhHw32) \
+ ((VOID *) (UINTN) (LShiftU64 ((High), 32) | ((QhHw32) & 0xFFFFFFF0)))
+
+#define EHCI_IS_DATAIN(EndpointAddr) EHC_BIT_IS_SET((EndpointAddr), 0x80)
+
+//
+// Structure to map the hardware port states to the
+// UEFI's port states.
+//
+typedef struct {
+ UINT16 HwState;
+ UINT16 UefiState;
+} USB_PORT_STATE_MAP;
+
+//
+// Ehci Data and Ctrl Structures
+//
+#pragma pack(1)
+typedef struct {
+ UINT8 PI;
+ UINT8 SubClassCode;
+ UINT8 BaseCode;
+} USB_CLASSC;
+#pragma pack()
+
+
+UINT32
+EhcReadCapRegister (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Offset
+ )
+/*++
+
+Routine Description:
+
+ Read EHCI capability register
+
+Arguments:
+
+ Ehc - The Ehc device
+ Offset - Capability register address
+
+Returns:
+
+ The register content read
+
+--*/
+;
+
+
+/**
+ Read Ehc Operation register
+
+ @param Ehc The EHCI device
+ @param Offset The operation register offset
+
+ @return The register content read
+
+**/
+UINT32
+EhcReadOpReg (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Offset
+ )
+;
+
+
+/**
+ Write the data to the EHCI operation register
+
+ @param Ehc The EHCI device
+ @param Offset EHCI operation register offset
+ @param Data The data to write
+
+ @return None
+
+**/
+VOID
+EhcWriteOpReg (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Offset,
+ IN UINT32 Data
+ )
+;
+
+
+/**
+ Add support for UEFI Over Legacy (UoL) feature, stop
+ the legacy USB SMI support
+
+ @param Ehc The EHCI device.
+
+ @return None
+
+**/
+VOID
+EhcClearLegacySupport (
+ IN USB2_HC_DEV *Ehc
+ )
+;
+
+
+
+/**
+ Set door bell and wait it to be ACKed by host controller.
+ This function is used to synchronize with the hardware.
+
+ @param Ehc The EHCI device
+ @param Timeout The time to wait before abort (in millisecond, ms)
+
+ @return EFI_SUCCESS : Synchronized with the hardware
+ @return EFI_TIMEOUT : Time out happened while waiting door bell to set
+
+**/
+EFI_STATUS
+EhcSetAndWaitDoorBell (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+;
+
+
+/**
+ Clear all the interrutp status bits, these bits
+ are Write-Clean
+
+ @param Ehc The EHCI device
+
+ @return None
+
+**/
+VOID
+EhcAckAllInterrupt (
+ IN USB2_HC_DEV *Ehc
+ )
+;
+
+
+
+/**
+ Whether Ehc is halted
+
+ @param Ehc The EHCI device
+
+ @return TRUE : The controller is halted
+ @return FALSE : It isn't halted
+
+**/
+BOOLEAN
+EhcIsHalt (
+ IN USB2_HC_DEV *Ehc
+ )
+;
+
+
+/**
+ Whether system error occurred
+
+ @param Ehc The EHCI device
+
+ @return TRUE : System error happened
+ @return FALSE : No system error
+
+**/
+BOOLEAN
+EhcIsSysError (
+ IN USB2_HC_DEV *Ehc
+ )
+;
+
+
+
+/**
+ Reset the host controller
+
+ @param Ehc The EHCI device
+ @param Timeout Time to wait before abort (in millisecond, ms)
+
+ @return EFI_SUCCESS : The host controller is reset
+ @return Others : Failed to reset the host
+
+**/
+EFI_STATUS
+EhcResetHC (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+;
+
+
+
+/**
+ Halt the host controller
+
+ @param Ehc The EHCI device
+ @param Timeout Time to wait before abort
+
+ @return EFI_SUCCESS : The EHCI is halt
+ @return EFI_TIMEOUT : Failed to halt the controller before Timeout
+
+**/
+EFI_STATUS
+EhcHaltHC (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+;
+
+
+
+/**
+ Set the EHCI to run
+
+ @param Ehc The EHCI device
+ @param Timeout Time to wait before abort
+
+ @return EFI_SUCCESS : The EHCI is running
+ @return Others : Failed to set the EHCI to run
+
+**/
+EFI_STATUS
+EhcRunHC (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+;
+
+
+
+/**
+ Initialize the HC hardware.
+ EHCI spec lists the five things to do to initialize the hardware
+ 1. Program CTRLDSSEGMENT
+ 2. Set USBINTR to enable interrupts
+ 3. Set periodic list base
+ 4. Set USBCMD, interrupt threshold, frame list size etc
+ 5. Write 1 to CONFIGFLAG to route all ports to EHCI
+
+ @param Ehc The EHCI device
+
+ @return EFI_SUCCESS : The EHCI has come out of halt state
+ @return EFI_TIMEOUT : Time out happened
+
+**/
+EFI_STATUS
+EhcInitHC (
+ IN USB2_HC_DEV *Ehc
+ )
+;
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/EhciSched.c b/MdeModulePkg/Bus/Pci/EhciDxe/EhciSched.c new file mode 100644 index 0000000000..b13e4078b0 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/EhciSched.c @@ -0,0 +1,941 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciSched.c
+
+Abstract:
+
+ EHCI transfer scheduling routines
+
+Revision History
+
+**/
+
+#include "Ehci.h"
+
+
+/**
+ Create helper QTD/QH for the EHCI device
+
+ @param Ehc The EHCI device
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH
+ @retval EFI_SUCCESS Helper QH/QTD are created
+
+**/
+STATIC
+EFI_STATUS
+EhcCreateHelpQ (
+ IN USB2_HC_DEV *Ehc
+ )
+{
+ USB_ENDPOINT Ep;
+ EHC_QH *Qh;
+ QH_HW *QhHw;
+ EHC_QTD *Qtd;
+
+ //
+ // Create an inactive Qtd to terminate the short packet read.
+ //
+ Qtd = EhcCreateQtd (Ehc, NULL, 0, QTD_PID_INPUT, 0, 64);
+
+ if (Qtd == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Qtd->QtdHw.Status = QTD_STAT_HALTED;
+ Ehc->ShortReadStop = Qtd;
+
+ //
+ // Create a QH to act as the EHC reclamation header.
+ // Set the header to loopback to itself.
+ //
+ Ep.DevAddr = 0;
+ Ep.EpAddr = 1;
+ Ep.Direction = EfiUsbDataIn;
+ Ep.DevSpeed = EFI_USB_SPEED_HIGH;
+ Ep.MaxPacket = 64;
+ Ep.HubAddr = 0;
+ Ep.HubPort = 0;
+ Ep.Toggle = 0;
+ Ep.Type = EHC_BULK_TRANSFER;
+ Ep.PollRate = 1;
+
+ Qh = EhcCreateQh (Ehc, &Ep);
+
+ if (Qh == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ QhHw = &Qh->QhHw;
+ QhHw->HorizonLink = QH_LINK (QhHw, EHC_TYPE_QH, FALSE);
+ QhHw->Status = QTD_STAT_HALTED;
+ QhHw->ReclaimHead = 1;
+ Ehc->ReclaimHead = Qh;
+
+ //
+ // Create a dummy QH to act as the terminator for periodical schedule
+ //
+ Ep.EpAddr = 2;
+ Ep.Type = EHC_INT_TRANSFER_SYNC;
+
+ Qh = EhcCreateQh (Ehc, &Ep);
+
+ if (Qh == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Qh->QhHw.Status = QTD_STAT_HALTED;
+ Ehc->PeriodOne = Qh;
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Initialize the schedule data structure such as frame list
+
+ @param Ehc The EHCI device to init schedule data for
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data
+ @retval EFI_SUCCESS The schedule data is initialized
+
+**/
+EFI_STATUS
+EhcInitSched (
+ IN USB2_HC_DEV *Ehc
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ VOID *Buf;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ VOID *Map;
+ UINTN Pages;
+ UINTN Bytes;
+ UINTN Index;
+ UINT32 *Desc;
+ EFI_STATUS Status;
+
+ //
+ // First initialize the periodical schedule data:
+ // 1. Allocate and map the memory for the frame list
+ // 2. Create the help QTD/QH
+ // 3. Initialize the frame entries
+ // 4. Set the frame list register
+ //
+ PciIo = Ehc->PciIo;
+
+ Bytes = 4096;
+ Pages = EFI_SIZE_TO_PAGES (Bytes);
+
+ Status = PciIo->AllocateBuffer (
+ PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ Pages,
+ &Buf,
+ 0
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = PciIo->Map (
+ PciIo,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ Buf,
+ &Bytes,
+ &PhyAddr,
+ &Map
+ );
+
+ if (EFI_ERROR (Status) || (Bytes != 4096)) {
+ PciIo->FreeBuffer (PciIo, Pages, Buf);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ehc->PeriodFrameHost = Buf;
+ Ehc->PeriodFrame = (VOID *) ((UINTN) PhyAddr);
+ Ehc->PeriodFrameMap = Map;
+ Ehc->High32bitAddr = EHC_HIGH_32BIT (PhyAddr);
+
+ //
+ // Init memory pool management then create the helper
+ // QTD/QH. If failed, previously allocated resources
+ // will be freed by EhcFreeSched
+ //
+ Ehc->MemPool = UsbHcInitMemPool (
+ PciIo,
+ EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT),
+ Ehc->High32bitAddr
+ );
+
+ if (Ehc->MemPool == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EhcCreateHelpQ (Ehc);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Initialize the frame list entries then set the registers
+ //
+ Desc = (UINT32 *) Ehc->PeriodFrame;
+
+ for (Index = 0; Index < EHC_FRAME_LEN; Index++) {
+ Desc[Index] = QH_LINK (Ehc->PeriodOne, EHC_TYPE_QH, FALSE);
+ }
+
+ EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (Ehc->PeriodFrame));
+
+ //
+ // Second initialize the asynchronous schedule:
+ // Only need to set the AsynListAddr register to
+ // the reclamation header
+ //
+ EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (Ehc->ReclaimHead));
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Free the schedule data. It may be partially initialized.
+
+ @param Ehc The EHCI device
+
+ @return None
+
+**/
+VOID
+EhcFreeSched (
+ IN USB2_HC_DEV *Ehc
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);
+ EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);
+
+ if (Ehc->PeriodOne != NULL) {
+ UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));
+ Ehc->PeriodOne = NULL;
+ }
+
+ if (Ehc->ReclaimHead != NULL) {
+ UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));
+ Ehc->ReclaimHead = NULL;
+ }
+
+ if (Ehc->ShortReadStop != NULL) {
+ UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));
+ Ehc->ShortReadStop = NULL;
+ }
+
+ if (Ehc->MemPool != NULL) {
+ UsbHcFreeMemPool (Ehc->MemPool);
+ Ehc->MemPool = NULL;
+ }
+
+ if (Ehc->PeriodFrame != NULL) {
+ PciIo = Ehc->PciIo;
+ ASSERT (PciIo != NULL);
+
+ PciIo->Unmap (PciIo, Ehc->PeriodFrameMap);
+
+ PciIo->FreeBuffer (
+ PciIo,
+ EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE),
+ Ehc->PeriodFrameHost
+ );
+
+ Ehc->PeriodFrame = NULL;
+ }
+}
+
+
+
+/**
+ Link the queue head to the asynchronous schedule list.
+ UEFI only supports one CTRL/BULK transfer at a time
+ due to its interfaces. This simplifies the AsynList
+ management: A reclamation header is always linked to
+ the AsyncListAddr, the only active QH is appended to it.
+
+ @param Ehc The EHCI device
+ @param Qh The queue head to link
+
+ @return None
+
+**/
+VOID
+EhcLinkQhToAsync (
+ IN USB2_HC_DEV *Ehc,
+ IN EHC_QH *Qh
+ )
+{
+ EHC_QH *Head;
+
+ //
+ // Append the queue head after the reclaim header, then
+ // fix the hardware visiable parts (EHCI R1.0 page 72).
+ // ReclaimHead is always linked to the EHCI's AsynListAddr.
+ //
+ Head = Ehc->ReclaimHead;
+
+ Qh->NextQh = Head->NextQh;
+ Head->NextQh = Qh;
+
+ Qh->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);;
+ Head->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
+}
+
+
+/**
+ Unlink a queue head from the asynchronous schedule list.
+ Need to synchronize with hardware
+
+ @param Ehc The EHCI device
+ @param Qh The queue head to unlink
+
+ @return None
+
+**/
+VOID
+EhcUnlinkQhFromAsync (
+ IN USB2_HC_DEV *Ehc,
+ IN EHC_QH *Qh
+ )
+{
+ EHC_QH *Head;
+ EFI_STATUS Status;
+
+ ASSERT (Ehc->ReclaimHead->NextQh == Qh);
+
+ //
+ // Remove the QH from reclamation head, then update the hardware
+ // visiable part: Only need to loopback the ReclaimHead. The Qh
+ // is pointing to ReclaimHead (which is staill in the list).
+ //
+ Head = Ehc->ReclaimHead;
+
+ Head->NextQh = Qh->NextQh;
+ Qh->NextQh = NULL;
+
+ Head->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);
+
+ //
+ // Set and wait the door bell to synchronize with the hardware
+ //
+ Status = EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIME);
+
+ if (EFI_ERROR (Status)) {
+ EHC_ERROR (("EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));
+ }
+}
+
+
+/**
+ Link a queue head for interrupt transfer to the periodic
+ schedule frame list. This code is very much the same as
+ that in UHCI.
+
+ @param Ehc The EHCI device
+ @param Qh The queue head to link
+
+ @return None
+
+**/
+VOID
+EhcLinkQhToPeriod (
+ IN USB2_HC_DEV *Ehc,
+ IN EHC_QH *Qh
+ )
+{
+ UINT32 *Frames;
+ UINTN Index;
+ EHC_QH *Prev;
+ EHC_QH *Next;
+
+ Frames = Ehc->PeriodFrame;
+
+ for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {
+ //
+ // First QH can't be NULL because we always keep PeriodOne
+ // heads on the frame list
+ //
+ ASSERT (!EHC_LINK_TERMINATED (Frames[Index]));
+ Next = EHC_ADDR (Ehc->High32bitAddr, Frames[Index]);
+ Prev = NULL;
+
+ //
+ // Now, insert the queue head (Qh) into this frame:
+ // 1. Find a queue head with the same poll interval, just insert
+ // Qh after this queue head, then we are done.
+ //
+ // 2. Find the position to insert the queue head into:
+ // Previous head's interval is bigger than Qh's
+ // Next head's interval is less than Qh's
+ // Then, insert the Qh between then
+ //
+ while (Next->Interval > Qh->Interval) {
+ Prev = Next;
+ Next = Next->NextQh;
+ }
+
+ ASSERT (Next != NULL);
+
+ //
+ // The entry may have been linked into the frame by early insertation.
+ // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
+ // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
+ // It isn't necessary to compare all the QH with the same interval to
+ // Qh. This is because if there is other QH with the same interval, Qh
+ // should has been inserted after that at Frames[0] and at Frames[0] it is
+ // impossible for (Next == Qh)
+ //
+ if (Next == Qh) {
+ continue;
+ }
+
+ if (Next->Interval == Qh->Interval) {
+ //
+ // If there is a QH with the same interval, it locates at
+ // Frames[0], and we can simply insert it after this QH. We
+ // are all done.
+ //
+ ASSERT ((Index == 0) && (Qh->NextQh == NULL));
+
+ Prev = Next;
+ Next = Next->NextQh;
+
+ Qh->NextQh = Next;
+ Prev->NextQh = Qh;
+
+ Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink;
+ Prev->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
+ break;
+ }
+
+ //
+ // OK, find the right position, insert it in. If Qh's next
+ // link has already been set, it is in position. This is
+ // guarranted by 2^n polling interval.
+ //
+ if (Qh->NextQh == NULL) {
+ Qh->NextQh = Next;
+ Qh->QhHw.HorizonLink = QH_LINK (Next, EHC_TYPE_QH, FALSE);
+ }
+
+ if (Prev == NULL) {
+ Frames[Index] = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
+ } else {
+ Prev->NextQh = Qh;
+ Prev->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
+ }
+ }
+}
+
+
+/**
+ Unlink an interrupt queue head from the periodic
+ schedule frame list
+
+ @param Ehc The EHCI device
+ @param Qh The queue head to unlink
+
+ @return None
+
+**/
+VOID
+EhcUnlinkQhFromPeriod (
+ IN USB2_HC_DEV *Ehc,
+ IN EHC_QH *Qh
+ )
+{
+ UINT32 *Frames;
+ UINTN Index;
+ EHC_QH *Prev;
+ EHC_QH *This;
+
+ Frames = Ehc->PeriodFrame;
+
+ for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {
+ //
+ // Frame link can't be NULL because we always keep PeroidOne
+ // on the frame list
+ //
+ ASSERT (!EHC_LINK_TERMINATED (Frames[Index]));
+ This = EHC_ADDR (Ehc->High32bitAddr, Frames[Index]);
+ Prev = NULL;
+
+ //
+ // Walk through the frame's QH list to find the
+ // queue head to remove
+ //
+ while ((This != NULL) && (This != Qh)) {
+ Prev = This;
+ This = This->NextQh;
+ }
+
+ //
+ // Qh may have already been unlinked from this frame
+ // by early action. See the comments in EhcLinkQhToPeriod.
+ //
+ if (This == NULL) {
+ continue;
+ }
+
+ if (Prev == NULL) {
+ //
+ // Qh is the first entry in the frame
+ //
+ Frames[Index] = Qh->QhHw.HorizonLink;
+ } else {
+ Prev->NextQh = Qh->NextQh;
+ Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink;
+ }
+ }
+}
+
+
+
+/**
+ Check the URB's execution result and update the URB's
+ result accordingly.
+
+ @param Ehc The EHCI device
+ @param Urb The URB to check result
+
+ @return Whether the result of URB transfer is finialized.
+
+**/
+STATIC
+BOOLEAN
+EhcCheckUrbResult (
+ IN USB2_HC_DEV *Ehc,
+ IN URB *Urb
+ )
+{
+ LIST_ENTRY *Entry;
+ EHC_QTD *Qtd;
+ QTD_HW *QtdHw;
+ UINT8 State;
+ BOOLEAN Finished;
+
+ ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));
+
+ Finished = TRUE;
+ Urb->Completed = 0;
+
+ Urb->Result = EFI_USB_NOERROR;
+
+ if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
+ Urb->Result |= EFI_USB_ERR_SYSTEM;
+ goto ON_EXIT;
+ }
+
+ EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {
+ Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList);
+ QtdHw = &Qtd->QtdHw;
+ State = (UINT8) QtdHw->Status;
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {
+ //
+ // EHCI will halt the queue head when met some error.
+ // If it is halted, the result of URB is finialized.
+ //
+ if ((State & QTD_STAT_ERR_MASK) == 0) {
+ Urb->Result |= EFI_USB_ERR_STALL;
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) {
+ Urb->Result |= EFI_USB_ERR_BABBLE;
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) {
+ Urb->Result |= EFI_USB_ERR_BUFFER;
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR) && (QtdHw->ErrCnt == 0)) {
+ Urb->Result |= EFI_USB_ERR_TIMEOUT;
+ }
+
+ Finished = TRUE;
+ goto ON_EXIT;
+
+ } else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {
+ //
+ // The QTD is still active, no need to check furthur.
+ //
+ Urb->Result |= EFI_USB_ERR_NOTEXECUTE;
+
+ Finished = FALSE;
+ goto ON_EXIT;
+
+ } else {
+ //
+ // This QTD is finished OK or met short packet read. Update the
+ // transfer length if it isn't a setup.
+ //
+ if (QtdHw->Pid != QTD_PID_SETUP) {
+ Urb->Completed += Qtd->DataLen - QtdHw->TotalBytes;
+ }
+
+ if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {
+ EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));
+
+ //
+ // Short packet read condition. If it isn't a setup transfer,
+ // no need to check furthur: the queue head will halt at the
+ // ShortReadStop. If it is a setup transfer, need to check the
+ // Status Stage of the setup transfer to get the finial result
+ //
+ if (QtdHw->AltNext == QTD_LINK (Ehc->ShortReadStop, FALSE)) {
+ EHC_DEBUG (("EhcCheckUrbResult: Short packet read, break\n"));
+
+ Finished = TRUE;
+ goto ON_EXIT;
+ }
+
+ EHC_DEBUG (("EhcCheckUrbResult: Short packet read, continue\n"));
+ }
+ }
+ }
+
+ON_EXIT:
+ //
+ // Return the data toggle set by EHCI hardware, bulk and interrupt
+ // transfer will use this to initialize the next transaction. For
+ // Control transfer, it always start a new data toggle sequence for
+ // new transfer.
+ //
+ // NOTICE: don't move DT update before the loop, otherwise there is
+ // a race condition that DT is wrong.
+ //
+ Urb->DataToggle = (UINT8) Urb->Qh->QhHw.DataToggle;
+
+ return Finished;
+}
+
+
+/**
+ Execute the transfer by polling the URB. This is a synchronous operation.
+
+ @param Ehc The EHCI device
+ @param Urb The URB to execute
+ @param TimeOut The time to wait before abort, in millisecond.
+
+ @return EFI_DEVICE_ERROR : The transfer failed due to transfer error
+ @return EFI_TIMEOUT : The transfer failed due to time out
+ @return EFI_SUCCESS : The transfer finished OK
+
+**/
+EFI_STATUS
+EhcExecTransfer (
+ IN USB2_HC_DEV *Ehc,
+ IN URB *Urb,
+ IN UINTN TimeOut
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN Loop;
+ BOOLEAN Finished;
+
+ Status = EFI_SUCCESS;
+ Loop = (TimeOut * EHC_STALL_1_MILLISECOND / EHC_SYNC_POLL_TIME) + 1;
+ Finished = FALSE;
+
+ for (Index = 0; Index < Loop; Index++) {
+ Finished = EhcCheckUrbResult (Ehc, Urb);
+
+ if (Finished) {
+ break;
+ }
+
+ gBS->Stall (EHC_SYNC_POLL_TIME);
+ }
+
+ if (!Finished) {
+ EHC_ERROR (("EhcExecTransfer: transfer not finished in %dms\n", TimeOut));
+ EHC_DUMP_QH ((Urb->Qh, NULL, FALSE));
+
+ Status = EFI_TIMEOUT;
+
+ } else if (Urb->Result != EFI_USB_NOERROR) {
+ EHC_ERROR (("EhcExecTransfer: transfer failed with %x\n", Urb->Result));
+ EHC_DUMP_QH ((Urb->Qh, NULL, FALSE));
+
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
+
+/**
+ Delete a single asynchronous interrupt transfer for
+ the device and endpoint
+
+ @param Ehc The EHCI device
+ @param DevAddr The address of the target device
+ @param EpNum The endpoint of the target
+ @param DataToggle Return the next data toggle to use
+
+ @retval EFI_SUCCESS An asynchronous transfer is removed
+ @retval EFI_NOT_FOUND No transfer for the device is found
+
+**/
+EFI_STATUS
+EhciDelAsyncIntTransfer (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT8 DevAddr,
+ IN UINT8 EpNum,
+ OUT UINT8 *DataToggle
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ URB *Urb;
+ EFI_USB_DATA_DIRECTION Direction;
+
+ Direction = ((EpNum & 0x80) ? EfiUsbDataIn : EfiUsbDataOut);
+ EpNum &= 0x0F;
+
+ EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
+ Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);
+
+ if ((Urb->Ep.DevAddr == DevAddr) && (Urb->Ep.EpAddr == EpNum) &&
+ (Urb->Ep.Direction == Direction)) {
+ //
+ // Check the URB status to retrieve the next data toggle
+ // from the associated queue head.
+ //
+ EhcCheckUrbResult (Ehc, Urb);
+ *DataToggle = Urb->DataToggle;
+
+ EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);
+ RemoveEntryList (&Urb->UrbList);
+
+ gBS->FreePool (Urb->Data);
+ EhcFreeUrb (Ehc, Urb);
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Remove all the asynchronous interrutp transfers
+
+ @param Ehc The EHCI device
+
+ @return None
+
+**/
+VOID
+EhciDelAllAsyncIntTransfers (
+ IN USB2_HC_DEV *Ehc
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ URB *Urb;
+
+ EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
+ Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);
+
+ EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);
+ RemoveEntryList (&Urb->UrbList);
+
+ gBS->FreePool (Urb->Data);
+ EhcFreeUrb (Ehc, Urb);
+ }
+}
+
+
+
+/**
+ Update the queue head for next round of asynchronous transfer
+
+ @param Urb The URB to update
+
+ @return None
+
+**/
+STATIC
+VOID
+EhcUpdateAsyncRequest (
+ IN URB *Urb
+ )
+{
+ LIST_ENTRY *Entry;
+ EHC_QTD *FirstQtd;
+ QH_HW *QhHw;
+ EHC_QTD *Qtd;
+ QTD_HW *QtdHw;
+ UINTN Index;
+
+ Qtd = NULL;
+
+ if (Urb->Result == EFI_USB_NOERROR) {
+ FirstQtd = NULL;
+
+ EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {
+ Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList);
+
+ if (FirstQtd == NULL) {
+ FirstQtd = Qtd;
+ }
+
+ //
+ // Update the QTD for next round of transfer. Host control
+ // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/
+ // Current Offset. These fields need to be updated. DT isn't
+ // used by interrupt transfer. It uses DT in queue head.
+ // Current Offset is in Page[0], only need to reset Page[0]
+ // to initial data buffer.
+ //
+ QtdHw = &Qtd->QtdHw;
+ QtdHw->Status = QTD_STAT_ACTIVE;
+ QtdHw->ErrCnt = QTD_MAX_ERR;
+ QtdHw->CurPage = 0;
+ QtdHw->TotalBytes = (UINT32) Qtd->DataLen;
+ QtdHw->Page[0] = EHC_LOW_32BIT (Qtd->Data);
+ }
+
+ //
+ // Update QH for next round of transfer. Host control only
+ // touch the fields in transfer overlay area. Only need to
+ // zero out the overlay area and set NextQtd to the first
+ // QTD. DateToggle bit is left untouched.
+ //
+ QhHw = &Urb->Qh->QhHw;
+ QhHw->CurQtd = QTD_LINK (0, TRUE);
+ QhHw->AltQtd = 0;
+
+ QhHw->Status = 0;
+ QhHw->Pid = 0;
+ QhHw->ErrCnt = 0;
+ QhHw->CurPage = 0;
+ QhHw->IOC = 0;
+ QhHw->TotalBytes = 0;
+
+ for (Index = 0; Index < 5; Index++) {
+ QhHw->Page[Index] = 0;
+ QhHw->PageHigh[Index] = 0;
+ }
+
+ QhHw->NextQtd = QTD_LINK (FirstQtd, FALSE);
+ }
+
+ return ;
+}
+
+
+/**
+ Interrupt transfer periodic check handler
+
+ @param Event Interrupt event
+ @param Context Pointer to USB2_HC_DEV
+
+ @return None
+
+**/
+VOID
+EhcMoniteAsyncRequests (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ USB2_HC_DEV *Ehc;
+ EFI_TPL OldTpl;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ BOOLEAN Finished;
+ UINT8 *ProcBuf;
+ URB *Urb;
+
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ Ehc = (USB2_HC_DEV *) Context;
+
+ EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
+ Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);
+
+ //
+ // Check the result of URB execution. If it is still
+ // active, check the next one.
+ //
+ Finished = EhcCheckUrbResult (Ehc, Urb);
+
+ if (!Finished) {
+ continue;
+ }
+
+ //
+ // Allocate a buffer then copy the transferred data for user.
+ // If failed to allocate the buffer, update the URB for next
+ // round of transfer. Ignore the data of this round.
+ //
+ ProcBuf = NULL;
+
+ if (Urb->Result == EFI_USB_NOERROR) {
+ ASSERT (Urb->Completed <= Urb->DataLen);
+
+ ProcBuf = AllocatePool (Urb->Completed);
+
+ if (ProcBuf == NULL) {
+ EhcUpdateAsyncRequest (Urb);
+ continue;
+ }
+
+ CopyMem (ProcBuf, Urb->Data, Urb->Completed);
+ }
+
+ EhcUpdateAsyncRequest (Urb);
+
+ //
+ // Leave error recovery to its related device driver. A
+ // common case of the error recovery is to re-submit the
+ // interrupt transfer which is linked to the head of the
+ // list. This function scans from head to tail. So the
+ // re-submitted interrupt transfer's callback function
+ // will not be called again in this round. Don't touch this
+ // URB after the callback, it may have been removed by the
+ // callback.
+ //
+ if (Urb->Callback != NULL) {
+ //
+ // Restore the old TPL, USB bus maybe connect device in
+ // his callback. Some drivers may has a lower TPL restriction.
+ //
+ gBS->RestoreTPL (OldTpl);
+ (Urb->Callback) (ProcBuf, Urb->Completed, Urb->Context, Urb->Result);
+ OldTpl = gBS->RaiseTPL (EHC_TPL);
+ }
+
+ if (ProcBuf != NULL) {
+ gBS->FreePool (ProcBuf);
+ }
+ }
+
+ gBS->RestoreTPL (OldTpl);
+}
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/EhciSched.h b/MdeModulePkg/Bus/Pci/EhciDxe/EhciSched.h new file mode 100644 index 0000000000..d8f2661059 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/EhciSched.h @@ -0,0 +1,220 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciSched.h
+
+Abstract:
+
+ This file contains the definination for host controller schedule routines
+
+Revision History
+
+**/
+
+#ifndef _EFI_EHCI_SCHED_H_
+#define _EFI_EHCI_SCHED_H_
+
+EFI_STATUS
+EhcInitSched (
+ IN USB2_HC_DEV *Ehc
+ )
+/*++
+
+Routine Description:
+
+ Initialize the schedule data structure such as frame list
+
+Arguments:
+
+ Ehc - The EHCI device to init schedule data for
+
+Returns:
+
+ EFI_OUT_OF_RESOURCES - Failed to allocate resource to init schedule data
+ EFI_SUCCESS - The schedule data is initialized
+
+--*/
+;
+
+
+
+/**
+ Free the schedule data. It may be partially initialized.
+
+ @param Ehc The EHCI device
+
+ @return None
+
+**/
+VOID
+EhcFreeSched (
+ IN USB2_HC_DEV *Ehc
+ )
+;
+
+
+
+/**
+ Link the queue head to the asynchronous schedule list.
+ UEFI only supports one CTRL/BULK transfer at a time
+ due to its interfaces. This simplifies the AsynList
+ management: A reclamation header is always linked to
+ the AsyncListAddr, the only active QH is appended to it.
+
+ @param Ehc The EHCI device
+ @param Qh The queue head to link
+
+ @return None
+
+**/
+VOID
+EhcLinkQhToAsync (
+ IN USB2_HC_DEV *Ehc,
+ IN EHC_QH *Qh
+ )
+;
+
+
+/**
+ Unlink a queue head from the asynchronous schedule list.
+ Need to synchronize with hardware
+
+ @param Ehc The EHCI device
+ @param Qh The queue head to unlink
+
+ @return None
+
+**/
+VOID
+EhcUnlinkQhFromAsync (
+ IN USB2_HC_DEV *Ehc,
+ IN EHC_QH *Qh
+ )
+;
+
+
+/**
+ Link a queue head for interrupt transfer to the periodic
+ schedule frame list. This code is very much the same as
+ that in UHCI.
+
+ @param Ehc The EHCI device
+ @param Qh The queue head to link
+
+ @return None
+
+**/
+VOID
+EhcLinkQhToPeriod (
+ IN USB2_HC_DEV *Ehc,
+ IN EHC_QH *Qh
+ )
+;
+
+
+/**
+ Unlink an interrupt queue head from the periodic
+ schedule frame list
+
+ @param Ehc The EHCI device
+ @param Qh The queue head to unlink
+
+ @return None
+
+**/
+VOID
+EhcUnlinkQhFromPeriod (
+ IN USB2_HC_DEV *Ehc,
+ IN EHC_QH *Qh
+ )
+;
+
+
+
+/**
+ Execute the transfer by polling the URB. This is a synchronous operation.
+
+ @param Ehc The EHCI device
+ @param Urb The URB to execute
+ @param TimeOut The time to wait before abort, in millisecond.
+
+ @return EFI_DEVICE_ERROR : The transfer failed due to transfer error
+ @return EFI_TIMEOUT : The transfer failed due to time out
+ @return EFI_SUCCESS : The transfer finished OK
+
+**/
+EFI_STATUS
+EhcExecTransfer (
+ IN USB2_HC_DEV *Ehc,
+ IN URB *Urb,
+ IN UINTN TimeOut
+ )
+;
+
+
+/**
+ Delete a single asynchronous interrupt transfer for
+ the device and endpoint
+
+ @param Ehc The EHCI device
+ @param DevAddr The address of the target device
+ @param EpNum The endpoint of the target
+ @param DataToggle Return the next data toggle to use
+
+ @retval EFI_SUCCESS An asynchronous transfer is removed
+ @retval EFI_NOT_FOUND No transfer for the device is found
+
+**/
+EFI_STATUS
+EhciDelAsyncIntTransfer (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT8 DevAddr,
+ IN UINT8 EpNum,
+ OUT UINT8 *DataToggle
+ )
+;
+
+
+/**
+ Remove all the asynchronous interrutp transfers
+
+ @param Ehc The EHCI device
+
+ @return None
+
+**/
+VOID
+EhciDelAllAsyncIntTransfers (
+ IN USB2_HC_DEV *Ehc
+ )
+;
+
+
+
+/**
+ Interrupt transfer periodic check handler
+
+ @param Event Interrupt event
+ @param Context Pointer to USB2_HC_DEV
+
+ @return None
+
+**/
+VOID
+EhcMoniteAsyncRequests (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+;
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.c b/MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.c new file mode 100644 index 0000000000..7b8b7b58f2 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.c @@ -0,0 +1,669 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciUrb.c
+
+Abstract:
+
+ This file contains URB request, each request is warpped in a
+ URB (Usb Request Block)
+
+Revision History
+
+**/
+
+#include "Ehci.h"
+
+
+/**
+ Create a single QTD to hold the data
+
+ @param Ehc The EHCI device
+ @param Data Current data not associated with a QTD
+ @param DataLen The length of the data
+ @param PktId Packet ID to use in the QTD
+ @param Toggle Data toggle to use in the QTD
+ @param MaxPacket Maximu packet length of the endpoint
+
+ @return Created QTD or NULL if failed to create one
+
+**/
+EHC_QTD *
+EhcCreateQtd (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT8 *Data,
+ IN UINTN DataLen,
+ IN UINT8 PktId,
+ IN UINT8 Toggle,
+ IN UINTN MaxPacket
+ )
+{
+ EHC_QTD *Qtd;
+ QTD_HW *QtdHw;
+ UINTN Index;
+ UINTN Len;
+ UINTN ThisBufLen;
+
+ ASSERT (Ehc != NULL);
+
+ Qtd = UsbHcAllocateMem (Ehc->MemPool, sizeof (EHC_QTD));
+
+ if (Qtd == NULL) {
+ return NULL;
+ }
+
+ Qtd->Signature = EHC_QTD_SIG;
+ Qtd->Data = Data;
+ Qtd->DataLen = 0;
+
+ InitializeListHead (&Qtd->QtdList);
+
+ QtdHw = &Qtd->QtdHw;
+ QtdHw->NextQtd = QTD_LINK (NULL, TRUE);
+ QtdHw->AltNext = QTD_LINK (NULL, TRUE);
+ QtdHw->Status = QTD_STAT_ACTIVE;
+ QtdHw->Pid = PktId;
+ QtdHw->ErrCnt = QTD_MAX_ERR;
+ QtdHw->IOC = 0;
+ QtdHw->TotalBytes = 0;
+ QtdHw->DataToggle = Toggle;
+
+ //
+ // Fill in the buffer points
+ //
+ if (Data != NULL) {
+ Len = 0;
+
+ for (Index = 0; Index <= QTD_MAX_BUFFER; Index++) {
+ //
+ // Set the buffer point (Check page 41 EHCI Spec 1.0). No need to
+ // compute the offset and clear Reserved fields. This is already
+ // done in the data point.
+ //
+ QtdHw->Page[Index] = EHC_LOW_32BIT (Data);
+ QtdHw->PageHigh[Index] = EHC_HIGH_32BIT (Data);
+
+ ThisBufLen = QTD_BUF_LEN - (EHC_LOW_32BIT (Data) & QTD_BUF_MASK);
+
+ if (Len + ThisBufLen >= DataLen) {
+ Len = DataLen;
+ break;
+ }
+
+ Len += ThisBufLen;
+ Data += ThisBufLen;
+ }
+
+ //
+ // Need to fix the last pointer if the Qtd can't hold all the
+ // user's data to make sure that the length is in the unit of
+ // max packets. If it can hold all the data, there is no such
+ // need.
+ //
+ if (Len < DataLen) {
+ Len = Len - Len % MaxPacket;
+ }
+
+ QtdHw->TotalBytes = (UINT32) Len;
+ Qtd->DataLen = Len;
+ }
+
+ return Qtd;
+}
+
+
+
+/**
+ Initialize the queue head for interrupt transfer,
+ that is, initialize the following three fields:
+ 1. SplitXState in the Status field
+ 2. Microframe S-mask
+ 3. Microframe C-mask
+
+ @param Ep The queue head's related endpoint
+ @param QhHw The queue head to initialize
+
+ @return None
+
+**/
+STATIC
+VOID
+EhcInitIntQh (
+ IN USB_ENDPOINT *Ep,
+ IN QH_HW *QhHw
+ )
+{
+ //
+ // Because UEFI interface can't utilitize an endpoint with
+ // poll rate faster than 1ms, only need to set one bit in
+ // the queue head. simple. But it may be changed later. If
+ // sub-1ms interrupt is supported, need to update the S-Mask
+ // here
+ //
+ if (Ep->DevSpeed == EFI_USB_SPEED_HIGH) {
+ QhHw->SMask = QH_MICROFRAME_0;
+ return ;
+ }
+
+ //
+ // For low/full speed device, the transfer must go through
+ // the split transaction. Need to update three fields
+ // 1. SplitXState in the status
+ // 2. Microframe S-Mask
+ // 3. Microframe C-Mask
+ // UEFI USB doesn't exercise admission control. It simplely
+ // schedule the high speed transactions in microframe 0, and
+ // full/low speed transactions at microframe 1. This also
+ // avoid the use of FSTN.
+ //
+ QhHw->SMask = QH_MICROFRAME_1;
+ QhHw->CMask = QH_MICROFRAME_3 | QH_MICROFRAME_4 | QH_MICROFRAME_5;
+}
+
+
+
+/**
+ Allocate and initialize a EHCI queue head
+
+ @param Ehci The EHCI device
+ @param Ep The endpoint to create queue head for
+
+ @return Created queue head or NULL if failed to create one
+
+**/
+EHC_QH *
+EhcCreateQh (
+ IN USB2_HC_DEV *Ehci,
+ IN USB_ENDPOINT *Ep
+ )
+{
+ EHC_QH *Qh;
+ QH_HW *QhHw;
+
+ Qh = UsbHcAllocateMem (Ehci->MemPool, sizeof (EHC_QH));
+
+ if (Qh == NULL) {
+ return NULL;
+ }
+
+ Qh->Signature = EHC_QH_SIG;
+ Qh->NextQh = NULL;
+ Qh->Interval = Ep->PollRate;
+
+ InitializeListHead (&Qh->Qtds);
+
+ QhHw = &Qh->QhHw;
+ QhHw->HorizonLink = QH_LINK (NULL, 0, TRUE);
+ QhHw->DeviceAddr = Ep->DevAddr;
+ QhHw->Inactive = 0;
+ QhHw->EpNum = Ep->EpAddr;
+ QhHw->EpSpeed = Ep->DevSpeed;
+ QhHw->DtCtrl = 0;
+ QhHw->ReclaimHead = 0;
+ QhHw->MaxPacketLen = (UINT32) Ep->MaxPacket;
+ QhHw->CtrlEp = 0;
+ QhHw->NakReload = QH_NAK_RELOAD;
+ QhHw->HubAddr = Ep->HubAddr;
+ QhHw->PortNum = Ep->HubPort;
+ QhHw->Multiplier = 1;
+ QhHw->DataToggle = Ep->Toggle;
+
+ if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) {
+ QhHw->Status |= QTD_STAT_DO_SS;
+ }
+
+ switch (Ep->Type) {
+ case EHC_CTRL_TRANSFER:
+ //
+ // Special initialization for the control transfer:
+ // 1. Control transfer initialize data toggle from each QTD
+ // 2. Set the Control Endpoint Flag (C) for low/full speed endpoint.
+ //
+ QhHw->DtCtrl = 1;
+
+ if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) {
+ QhHw->CtrlEp = 1;
+ }
+ break;
+
+ case EHC_INT_TRANSFER_ASYNC:
+ case EHC_INT_TRANSFER_SYNC:
+ //
+ // Special initialization for the interrupt transfer
+ // to set the S-Mask and C-Mask
+ //
+ QhHw->NakReload = 0;
+ EhcInitIntQh (Ep, QhHw);
+ break;
+
+ case EHC_BULK_TRANSFER:
+ if ((Ep->DevSpeed == EFI_USB_SPEED_HIGH) && (Ep->Direction == EfiUsbDataOut)) {
+ QhHw->Status |= QTD_STAT_DO_PING;
+ }
+
+ break;
+ }
+
+ return Qh;
+}
+
+
+
+/**
+ Convert the poll interval from application to that
+ be used by EHCI interface data structure. Only need
+ to get the max 2^n that is less than interval. UEFI
+ can't support high speed endpoint with a interval less
+ than 8 microframe because interval is specified in
+ the unit of ms (millisecond)
+
+ @param Interval The interval to convert
+
+ @return The converted interval
+
+**/
+STATIC
+UINTN
+EhcConvertPollRate (
+ IN UINTN Interval
+ )
+{
+ UINTN BitCount;
+
+ if (Interval == 0) {
+ return 1;
+ }
+
+ //
+ // Find the index (1 based) of the highest non-zero bit
+ //
+ BitCount = 0;
+
+ while (Interval != 0) {
+ Interval >>= 1;
+ BitCount++;
+ }
+
+ return 1 << (BitCount - 1);
+}
+
+
+
+/**
+ Free a list of QTDs
+
+ @param Ehc The EHCI device
+ @param Qtds The list head of the QTD
+
+ @return None
+
+**/
+STATIC
+VOID
+EhcFreeQtds (
+ IN USB2_HC_DEV *Ehc,
+ IN LIST_ENTRY *Qtds
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ EHC_QTD *Qtd;
+
+ EFI_LIST_FOR_EACH_SAFE (Entry, Next, Qtds) {
+ Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList);
+
+ RemoveEntryList (&Qtd->QtdList);
+ UsbHcFreeMem (Ehc->MemPool, Qtd, sizeof (EHC_QTD));
+ }
+}
+
+
+/**
+ Free an allocated URB. It is possible for it to be partially inited.
+
+ @param Ehc The EHCI device
+ @param Urb The URB to free
+
+ @return None
+
+**/
+VOID
+EhcFreeUrb (
+ IN USB2_HC_DEV *Ehc,
+ IN URB *Urb
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ PciIo = Ehc->PciIo;
+
+ if (Urb->RequestPhy != NULL) {
+ PciIo->Unmap (PciIo, Urb->RequestMap);
+ }
+
+ if (Urb->DataMap != NULL) {
+ PciIo->Unmap (PciIo, Urb->DataMap);
+ }
+
+ if (Urb->Qh != NULL) {
+ //
+ // Ensure that this queue head has been unlinked from the
+ // schedule data structures. Free all the associated QTDs
+ //
+ EhcFreeQtds (Ehc, &Urb->Qh->Qtds);
+ UsbHcFreeMem (Ehc->MemPool, Urb->Qh, sizeof (EHC_QH));
+ }
+
+ gBS->FreePool (Urb);
+}
+
+
+
+/**
+ Create a list of QTDs for the URB
+
+ @param Ehc The EHCI device
+ @param Urb The URB to create QTDs for
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for QTD
+ @retval EFI_SUCCESS The QTDs are allocated for the URB
+
+**/
+STATIC
+EFI_STATUS
+EhcCreateQtds (
+ IN USB2_HC_DEV *Ehc,
+ IN URB *Urb
+ )
+{
+ USB_ENDPOINT *Ep;
+ EHC_QH *Qh;
+ EHC_QTD *Qtd;
+ EHC_QTD *StatusQtd;
+ EHC_QTD *NextQtd;
+ LIST_ENTRY *Entry;
+ UINT32 AlterNext;
+ UINT8 Toggle;
+ UINTN Len;
+ UINT8 Pid;
+
+ ASSERT ((Urb != NULL) && (Urb->Qh != NULL));
+
+ //
+ // EHCI follows the alternet next QTD pointer if it meets
+ // a short read and the AlterNext pointer is valid. UEFI
+ // EHCI driver should terminate the transfer except the
+ // control transfer.
+ //
+ Toggle = 0;
+ Qh = Urb->Qh;
+ Ep = &Urb->Ep;
+ StatusQtd = NULL;
+ AlterNext = QTD_LINK (NULL, TRUE);
+
+ if (Ep->Direction == EfiUsbDataIn) {
+ AlterNext = QTD_LINK (Ehc->ShortReadStop, FALSE);
+ }
+
+ //
+ // Build the Setup and status packets for control transfer
+ //
+ if (Urb->Ep.Type == EHC_CTRL_TRANSFER) {
+ Len = sizeof (EFI_USB_DEVICE_REQUEST);
+ Qtd = EhcCreateQtd (Ehc, Urb->RequestPhy, Len, QTD_PID_SETUP, 0, Ep->MaxPacket);
+
+ if (Qtd == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InsertTailList (&Qh->Qtds, &Qtd->QtdList);
+
+ //
+ // Create the status packet now. Set the AlterNext to it. So, when
+ // EHCI meets a short control read, it can resume at the status stage.
+ // Use the opposite direction of the data stage, or IN if there is
+ // no data stage.
+ //
+ if (Ep->Direction == EfiUsbDataIn) {
+ Pid = QTD_PID_OUTPUT;
+ } else {
+ Pid = QTD_PID_INPUT;
+ }
+
+ StatusQtd = EhcCreateQtd (Ehc, NULL, 0, Pid, 1, Ep->MaxPacket);
+
+ if (StatusQtd == NULL) {
+ goto ON_ERROR;
+ }
+
+ if (Ep->Direction == EfiUsbDataIn) {
+ AlterNext = QTD_LINK (StatusQtd, FALSE);
+ }
+
+ Toggle = 1;
+ }
+
+ //
+ // Build the data packets for all the transfers
+ //
+ if (Ep->Direction == EfiUsbDataIn) {
+ Pid = QTD_PID_INPUT;
+ } else {
+ Pid = QTD_PID_OUTPUT;
+ }
+
+ Qtd = NULL;
+ Len = 0;
+
+ while (Len < Urb->DataLen) {
+ Qtd = EhcCreateQtd (
+ Ehc,
+ (UINT8 *) Urb->DataPhy + Len,
+ Urb->DataLen - Len,
+ Pid,
+ Toggle,
+ Ep->MaxPacket
+ );
+
+ if (Qtd == NULL) {
+ goto ON_ERROR;
+ }
+
+ Qtd->QtdHw.AltNext = AlterNext;
+ InsertTailList (&Qh->Qtds, &Qtd->QtdList);
+
+ //
+ // Switch the Toggle bit if odd number of packets are included in the QTD.
+ //
+ if (((Qtd->DataLen + Ep->MaxPacket - 1) / Ep->MaxPacket) % 2) {
+ Toggle = 1 - Toggle;
+ }
+
+ Len += Qtd->DataLen;
+ }
+
+ //
+ // Insert the status packet for control transfer
+ //
+ if (Ep->Type == EHC_CTRL_TRANSFER) {
+ InsertTailList (&Qh->Qtds, &StatusQtd->QtdList);
+ }
+
+ //
+ // OK, all the QTDs needed are created. Now, fix the NextQtd point
+ //
+ EFI_LIST_FOR_EACH (Entry, &Qh->Qtds) {
+ Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList);
+
+ //
+ // break if it is the last entry on the list
+ //
+ if (Entry->ForwardLink == &Qh->Qtds) {
+ break;
+ }
+
+ NextQtd = EFI_LIST_CONTAINER (Entry->ForwardLink, EHC_QTD, QtdList);
+ Qtd->QtdHw.NextQtd = QTD_LINK (NextQtd, FALSE);
+ }
+
+ //
+ // Link the QTDs to the queue head
+ //
+ NextQtd = EFI_LIST_CONTAINER (Qh->Qtds.ForwardLink, EHC_QTD, QtdList);
+ Qh->QhHw.NextQtd = QTD_LINK (NextQtd, FALSE);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ EhcFreeQtds (Ehc, &Qh->Qtds);
+ return EFI_OUT_OF_RESOURCES;
+}
+
+
+/**
+ Create a new URB and its associated QTD
+
+ @param Ehc The EHCI device
+ @param DevAddr The device address
+ @param EpAddr Endpoint addrress & its direction
+ @param DevSpeed The device speed
+ @param Toggle Initial data toggle to use
+ @param MaxPacket The max packet length of the endpoint
+ @param Hub The transaction translator to use
+ @param Type The transaction type
+ @param Request The standard USB request for control transfer
+ @param Data The user data to transfer
+ @param DataLen The length of data buffer
+ @param Callback The function to call when data is transferred
+ @param Context The context to the callback
+ @param Interval The interval for interrupt transfer
+
+ @return Created URB or NULL
+
+**/
+URB *
+EhcCreateUrb (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINT8 Toggle,
+ IN UINTN MaxPacket,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub,
+ IN UINTN Type,
+ IN EFI_USB_DEVICE_REQUEST *Request,
+ IN VOID *Data,
+ IN UINTN DataLen,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
+ IN VOID *Context,
+ IN UINTN Interval
+ )
+{
+ USB_ENDPOINT *Ep;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ EFI_PCI_IO_PROTOCOL_OPERATION MapOp;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+ UINTN Len;
+ URB *Urb;
+ VOID *Map;
+
+ Urb = AllocateZeroPool (sizeof (URB));
+
+ if (Urb == NULL) {
+ return NULL;
+ }
+
+ Urb->Signature = EHC_URB_SIG;
+ InitializeListHead (&Urb->UrbList);
+
+ Ep = &Urb->Ep;
+ Ep->DevAddr = DevAddr;
+ Ep->EpAddr = EpAddr & 0x0F;
+ Ep->Direction = ((EpAddr & 0x80) ? EfiUsbDataIn : EfiUsbDataOut);
+ Ep->DevSpeed = DevSpeed;
+ Ep->MaxPacket = MaxPacket;
+
+ Ep->HubAddr = 0;
+ Ep->HubPort = 0;
+
+ if (DevSpeed != EFI_USB_SPEED_HIGH) {
+ ASSERT (Hub != NULL);
+
+ Ep->HubAddr = Hub->TranslatorHubAddress;
+ Ep->HubPort = Hub->TranslatorPortNumber;
+ }
+
+ Ep->Toggle = Toggle;
+ Ep->Type = Type;
+ Ep->PollRate = EhcConvertPollRate (Interval);
+
+ Urb->Request = Request;
+ Urb->Data = Data;
+ Urb->DataLen = DataLen;
+ Urb->Callback = Callback;
+ Urb->Context = Context;
+
+ PciIo = Ehc->PciIo;
+ Urb->Qh = EhcCreateQh (Ehc, &Urb->Ep);
+
+ if (Urb->Qh == NULL) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Map the request and user data
+ //
+ if (Request != NULL) {
+ Len = sizeof (EFI_USB_DEVICE_REQUEST);
+ MapOp = EfiPciIoOperationBusMasterRead;
+ Status = PciIo->Map (PciIo, MapOp, Request, &Len, &PhyAddr, &Map);
+
+ if (EFI_ERROR (Status) || (Len != sizeof (EFI_USB_DEVICE_REQUEST))) {
+ goto ON_ERROR;
+ }
+
+ Urb->RequestPhy = (VOID *) ((UINTN) PhyAddr);
+ Urb->RequestMap = Map;
+ }
+
+ if (Data != NULL) {
+ Len = DataLen;
+
+ if (Ep->Direction == EfiUsbDataIn) {
+ MapOp = EfiPciIoOperationBusMasterWrite;
+ } else {
+ MapOp = EfiPciIoOperationBusMasterRead;
+ }
+
+ Status = PciIo->Map (PciIo, MapOp, Data, &Len, &PhyAddr, &Map);
+
+ if (EFI_ERROR (Status) || (Len != DataLen)) {
+ goto ON_ERROR;
+ }
+
+ Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);
+ Urb->DataMap = Map;
+ }
+
+ Status = EhcCreateQtds (Ehc, Urb);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return Urb;
+
+ON_ERROR:
+ EhcFreeUrb (Ehc, Urb);
+ return NULL;
+}
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.h b/MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.h new file mode 100644 index 0000000000..a2a5826de8 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.h @@ -0,0 +1,350 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciUrb.h
+
+Abstract:
+
+ This file contains URB request, each request is warpped in a
+ URB (Usb Request Block)
+
+Revision History
+
+**/
+
+#ifndef _EFI_EHCI_URB_H_
+#define _EFI_EHCI_URB_H_
+
+
+typedef struct _EHC_QTD EHC_QTD;
+typedef struct _EHC_QH EHC_QH;
+typedef struct _URB URB;
+
+enum {
+ //
+ // Transfer types, used in URB to identify the transfer type
+ //
+ EHC_CTRL_TRANSFER = 0x01,
+ EHC_BULK_TRANSFER = 0x02,
+ EHC_INT_TRANSFER_SYNC = 0x04,
+ EHC_INT_TRANSFER_ASYNC = 0x08,
+
+ EHC_QTD_SIG = EFI_SIGNATURE_32 ('U', 'S', 'B', 'T'),
+ EHC_QH_SIG = EFI_SIGNATURE_32 ('U', 'S', 'B', 'H'),
+ EHC_URB_SIG = EFI_SIGNATURE_32 ('U', 'S', 'B', 'R'),
+
+ //
+ // Hardware related bit definitions
+ //
+ EHC_TYPE_ITD = 0x00,
+ EHC_TYPE_QH = 0x02,
+ EHC_TYPE_SITD = 0x04,
+ EHC_TYPE_FSTN = 0x06,
+
+ QH_NAK_RELOAD = 3,
+ QH_HSHBW_MULTI = 1,
+
+ QTD_MAX_ERR = 3,
+ QTD_PID_OUTPUT = 0x00,
+ QTD_PID_INPUT = 0x01,
+ QTD_PID_SETUP = 0x02,
+
+ QTD_STAT_DO_OUT = 0,
+ QTD_STAT_DO_SS = 0,
+ QTD_STAT_DO_PING = 0x01,
+ QTD_STAT_DO_CS = 0x02,
+ QTD_STAT_TRANS_ERR = 0x08,
+ QTD_STAT_BABBLE_ERR = 0x10,
+ QTD_STAT_BUFF_ERR = 0x20,
+ QTD_STAT_HALTED = 0x40,
+ QTD_STAT_ACTIVE = 0x80,
+ QTD_STAT_ERR_MASK = QTD_STAT_TRANS_ERR | QTD_STAT_BABBLE_ERR | QTD_STAT_BUFF_ERR,
+
+ QTD_MAX_BUFFER = 4,
+ QTD_BUF_LEN = 4096,
+ QTD_BUF_MASK = 0x0FFF,
+
+ QH_MICROFRAME_0 = 0x01,
+ QH_MICROFRAME_1 = 0x02,
+ QH_MICROFRAME_2 = 0x04,
+ QH_MICROFRAME_3 = 0x08,
+ QH_MICROFRAME_4 = 0x10,
+ QH_MICROFRAME_5 = 0x20,
+ QH_MICROFRAME_6 = 0x40,
+ QH_MICROFRAME_7 = 0x80,
+
+ USB_ERR_SHORT_PACKET = 0x200,
+};
+
+//
+// Fill in the hardware link point: pass in a EHC_QH/QH_HW
+// pointer to QH_LINK; A EHC_QTD/QTD_HW pointer to QTD_LINK
+//
+#define QH_LINK(Addr, Type, Term) \
+ ((UINT32) ((EHC_LOW_32BIT (Addr) & 0xFFFFFFE0) | (Type) | ((Term) ? 1 : 0)))
+
+#define QTD_LINK(Addr, Term) QH_LINK((Addr), 0, (Term))
+
+//
+// The defination of EHCI hardware used data structure for
+// little endian architecture. The QTD and QH structures
+// are required to be 32 bytes aligned. Don't add members
+// to the head of the associated software strucuture.
+//
+#pragma pack(1)
+typedef struct {
+ UINT32 NextQtd;
+ UINT32 AltNext;
+
+ UINT32 Status : 8;
+ UINT32 Pid : 2;
+ UINT32 ErrCnt : 2;
+ UINT32 CurPage : 3;
+ UINT32 IOC : 1;
+ UINT32 TotalBytes : 15;
+ UINT32 DataToggle : 1;
+
+ UINT32 Page[5];
+ UINT32 PageHigh[5];
+} QTD_HW;
+
+typedef struct {
+ UINT32 HorizonLink;
+ //
+ // Endpoint capabilities/Characteristics DWord 1 and DWord 2
+ //
+ UINT32 DeviceAddr : 7;
+ UINT32 Inactive : 1;
+ UINT32 EpNum : 4;
+ UINT32 EpSpeed : 2;
+ UINT32 DtCtrl : 1;
+ UINT32 ReclaimHead : 1;
+ UINT32 MaxPacketLen : 11;
+ UINT32 CtrlEp : 1;
+ UINT32 NakReload : 4;
+
+ UINT32 SMask : 8;
+ UINT32 CMask : 8;
+ UINT32 HubAddr : 7;
+ UINT32 PortNum : 7;
+ UINT32 Multiplier : 2;
+
+ //
+ // Transaction execution overlay area
+ //
+ UINT32 CurQtd;
+ UINT32 NextQtd;
+ UINT32 AltQtd;
+
+ UINT32 Status : 8;
+ UINT32 Pid : 2;
+ UINT32 ErrCnt : 2;
+ UINT32 CurPage : 3;
+ UINT32 IOC : 1;
+ UINT32 TotalBytes : 15;
+ UINT32 DataToggle : 1;
+
+ UINT32 Page[5];
+ UINT32 PageHigh[5];
+} QH_HW;
+#pragma pack()
+
+
+//
+// Endpoint address and its capabilities
+//
+typedef struct _USB_ENDPOINT {
+ UINT8 DevAddr;
+ UINT8 EpAddr; // Endpoint address, no direction encoded in
+ EFI_USB_DATA_DIRECTION Direction;
+ UINT8 DevSpeed;
+ UINTN MaxPacket;
+ UINT8 HubAddr;
+ UINT8 HubPort;
+ UINT8 Toggle; // Data toggle, not used for control transfer
+ UINTN Type;
+ UINTN PollRate; // Polling interval used by EHCI
+} USB_ENDPOINT;
+
+//
+// Software QTD strcture, this is used to manage all the
+// QTD generated from a URB. Don't add fields before QtdHw.
+//
+typedef struct _EHC_QTD {
+ QTD_HW QtdHw;
+ UINT32 Signature;
+ LIST_ENTRY QtdList; // The list of QTDs to one end point
+ UINT8 *Data; // Buffer of the original data
+ UINTN DataLen; // Original amount of data in this QTD
+} EHC_QTD;
+
+//
+// Software QH structure. All three different transaction types
+// supported by UEFI USB, that is the control/bulk/interrupt
+// transfers use the queue head and queue token strcuture.
+//
+// Interrupt QHs are linked to periodic frame list in the reversed
+// 2^N tree. Each interrupt QH is linked to the list starting at
+// frame 0. There is a dummy interrupt QH linked to each frame as
+// a sentinental whose polling interval is 1. Synchronous interrupt
+// transfer is linked after this dummy QH.
+//
+// For control/bulk transfer, only synchronous (in the sense of UEFI)
+// transfer is supported. A dummy QH is linked to EHCI AsyncListAddr
+// as the reclamation header. New transfer is inserted after this QH.
+//
+typedef struct _EHC_QH {
+ QH_HW QhHw;
+ UINT32 Signature;
+ EHC_QH *NextQh; // The queue head pointed to by horizontal link
+ LIST_ENTRY Qtds; // The list of QTDs to this queue head
+ UINTN Interval;
+} EHC_QH;
+
+//
+// URB (Usb Request Block) contains information for all kinds of
+// usb requests.
+//
+typedef struct _URB {
+ UINT32 Signature;
+ LIST_ENTRY UrbList;
+
+ //
+ // Transaction information
+ //
+ USB_ENDPOINT Ep;
+ EFI_USB_DEVICE_REQUEST *Request; // Control transfer only
+ VOID *RequestPhy; // Address of the mapped request
+ VOID *RequestMap;
+ VOID *Data;
+ UINTN DataLen;
+ VOID *DataPhy; // Address of the mapped user data
+ VOID *DataMap;
+ EFI_ASYNC_USB_TRANSFER_CALLBACK Callback;
+ VOID *Context;
+
+ //
+ // Schedule data
+ //
+ EHC_QH *Qh;
+
+ //
+ // Transaction result
+ //
+ UINT32 Result;
+ UINTN Completed; // completed data length
+ UINT8 DataToggle;
+} URB;
+
+
+
+/**
+ Create a single QTD to hold the data
+
+ @param Ehc The EHCI device
+ @param Data Current data not associated with a QTD
+ @param DataLen The length of the data
+ @param PktId Packet ID to use in the QTD
+ @param Toggle Data toggle to use in the QTD
+ @param MaxPacket Maximu packet length of the endpoint
+
+ @return Created QTD or NULL if failed to create one
+
+**/
+EHC_QTD *
+EhcCreateQtd (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT8 *Data,
+ IN UINTN DataLen,
+ IN UINT8 PktId,
+ IN UINT8 Toggle,
+ IN UINTN MaxPacket
+ )
+;
+
+
+
+/**
+ Allocate and initialize a EHCI queue head
+
+ @param Ehci The EHCI device
+ @param Ep The endpoint to create queue head for
+
+ @return Created queue head or NULL if failed to create one
+
+**/
+EHC_QH *
+EhcCreateQh (
+ IN USB2_HC_DEV *Ehci,
+ IN USB_ENDPOINT *Ep
+ )
+;
+
+
+/**
+ Free an allocated URB. It is possible for it to be partially inited.
+
+ @param Ehc The EHCI device
+ @param Urb The URB to free
+
+ @return None
+
+**/
+VOID
+EhcFreeUrb (
+ IN USB2_HC_DEV *Ehc,
+ IN URB *Urb
+ )
+;
+
+
+/**
+ Create a new URB and its associated QTD
+
+ @param Ehc The EHCI device
+ @param DevAddr The device address
+ @param EpAddr Endpoint addrress & its direction
+ @param DevSpeed The device speed
+ @param Toggle Initial data toggle to use
+ @param MaxPacket The max packet length of the endpoint
+ @param Hub The transaction translator to use
+ @param Type The transaction type
+ @param Request The standard USB request for control transfer
+ @param Data The user data to transfer
+ @param DataLen The length of data buffer
+ @param Callback The function to call when data is transferred
+ @param Context The context to the callback
+ @param Interval The interval for interrupt transfer
+
+ @return Created URB or NULL
+
+**/
+URB *
+EhcCreateUrb (
+ IN USB2_HC_DEV *Ehc,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINT8 Toggle,
+ IN UINTN MaxPacket,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub,
+ IN UINTN Type,
+ IN EFI_USB_DEVICE_REQUEST *Request,
+ IN VOID *Data,
+ IN UINTN DataLen,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
+ IN VOID *Context,
+ IN UINTN Interval
+ )
+;
+#endif
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/UsbHcMem.c b/MdeModulePkg/Bus/Pci/EhciDxe/UsbHcMem.c new file mode 100644 index 0000000000..b7e7d66025 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/UsbHcMem.c @@ -0,0 +1,549 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciMem.c
+
+Abstract:
+
+
+Revision History
+
+**/
+
+
+#include "Ehci.h"
+
+
+UINTN mUsbHcDebugLevel = DEBUG_INFO;
+
+
+/**
+ Allocate a block of memory to be used by the buffer pool
+
+ @param Pool The buffer pool to allocate memory for
+ @param Pages How many pages to allocate
+
+ @return The allocated memory block or NULL if failed
+
+**/
+STATIC
+USBHC_MEM_BLOCK *
+UsbHcAllocMemBlock (
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Pages
+ )
+{
+ USBHC_MEM_BLOCK *Block;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ VOID *BufHost;
+ VOID *Mapping;
+ EFI_PHYSICAL_ADDRESS MappedAddr;
+ UINTN Bytes;
+ EFI_STATUS Status;
+
+ PciIo = Pool->PciIo;
+
+ Block = AllocateZeroPool (sizeof (USBHC_MEM_BLOCK));
+ if (Block == NULL) {
+ return NULL;
+ }
+
+ //
+ // each bit in the bit array represents USBHC_MEM_UNIT
+ // bytes of memory in the memory block.
+ //
+ ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE);
+
+ Block->BufLen = EFI_PAGES_TO_SIZE (Pages);
+ Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8);
+ Block->Bits = AllocateZeroPool (Block->BitsLen);
+
+ if (Block->Bits == NULL) {
+ gBS->FreePool (Block);
+ return NULL;
+ }
+
+ //
+ // Allocate the number of Pages of memory, then map it for
+ // bus master read and write.
+ //
+ Status = PciIo->AllocateBuffer (
+ PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ Pages,
+ &BufHost,
+ 0
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_BITARRAY;
+ }
+
+ Bytes = EFI_PAGES_TO_SIZE (Pages);
+ Status = PciIo->Map (
+ PciIo,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ BufHost,
+ &Bytes,
+ &MappedAddr,
+ &Mapping
+ );
+
+ if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (Pages))) {
+ goto FREE_BUFFER;
+ }
+
+ //
+ // Check whether the data structure used by the host controller
+ // should be restricted into the same 4G
+ //
+ if (Pool->Check4G && (Pool->Which4G != USB_HC_HIGH_32BIT (MappedAddr))) {
+ PciIo->Unmap (PciIo, Mapping);
+ goto FREE_BUFFER;
+ }
+
+ Block->BufHost = BufHost;
+ Block->Buf = (UINT8 *) ((UINTN) MappedAddr);
+ Block->Mapping = Mapping;
+
+ DEBUG ((mUsbHcDebugLevel, "UsbHcAllocMemBlock: block %x created with buffer %x\n",
+ Block, Block->Buf));
+
+ return Block;
+
+FREE_BUFFER:
+ PciIo->FreeBuffer (PciIo, Pages, BufHost);
+
+FREE_BITARRAY:
+ gBS->FreePool (Block->Bits);
+ gBS->FreePool (Block);
+ return NULL;
+}
+
+
+/**
+ Free the memory block from the memory pool
+
+ @param Pool The memory pool to free the block from
+ @param Block The memory block to free
+
+ @return VOID
+
+**/
+STATIC
+VOID
+UsbHcFreeMemBlock (
+ IN USBHC_MEM_POOL *Pool,
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ ASSERT ((Pool != NULL) && (Block != NULL));
+
+ PciIo = Pool->PciIo;
+
+ //
+ // Unmap the common buffer then free the structures
+ //
+ PciIo->Unmap (PciIo, Block->Mapping);
+ PciIo->FreeBuffer (PciIo, EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost);
+
+ gBS->FreePool (Block->Bits);
+ gBS->FreePool (Block);
+}
+
+
+/**
+ Alloc some memory from the block
+
+ @param Block The memory block to allocate memory from
+ @param Mem The variable to store the memory allocated
+ @param Units Number of memory units to allocate
+
+ @return EFI_SUCCESS : The needed memory is allocated
+ @return EFI_NOT_FOUND : Can't find the free memory
+
+**/
+STATIC
+VOID *
+UsbHcAllocMemFromBlock (
+ IN USBHC_MEM_BLOCK *Block,
+ IN UINTN Units
+ )
+{
+ UINTN Byte;
+ UINT8 Bit;
+ UINTN StartByte;
+ UINT8 StartBit;
+ UINTN Available;
+ UINTN Count;
+
+ ASSERT ((Block != 0) && (Units != 0));
+
+ StartByte = 0;
+ StartBit = 0;
+ Available = 0;
+
+ for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {
+ //
+ // If current bit is zero, the corresponding memory unit is
+ // available, otherwise we need to restart our searching.
+ // Available counts the consective number of zero bit.
+ //
+ if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) {
+ Available++;
+
+ if (Available >= Units) {
+ break;
+ }
+
+ NEXT_BIT (Byte, Bit);
+
+ } else {
+ NEXT_BIT (Byte, Bit);
+
+ Available = 0;
+ StartByte = Byte;
+ StartBit = Bit;
+ }
+ }
+
+ if (Available < Units) {
+ return NULL;
+ }
+
+ //
+ // Mark the memory as allocated
+ //
+ Byte = StartByte;
+ Bit = StartBit;
+
+ for (Count = 0; Count < Units; Count++) {
+ ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
+
+ Block->Bits[Byte] |= USB_HC_BIT (Bit);
+ NEXT_BIT (Byte, Bit);
+ }
+
+ return Block->Buf + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT;
+}
+
+
+/**
+ Insert the memory block to the pool's list of the blocks
+
+ @param Head The head of the memory pool's block list
+ @param Block The memory block to insert
+
+ @return VOID
+
+**/
+STATIC
+VOID
+UsbHcInsertMemBlockToPool (
+ IN USBHC_MEM_BLOCK *Head,
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ ASSERT ((Head != NULL) && (Block != NULL));
+ Block->Next = Head->Next;
+ Head->Next = Block;
+}
+
+
+/**
+ Is the memory block empty?
+
+ @param Block The memory block to check
+
+ @return TRUE : The memory block is empty
+ @return FALSE : The memory block isn't empty
+
+**/
+STATIC
+BOOLEAN
+UsbHcIsMemBlockEmpty (
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < Block->BitsLen; Index++) {
+ if (Block->Bits[Index] != 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Unlink the memory block from the pool's list
+
+ @param Head The block list head of the memory's pool
+ @param BlockToUnlink The memory block to unlink.
+
+ @return VOID
+
+**/
+STATIC
+VOID
+UsbHcUnlinkMemBlock (
+ IN USBHC_MEM_BLOCK *Head,
+ IN USBHC_MEM_BLOCK *BlockToUnlink
+ )
+{
+ USBHC_MEM_BLOCK *Block;
+
+ ASSERT ((Head != NULL) && (BlockToUnlink != NULL));
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ if (Block->Next == BlockToUnlink) {
+ Block->Next = BlockToUnlink->Next;
+ BlockToUnlink->Next = NULL;
+ break;
+ }
+ }
+}
+
+
+/**
+ Initialize the memory management pool for the host controller
+
+ @param Pool The USB memory pool to initialize
+ @param PciIo The PciIo that can be used to access the host controller
+ @param Check4G Whether the host controller requires allocated memory
+ from one 4G address space.
+ @param Which4G The 4G memory area each memory allocated should be from
+
+ @return EFI_SUCCESS : The memory pool is initialized
+ @return EFI_OUT_OF_RESOURCE : Fail to init the memory pool
+
+**/
+USBHC_MEM_POOL *
+UsbHcInitMemPool (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN BOOLEAN Check4G,
+ IN UINT32 Which4G
+ )
+{
+ USBHC_MEM_POOL *Pool;
+
+ Pool = AllocatePool (sizeof (USBHC_MEM_POOL));
+
+ if (Pool == NULL) {
+ return Pool;
+ }
+
+ Pool->PciIo = PciIo;
+ Pool->Check4G = Check4G;
+ Pool->Which4G = Which4G;
+ Pool->Head = UsbHcAllocMemBlock (Pool, USBHC_MEM_DEFAULT_PAGES);
+
+ if (Pool->Head == NULL) {
+ gBS->FreePool (Pool);
+ Pool = NULL;
+ }
+
+ return Pool;
+}
+
+
+/**
+ Release the memory management pool
+
+ @param Pool The USB memory pool to free
+
+ @return EFI_SUCCESS : The memory pool is freed
+ @return EFI_DEVICE_ERROR : Failed to free the memory pool
+
+**/
+EFI_STATUS
+UsbHcFreeMemPool (
+ IN USBHC_MEM_POOL *Pool
+ )
+{
+ USBHC_MEM_BLOCK *Block;
+
+ ASSERT (Pool->Head != NULL);
+
+ //
+ // Unlink all the memory blocks from the pool, then free them.
+ // UsbHcUnlinkMemBlock can't be used to unlink and free the
+ // first block.
+ //
+ for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) {
+ UsbHcUnlinkMemBlock (Pool->Head, Block);
+ UsbHcFreeMemBlock (Pool, Block);
+ }
+
+ UsbHcFreeMemBlock (Pool, Pool->Head);
+ gBS->FreePool (Pool);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Allocate some memory from the host controller's memory pool
+ which can be used to communicate with host controller.
+
+ @param Pool The host controller's memory pool
+ @param Size Size of the memory to allocate
+
+ @return The allocated memory or NULL
+
+**/
+VOID *
+UsbHcAllocateMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Size
+ )
+{
+ USBHC_MEM_BLOCK *Head;
+ USBHC_MEM_BLOCK *Block;
+ USBHC_MEM_BLOCK *NewBlock;
+ VOID *Mem;
+ UINTN AllocSize;
+ UINTN Pages;
+
+ Mem = NULL;
+ AllocSize = USBHC_MEM_ROUND (Size);
+ Head = Pool->Head;
+ ASSERT (Head != NULL);
+
+ //
+ // First check whether current memory blocks can satisfy the allocation.
+ //
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT);
+
+ if (Mem != NULL) {
+ ZeroMem (Mem, Size);
+ break;
+ }
+ }
+
+ if (Mem != NULL) {
+ return Mem;
+ }
+
+ //
+ // Create a new memory block if there is not enough memory
+ // in the pool. If the allocation size is larger than the
+ // default page number, just allocate a large enough memory
+ // block. Otherwise allocate default pages.
+ //
+ if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) {
+ Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;
+ } else {
+ Pages = USBHC_MEM_DEFAULT_PAGES;
+ }
+
+ NewBlock = UsbHcAllocMemBlock (Pool, Pages);
+
+ if (NewBlock == NULL) {
+ DEBUG ((mUsbHcDebugLevel, "UsbHcAllocateMem: failed to allocate block\n"));
+ return NULL;
+ }
+
+ //
+ // Add the new memory block to the pool, then allocate memory from it
+ //
+ UsbHcInsertMemBlockToPool (Head, NewBlock);
+ Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT);
+
+ if (Mem != NULL) {
+ ZeroMem (Mem, Size);
+ }
+
+ return Mem;
+}
+
+
+/**
+ Free the allocated memory back to the memory pool
+
+ @param Pool The memory pool of the host controller
+ @param Mem The memory to free
+ @param Size The size of the memory to free
+
+ @return VOID
+
+**/
+VOID
+UsbHcFreeMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ )
+{
+ USBHC_MEM_BLOCK *Head;
+ USBHC_MEM_BLOCK *Block;
+ UINT8 *ToFree;
+ UINTN AllocSize;
+ UINTN Byte;
+ UINTN Bit;
+ UINTN Count;
+
+ Head = Pool->Head;
+ AllocSize = USBHC_MEM_ROUND (Size);
+ ToFree = (UINT8 *) Mem;
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ //
+ // scan the memory block list for the memory block that
+ // completely contains the memory to free.
+ //
+ if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) {
+ //
+ // compute the start byte and bit in the bit array
+ //
+ Byte = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) / 8;
+ Bit = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) % 8;
+
+ //
+ // reset associated bits in bit arry
+ //
+ for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) {
+ ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
+
+ Block->Bits[Byte] ^= (UINT8) USB_HC_BIT (Bit);
+ NEXT_BIT (Byte, Bit);
+ }
+
+ break;
+ }
+ }
+
+ //
+ // If Block == NULL, it means that the current memory isn't
+ // in the host controller's pool. This is critical because
+ // the caller has passed in a wrong memory point
+ //
+ ASSERT (Block != NULL);
+
+ //
+ // Release the current memory block if it is empty and not the head
+ //
+ if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) {
+ DEBUG ((mUsbHcDebugLevel, "UsbHcFreeMem: block %x is empty, recycle\n", Block));
+
+ UsbHcUnlinkMemBlock (Head, Block);
+ UsbHcFreeMemBlock (Pool, Block);
+ }
+
+ return ;
+}
diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/UsbHcMem.h b/MdeModulePkg/Bus/Pci/EhciDxe/UsbHcMem.h new file mode 100644 index 0000000000..10d3545feb --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/UsbHcMem.h @@ -0,0 +1,168 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciMem.h
+
+Abstract:
+
+ This file contains the definination for host controller memory management routines
+
+Revision History
+
+**/
+
+#ifndef _EFI_EHCI_MEM_H_
+#define _EFI_EHCI_MEM_H_
+
+
+#include <IndustryStandard/Pci22.h>
+
+#define USB_HC_BIT(a) ((UINTN)(1 << (a)))
+
+#define USB_HC_BIT_IS_SET(Data, Bit) \
+ ((BOOLEAN)(((Data) & USB_HC_BIT(Bit)) == USB_HC_BIT(Bit)))
+
+#define USB_HC_HIGH_32BIT(Addr64) \
+ ((UINT32)(RShiftU64((UINTN)(Addr64), 32) & 0XFFFFFFFF))
+
+typedef struct _USBHC_MEM_BLOCK USBHC_MEM_BLOCK;
+
+typedef struct _USBHC_MEM_BLOCK {
+ UINT8 *Bits; // Bit array to record which unit is allocated
+ UINTN BitsLen;
+ UINT8 *Buf;
+ UINT8 *BufHost;
+ UINTN BufLen; // Memory size in bytes
+ VOID *Mapping;
+ USBHC_MEM_BLOCK *Next;
+} USBHC_MEM_BLOCK;
+
+//
+// USBHC_MEM_POOL is used to manage the memory used by USB
+// host controller. EHCI requires the control memory and transfer
+// data to be on the same 4G memory.
+//
+typedef struct _USBHC_MEM_POOL {
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ BOOLEAN Check4G;
+ UINT32 Which4G;
+ USBHC_MEM_BLOCK *Head;
+} USBHC_MEM_POOL;
+
+enum {
+ USBHC_MEM_UNIT = 64, // Memory allocation unit, must be 2^n, n>4
+
+ USBHC_MEM_UNIT_MASK = USBHC_MEM_UNIT - 1,
+ USBHC_MEM_DEFAULT_PAGES = 16,
+};
+
+#define USBHC_MEM_ROUND(Len) (((Len) + USBHC_MEM_UNIT_MASK) & (~USBHC_MEM_UNIT_MASK))
+
+//
+// Advance the byte and bit to the next bit, adjust byte accordingly.
+//
+#define NEXT_BIT(Byte, Bit) \
+ do { \
+ (Bit)++; \
+ if ((Bit) > 7) { \
+ (Byte)++; \
+ (Bit) = 0; \
+ } \
+ } while (0)
+
+
+
+USBHC_MEM_POOL *
+UsbHcInitMemPool (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN BOOLEAN Check4G,
+ IN UINT32 Which4G
+ )
+/*++
+
+Routine Description:
+
+ Initialize the memory management pool for the host controller
+
+Arguments:
+
+ Pool - The USB memory pool to initialize
+ PciIo - The PciIo that can be used to access the host controller
+ Check4G - Whether the host controller requires allocated memory
+ from one 4G address space.
+ Which4G - The 4G memory area each memory allocated should be from
+
+Returns:
+
+ EFI_SUCCESS : The memory pool is initialized
+ EFI_OUT_OF_RESOURCE : Fail to init the memory pool
+
+--*/
+;
+
+
+
+/**
+ Release the memory management pool
+
+ @param Pool The USB memory pool to free
+
+ @return EFI_SUCCESS : The memory pool is freed
+ @return EFI_DEVICE_ERROR : Failed to free the memory pool
+
+**/
+EFI_STATUS
+UsbHcFreeMemPool (
+ IN USBHC_MEM_POOL *Pool
+ )
+;
+
+
+
+/**
+ Allocate some memory from the host controller's memory pool
+ which can be used to communicate with host controller.
+
+ @param Pool The host controller's memory pool
+ @param Size Size of the memory to allocate
+
+ @return The allocated memory or NULL
+
+**/
+VOID *
+UsbHcAllocateMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Size
+ )
+;
+
+
+
+/**
+ Free the allocated memory back to the memory pool
+
+ @param Pool The memory pool of the host controller
+ @param Mem The memory to free
+ @param Size The size of the memory to free
+
+ @return VOID
+
+**/
+VOID
+UsbHcFreeMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ )
+;
+#endif
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.c b/MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.c new file mode 100644 index 0000000000..d1cb1eb121 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.c @@ -0,0 +1,203 @@ +/** @file
+
+Copyright (c) 2004 - 2007, 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:
+
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+#include "uhci.h"
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+UhciComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+UhciComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle, OPTIONAL
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gUhciComponentName = {
+ UhciComponentNameGetDriverName,
+ UhciComponentNameGetControllerName,
+ "eng"
+};
+
+static EFI_UNICODE_STRING_TABLE mUhciDriverNameTable[] = {
+ { "eng", L"Usb Uhci Driver" },
+ { NULL, NULL }
+};
+
+EFI_STATUS
+EFIAPI
+UhciComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gUhciComponentName.SupportedLanguages,
+ mUhciDriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+UhciComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle, OPTIONAL
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language
+ specified by Language from the point of view of the
+ driver specified by This.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ EFI_STATUS Status;
+ USB_HC_DEV *UhciDev;
+ EFI_USB_HC_PROTOCOL *UsbHc;
+
+ //
+ // This is a device driver, so ChildHandle must be NULL.
+ //
+ if (ChildHandle != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver is currently managing ControllerHandle
+ //
+ Status = EfiTestManagedDevice (
+ ControllerHandle,
+ gUhciDriverBinding.DriverBindingHandle,
+ &gEfiPciIoProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get the device context
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiUsbHcProtocolGuid,
+ (VOID **) &UsbHc,
+ gUhciDriverBinding.DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ UhciDev = UHC_FROM_USB_HC_PROTO (UsbHc);
+
+ return LookupUnicodeString (
+ Language,
+ gUhciComponentName.SupportedLanguages,
+ UhciDev->CtrlNameTable,
+ ControllerName
+ );
+
+}
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c b/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c new file mode 100644 index 0000000000..1c3e37a7bc --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c @@ -0,0 +1,2331 @@ +/** @file
+
+Copyright (c) 2004 - 2007, 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:
+
+ Uhci.c
+
+Abstract:
+
+ The UHCI driver model and HC protocol routines.
+
+Revision History
+
+
+**/
+
+#include "Uhci.h"
+
+
+/**
+ Provides software reset for the USB host controller.
+
+ This : A pointer to the EFI_USB_HC_PROTOCOL instance.
+ Attributes: A bit mask of the reset operation to perform.
+
+ @return EFI_SUCCESS : The reset operation succeeded.
+ @return EFI_INVALID_PARAMETER : Attributes is not valid.
+ @return EFI_DEVICE_ERROR : An error was encountered while attempting
+ @return to perform the reset operation.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciReset (
+ IN EFI_USB_HC_PROTOCOL *This,
+ IN UINT16 Attributes
+ )
+{
+ USB_HC_DEV *Uhc;
+ EFI_TPL OldTpl;
+
+ OldTpl = gBS->RaiseTPL (UHCI_TPL);
+ Uhc = UHC_FROM_USB_HC_PROTO (This);
+
+ switch (Attributes) {
+ case EFI_USB_HC_RESET_GLOBAL:
+ //
+ // Stop schedule and set the Global Reset bit in the command register
+ //
+ UhciStopHc (Uhc, STALL_1_SECOND);
+ UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET);
+
+ //
+ // Wait 50ms for root port to let reset complete
+ // See UHCI spec page122 Reset signaling
+ //
+ gBS->Stall (ROOT_PORT_REST_TIME);
+
+ //
+ // Clear the Global Reset bit to zero.
+ //
+ UhciClearRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET);
+
+ //
+ // UHCI spec page120 reset recovery time
+ //
+ gBS->Stall (PORT_RESET_RECOVERY_TIME);
+ break;
+
+ case EFI_USB_HC_RESET_HOST_CONTROLLER:
+ //
+ // Stop schedule and set Host Controller Reset bit to 1
+ //
+ UhciStopHc (Uhc, STALL_1_SECOND);
+ UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_HCRESET);
+
+ //
+ // this bit will be reset by Host Controller when reset is completed.
+ // wait 10ms to let reset complete
+ //
+ gBS->Stall (PORT_RESET_RECOVERY_TIME);
+ 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.
+
+ This : A pointer to the EFI_USB_HC_PROTOCOL instance.
+ State : A pointer to the EFI_USB_HC_STATE data structure that
+ indicates current state of the USB host controller.
+
+ @return EFI_SUCCESS : State was returned
+ @return EFI_INVALID_PARAMETER : State is NULL.
+ @return EFI_DEVICE_ERROR : An error was encountered
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciGetState (
+ IN EFI_USB_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_USB_HC_PROTO (This);
+
+ UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET);
+ UsbSts = UhciReadReg (Uhc->PciIo, USBSTS_OFFSET);
+
+ if (UsbCmd & USBCMD_EGSM) {
+ *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.
+
+ This : A pointer to the EFI_USB_HC_PROTOCOL instance.
+ State : Indicates the state of the host controller that will be set.
+
+ @return EFI_SUCCESS : The USB host controller was successfully set
+ @return EFI_INVALID_PARAMETER : State is invalid.
+ @return EFI_DEVICE_ERROR : Failed to set the state specified
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciSetState (
+ IN EFI_USB_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_USB_HC_PROTO (This);
+ Status = UhciGetState (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, STALL_1_SECOND);
+ 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 (FORCE_GLOBAL_RESUME_TIME);
+
+ //
+ // 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 = UhciSetState (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 the number of root hub ports.
+
+ This : A pointer to the EFI_USB_HC_PROTOCOL instance.
+ PortNumber : A pointer to the number of the root hub ports.
+
+ @return EFI_SUCCESS : The port number was retrieved successfully.
+ @return EFI_INVALID_PARAMETER : PortNumber is NULL.
+ @return EFI_DEVICE_ERROR : An error was encountered
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciGetRootHubPortNumber (
+ IN EFI_USB_HC_PROTOCOL *This,
+ OUT UINT8 *PortNumber
+ )
+{
+ USB_HC_DEV *Uhc;
+ UINT32 Offset;
+ UINT16 PortSC;
+ UINT32 Index;
+
+ Uhc = UHC_FROM_USB_HC_PROTO (This);
+
+ if (PortNumber == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *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)) {
+ (*PortNumber)++;
+ }
+ }
+
+ Uhc->RootPorts = *PortNumber;
+
+ UHCI_DEBUG (("UhciGetRootHubPortNumber: %d ports\n", Uhc->RootPorts));
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Retrieves the current status of a USB root hub port.
+
+ This : A pointer to the EFI_USB_HC_PROTOCOL.
+ PortNumber : Specifies the root hub port. This value is zero-based.
+ PortStatus : A pointer to the current port status bits and port status change bits.
+
+ @return EFI_SUCCESS : The port status was returned in PortStatus.
+ @return EFI_INVALID_PARAMETER : PortNumber is invalid.
+ @return EFI_DEVICE_ERROR : Can't read register
+
+**/
+EFI_STATUS
+EFIAPI
+UhciGetRootHubPortStatus (
+ IN EFI_USB_HC_PROTOCOL *This,
+ IN UINT8 PortNumber,
+ OUT EFI_USB_PORT_STATUS *PortStatus
+ )
+{
+ USB_HC_DEV *Uhc;
+ UINT32 Offset;
+ UINT16 PortSC;
+
+ Uhc = UHC_FROM_USB_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) {
+ PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION;
+ }
+
+ if (PortSC & USBPORTSC_PED) {
+ PortStatus->PortStatus |= USB_PORT_STAT_ENABLE;
+ }
+
+ if (PortSC & USBPORTSC_SUSP) {
+ UHCI_DEBUG (("UhciGetRootHubPortStatus: port %d is suspended\n", PortNumber));
+ PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND;
+ }
+
+ if (PortSC & USBPORTSC_PR) {
+ PortStatus->PortStatus |= USB_PORT_STAT_RESET;
+ }
+
+ if (PortSC & USBPORTSC_LSDA) {
+ 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) {
+ PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION;
+ }
+
+ if (PortSC & USBPORTSC_PEDC) {
+ PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Sets a feature for the specified root hub port.
+
+ This : A pointer to the EFI_USB_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.
+
+ @return EFI_SUCCESS : The feature was set for the port.
+ @return EFI_INVALID_PARAMETER : PortNumber is invalid or PortFeature is invalid.
+ @return EFI_DEVICE_ERROR : Can't read register
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciSetRootHubPortFeature (
+ IN EFI_USB_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_USB_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)) {
+ //
+ // 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.
+
+ This : A pointer to the EFI_USB_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.
+
+ @return EFI_SUCCESS : The feature was cleared for the port.
+ @return EFI_INVALID_PARAMETER : PortNumber is invalid or PortFeature is invalid.
+ @return EFI_DEVICE_ERROR : Can't read register
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciClearRootHubPortFeature (
+ IN EFI_USB_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_USB_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.
+
+ This : A pointer to the EFI_USB_HC_PROTOCOL instance.
+ DeviceAddress : Usb device address
+ IsSlowDevice : Whether the device is of slow speed or full speed
+ MaximumPacketLength : maximum packet size of the default control endpoint
+ Request : USB device request to send
+ TransferDirection : Specifies the data direction for the transfer.
+ Data : Data buffer to transmit from or receive into
+ DataLength : Number of bytes of the data
+ TimeOut : Maximum time, in microseconds
+ TransferResult : Return result in this
+
+ @return EFI_SUCCESS : Transfer was completed successfully.
+ @return EFI_OUT_OF_RESOURCES : Failed due to a lack of resources.
+ @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.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciControlTransfer (
+ IN EFI_USB_HC_PROTOCOL *This,
+ IN UINT8 DeviceAddress,
+ IN BOOLEAN IsSlowDevice,
+ IN UINT8 MaximumPacketLength,
+ IN EFI_USB_DEVICE_REQUEST *Request,
+ IN EFI_USB_DATA_DIRECTION TransferDirection,
+ IN OUT VOID *Data, OPTIONAL
+ IN OUT UINTN *DataLength, OPTIONAL
+ IN UINTN TimeOut,
+ OUT UINT32 *TransferResult
+ )
+{
+ USB_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;
+
+ Uhc = UHC_FROM_USB_HC_PROTO (This);
+ TDs = NULL;
+ DataPhy = NULL;
+ DataMap = NULL;
+ RequestPhy = NULL;
+ RequestMap = NULL;
+
+ //
+ // 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) && (DataLength == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *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,
+ *DataLength,
+ 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.
+
+ This :A pointer to the EFI_USB_HC_PROTOCOL instance.
+ DeviceAddress : Usb device address
+ EndPointAddress : Endpoint number and endpoint direction
+ MaximumPacketLength : Maximum packet size of the target endpoint
+ Data : Data buffer to transmit from or receive into
+ DataLength : Length of the data buffer
+ DataToggle : On input, data toggle to use, on output, the next toggle
+ TimeOut : Indicates the maximum time
+ TransferResult : Variable to receive the 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.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciBulkTransfer (
+ IN EFI_USB_HC_PROTOCOL *This,
+ IN UINT8 DeviceAddress,
+ IN UINT8 EndPointAddress,
+ IN UINT8 MaximumPacketLength,
+ IN OUT VOID *Data,
+ IN OUT UINTN *DataLength,
+ IN OUT UINT8 *DataToggle,
+ IN UINTN TimeOut,
+ 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_USB_HC_PROTO (This);
+ DataPhy = NULL;
+ DataMap = NULL;
+
+ if ((DataLength == NULL) || (Data == NULL) || (TransferResult == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*DataLength == 0) {
+ 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) {
+ 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,
+ 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.
+
+ This : A pointer to the EFI_USB_HC_PROTOCOL instance.
+ DeviceAddress : Target device address
+ EndPointAddress : Endpoint number with direction
+ IsSlowDevice : Whether the target device is slow device or full-speed device.
+ MaximumPacketLength : Maximum packet size of the target endpoint
+ IsNewTransfer : If TRUE, submit a new async interrupt transfer, otherwise
+ cancel an existed one
+ DataToggle : On input, the data toggle to use; On output, next data toggle
+ PollingInterval : Interrupt poll rate in milliseconds
+ DataLength : Length of data to receive
+ CallBackFunction : Function to call periodically
+ Context : User context
+
+ @return EFI_SUCCESS : Request is submitted or cancelled
+ @return EFI_INVALID_PARAMETER : Some parameters are invalid.
+ @return EFI_OUT_OF_RESOURCES : Failed due to a lack of resources.
+ @return EFI_DEVICE_ERROR : Failed to due to device error
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciAsyncInterruptTransfer (
+ IN EFI_USB_HC_PROTOCOL * This,
+ IN UINT8 DeviceAddress,
+ IN UINT8 EndPointAddress,
+ IN BOOLEAN IsSlowDevice,
+ IN UINT8 MaximumPacketLength,
+ IN BOOLEAN IsNewTransfer,
+ IN OUT UINT8 *DataToggle,
+ IN UINTN PollingInterval, OPTIONAL
+ IN UINTN DataLength, OPTIONAL
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, OPTIONAL
+ IN VOID *Context OPTIONAL
+ )
+{
+ USB_HC_DEV *Uhc;
+ 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_USB_HC_PROTO (This);
+ Qh = NULL;
+ IntTds = NULL;
+ DataPtr = NULL;
+ DataPhy = NULL;
+ DataMap = NULL;
+
+ 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,
+ 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.
+
+ This : A pointer to the EFI_USB_HC_PROTOCOL instance.
+ DeviceAddress : Device address of the target USB device
+ EndPointAddress : Endpoint number and direction
+ IsSlowDevice : Whether the target device is of slow speed or full speed
+ MaximumPacketLength : Maximum packet size of target endpoint
+ Data : Data to transmit or receive
+ DataLength : On input, data length to transmit or buffer size.
+ On output, the number of bytes transferred.
+ DataToggle : On input, data toggle to use; On output, next data toggle
+ TimeOut : Maximum time, in microseconds, transfer is allowed to complete.
+ TransferResult : Variable to receive transfer result
+
+ @return EFI_SUCCESS : 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
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciSyncInterruptTransfer (
+ IN EFI_USB_HC_PROTOCOL *This,
+ IN UINT8 DeviceAddress,
+ IN UINT8 EndPointAddress,
+ IN BOOLEAN IsSlowDevice,
+ IN UINT8 MaximumPacketLength,
+ IN OUT VOID *Data,
+ IN OUT UINTN *DataLength,
+ IN OUT UINT8 *DataToggle,
+ IN UINTN TimeOut,
+ 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;
+
+ Uhc = UHC_FROM_USB_HC_PROTO (This);
+ DataPhy = NULL;
+ DataMap = NULL;
+ TDs = NULL;
+
+ 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,
+ 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.
+
+ This : A pointer to the EFI_USB_HC_PROTOCOL instance.
+ DeviceAddress : Target device address
+ EndPointAddress : End point address withdirection
+ MaximumPacketLength : Maximum packet size of the endpoint
+ Data : Data to transmit or receive
+ DataLength : Bytes of the data
+ TransferResult : Variable to receive the result
+
+ @return EFI_UNSUPPORTED
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciIsochronousTransfer (
+ IN EFI_USB_HC_PROTOCOL *This,
+ IN UINT8 DeviceAddress,
+ IN UINT8 EndPointAddress,
+ IN UINT8 MaximumPacketLength,
+ IN OUT VOID *Data,
+ IN UINTN DataLength,
+ OUT UINT32 *TransferResult
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Submits Async isochronous transfer to a target USB device.
+
+ This : A pointer to the EFI_USB_HC_PROTOCOL instance.
+ DeviceAddress : Target device address
+ EndPointAddress : End point address withdirection
+ MaximumPacketLength : Maximum packet size of the endpoint
+ Data : Data to transmit or receive
+ IsochronousCallBack : Function to call when the transfer completes
+ Context : User context
+
+ @return EFI_UNSUPPORTED
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UhciAsyncIsochronousTransfer (
+ IN EFI_USB_HC_PROTOCOL * This,
+ IN UINT8 DeviceAddress,
+ IN UINT8 EndPointAddress,
+ IN UINT8 MaximumPacketLength,
+ IN OUT VOID *Data,
+ IN UINTN DataLength,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack,
+ IN VOID *Context OPTIONAL
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+
+/**
+ 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
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Uhci2Reset (
+ IN EFI_USB2_HC_PROTOCOL *This,
+ IN UINT16 Attributes
+ )
+{
+ USB_HC_DEV *UhciDev;
+
+ UhciDev = UHC_FROM_USB2_HC_PROTO (This);
+
+ if ((Attributes == EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG) ||
+ (Attributes == EFI_USB_HC_RESET_HOST_WITH_DEBUG)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return UhciReset (&UhciDev->UsbHc, Attributes);
+}
+
+
+/**
+ Retrieves current state of the USB host controller according to UEFI 2.0 spec.
+
+ @param This A pointer to the EFI_USB_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 errors2006
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Uhci2GetState (
+ IN CONST EFI_USB2_HC_PROTOCOL *This,
+ OUT EFI_USB_HC_STATE *State
+ )
+{
+ USB_HC_DEV *Uhc;
+
+ Uhc = UHC_FROM_USB2_HC_PROTO (This);
+ return UhciGetState (&Uhc->UsbHc, State);
+}
+
+
+/**
+ Sets the USB host controller to a specific state according to UEFI 2.0 spec.
+
+ @param This A pointer to the EFI_USB_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
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Uhci2SetState (
+ IN EFI_USB2_HC_PROTOCOL *This,
+ IN EFI_USB_HC_STATE State
+ )
+{
+ USB_HC_DEV *Uhc;
+
+ Uhc = UHC_FROM_USB2_HC_PROTO (This);
+ return UhciSetState (&Uhc->UsbHc, State);
+}
+
+
+/**
+ 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
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Uhci2GetCapability (
+ IN EFI_USB2_HC_PROTOCOL *This,
+ OUT UINT8 *MaxSpeed,
+ OUT UINT8 *PortNumber,
+ OUT UINT8 *Is64BitCapable
+ )
+{
+ USB_HC_DEV *Uhc;
+
+ 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;
+
+ return UhciGetRootHubPortNumber (&Uhc->UsbHc, PortNumber);
+}
+
+
+/**
+ 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
+
+**/
+STATIC
+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;
+
+ Uhc = UHC_FROM_USB2_HC_PROTO (This);
+
+ return UhciGetRootHubPortStatus (&Uhc->UsbHc, PortNumber, PortStatus);
+}
+
+
+/**
+ 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
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Uhci2SetRootHubPortFeature (
+ IN EFI_USB2_HC_PROTOCOL *This,
+ IN UINT8 PortNumber,
+ IN EFI_USB_PORT_FEATURE PortFeature
+ )
+{
+ USB_HC_DEV *Uhc;
+
+ Uhc = UHC_FROM_USB2_HC_PROTO (This);
+
+ return UhciSetRootHubPortFeature (&Uhc->UsbHc, PortNumber, PortFeature);
+}
+
+
+/**
+ 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
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Uhci2ClearRootHubPortFeature (
+ IN EFI_USB2_HC_PROTOCOL *This,
+ IN UINT8 PortNumber,
+ IN EFI_USB_PORT_FEATURE PortFeature
+ )
+{
+ USB_HC_DEV *Uhc;
+
+ Uhc = UHC_FROM_USB2_HC_PROTO (This);
+
+ return UhciClearRootHubPortFeature (&Uhc->UsbHc, PortNumber, PortFeature);
+}
+
+
+/**
+ Submits control transfer to a target USB device accroding to UEFI 2.0 spec..
+
+ This : A pointer to the EFI_USB2_HC_PROTOCOL instance.
+ DeviceAddress : Target device address
+ DeviceSpeed : Device speed
+ MaximumPacketLength : Maximum packet size of the target endpoint
+ Request : USB device request to send
+ TransferDirection : Data direction of the Data stage in control transfer
+ Data : Data to transmit/receive in data stage
+ DataLength : Length of the data
+ TimeOut : Maximum time, in microseconds, for transfer to complete.
+ 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.
+
+**/
+STATIC
+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;
+ BOOLEAN IsSlow;
+
+ Uhc = UHC_FROM_USB2_HC_PROTO (This);
+ IsSlow = (EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE;
+
+ return UhciControlTransfer (
+ &Uhc->UsbHc,
+ DeviceAddress,
+ IsSlow,
+ (UINT8) MaximumPacketLength,
+ Request,
+ TransferDirection,
+ Data,
+ DataLength,
+ TimeOut,
+ TransferResult
+ );
+}
+
+
+/**
+ Submits bulk transfer to a bulk endpoint of a USB device
+
+ This : A pointer to the EFI_USB2_HC_PROTOCOL instance.
+ DeviceAddress : Target device address
+ EndPointAddress : Endpoint number and direction
+ DeviceSpeed : Device speed
+ MaximumPacketLength : Maximum packet size of the target endpoint
+ DataBuffersNumber : Number of data buffers prepared for the transfer.
+ Data : Array of pointers to the buffers of data
+ DataLength : On input, size of the data buffer, On output,
+ actually transferred data size.
+ DataToggle : On input, data toggle to use; On output, next data toggle
+ Translator : A pointr to the transaction translator data.
+ TimeOut : Maximum time out, in microseconds
+ 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.
+
+**/
+STATIC
+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
+ )
+{
+ USB_HC_DEV *Uhc;
+
+ Uhc = UHC_FROM_USB2_HC_PROTO (This);
+
+ if (Data == NULL || DeviceSpeed == EFI_USB_SPEED_LOW) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // For full-speed bulk transfers only the data pointed by Data[0] shall be used
+ //
+ return UhciBulkTransfer (
+ &Uhc->UsbHc,
+ DeviceAddress,
+ EndPointAddress,
+ (UINT8) MaximumPacketLength,
+ *Data,
+ DataLength,
+ DataToggle,
+ TimeOut,
+ TransferResult
+ );
+}
+
+
+/**
+ Submits an asynchronous interrupt transfer to an
+ interrupt endpoint of a USB device according to UEFI 2.0 spec.
+
+ This : A pointer to the EFI_USB2_HC_PROTOCOL instance.
+ DeviceAddress : Target device address
+ EndPointAddress : Endpoint number and direction
+ DeviceSpeed : Device speed
+ MaximumPacketLength : Maximum packet size of the target endpoint
+ IsNewTransfer : If TRUE, submit a new transfer, if FALSE cancel old transfer
+ DataToggle : On input, data toggle to use; On output, next data toggle
+ PollingInterval : Interrupt poll rate in milliseconds
+ DataLength : On input, size of the data buffer, On output,
+ actually transferred data size.
+ Translator : A pointr to the transaction translator data.
+ CallBackFunction : Function to call periodically
+ 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
+
+**/
+STATIC
+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 IsSlow;
+
+ Uhc = UHC_FROM_USB2_HC_PROTO (This);
+ IsSlow = (EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE;
+
+ return UhciAsyncInterruptTransfer (
+ &Uhc->UsbHc,
+ DeviceAddress,
+ EndPointAddress,
+ IsSlow,
+ (UINT8) MaximumPacketLength,
+ IsNewTransfer,
+ DataToggle,
+ PollingInterval,
+ DataLength,
+ CallBackFunction,
+ Context
+ );
+}
+
+
+/**
+ Submits synchronous interrupt transfer to an interrupt endpoint
+ of a USB device according to UEFI 2.0 spec.
+
+ This : A pointer to the EFI_USB2_HC_PROTOCOL instance.
+ DeviceAddress : Target device address
+ EndPointAddress : Endpoint number and direction
+ DeviceSpeed : Device speed
+ MaximumPacketLength : Maximum packet size of the target endpoint
+ DataBuffersNumber : Number of data buffers prepared for the transfer.
+ Data : Array of pointers to the buffers of data
+ DataLength : On input, size of the data buffer, On output,
+ actually transferred data size.
+ DataToggle : On input, data toggle to use; On output, next data toggle
+ TimeOut : Maximum time out, in microseconds
+ Translator : A pointr to the transaction translator data.
+ 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.
+
+**/
+STATIC
+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
+ )
+{
+ USB_HC_DEV *Uhc;
+ BOOLEAN IsSlow;
+
+ if (DeviceSpeed == EFI_USB_SPEED_HIGH) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Uhc = UHC_FROM_USB2_HC_PROTO (This);
+ IsSlow = (EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE;
+
+ return UhciSyncInterruptTransfer (
+ &Uhc->UsbHc,
+ DeviceAddress,
+ EndPointAddress,
+ IsSlow,
+ (UINT8) MaximumPacketLength,
+ Data,
+ DataLength,
+ DataToggle,
+ TimeOut,
+ TransferResult
+ );
+}
+
+
+/**
+ Submits isochronous transfer to a target USB device according to UEFI 2.0 spec.
+
+ This : A pointer to the EFI_USB2_HC_PROTOCOL instance.
+ DeviceAddress : Target device address
+ EndPointAddress : Endpoint number and direction
+ DeviceSpeed : Device speed
+ MaximumPacketLength : Maximum packet size of the target endpoint
+ DataBuffersNumber : Number of data buffers prepared for the transfer.
+ Data : Array of pointers to the buffers of data
+ DataLength : On input, size of the data buffer, On output,
+ actually transferred data size.
+ Translator : A pointr to the transaction translator data.
+ TransferResult : Variable to receive transfer result
+
+ @return EFI_UNSUPPORTED
+
+**/
+STATIC
+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.
+
+ This : A pointer to the EFI_USB2_HC_PROTOCOL instance.
+ DeviceAddress : Target device address
+ EndPointAddress : Endpoint number and direction
+ DeviceSpeed : Device speed
+ MaximumPacketLength : Maximum packet size of the target endpoint
+ DataBuffersNumber : Number of data buffers prepared for the transfer.
+ Data : Array of pointers to the buffers of data
+ Translator : A pointr to the transaction translator data.
+ IsochronousCallBack : Function to call when the transfer complete
+ Context : Pass to the call back function as parameter
+
+ @return EFI_UNSUPPORTED
+
+**/
+STATIC
+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;
+}
+
+//@MT: EFI_DRIVER_ENTRY_POINT (UhciDriverEntryPoint)
+
+EFI_STATUS
+EFIAPI
+UhciDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+ Routine Description:
+
+ Entry point for EFI drivers.
+
+ Arguments:
+
+ ImageHandle - EFI_HANDLE
+ SystemTable - EFI_SYSTEM_TABLE
+
+ Returns:
+
+ EFI_SUCCESS : Driver is successfully loaded
+ Others : Failed
+
+--*/
+{
+ return EfiLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &gUhciDriverBinding,
+ ImageHandle,
+ &gUhciComponentName,
+ NULL,
+ NULL
+ );
+}
+
+
+/**
+ 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,
+ &PciIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (OpenStatus)) {
+ return OpenStatus;
+ }
+
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciIoWidthUint8,
+ CLASSC_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_CLASSC_PI_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
+
+ @return Allocated UHCI device
+
+**/
+STATIC
+USB_HC_DEV *
+UhciAllocateDev (
+ IN EFI_PCI_IO_PROTOCOL *PciIo
+ )
+{
+ 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->UsbHc.Reset = UhciReset;
+ Uhc->UsbHc.GetState = UhciGetState;
+ Uhc->UsbHc.SetState = UhciSetState;
+ Uhc->UsbHc.ControlTransfer = UhciControlTransfer;
+ Uhc->UsbHc.BulkTransfer = UhciBulkTransfer;
+ Uhc->UsbHc.AsyncInterruptTransfer = UhciAsyncInterruptTransfer;
+ Uhc->UsbHc.SyncInterruptTransfer = UhciSyncInterruptTransfer;
+ Uhc->UsbHc.IsochronousTransfer = UhciIsochronousTransfer;
+ Uhc->UsbHc.AsyncIsochronousTransfer = UhciAsyncIsochronousTransfer;
+ Uhc->UsbHc.GetRootHubPortNumber = UhciGetRootHubPortNumber;
+ Uhc->UsbHc.GetRootHubPortStatus = UhciGetRootHubPortStatus;
+ Uhc->UsbHc.SetRootHubPortFeature = UhciSetRootHubPortFeature;
+ Uhc->UsbHc.ClearRootHubPortFeature = UhciClearRootHubPortFeature;
+ Uhc->UsbHc.MajorRevision = 0x1;
+ Uhc->UsbHc.MinorRevision = 0x1;
+
+ 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->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:
+ gBS->FreePool (Uhc);
+ return NULL;
+}
+
+
+/**
+ Free the UHCI device and release its associated resources
+
+ @param Uhc The UHCI device to release
+
+ @return None
+
+**/
+STATIC
+VOID
+UhciFreeDev (
+ IN USB_HC_DEV *Uhc
+ )
+{
+ if (Uhc->AsyncIntMonitor != NULL) {
+ gBS->CloseEvent (Uhc->AsyncIntMonitor);
+ }
+
+ if (Uhc->MemPool != NULL) {
+ UsbHcFreeMemPool (Uhc->MemPool);
+ }
+
+ if (Uhc->CtrlNameTable) {
+ FreeUnicodeStringTable (Uhc->CtrlNameTable);
+ }
+
+ gBS->FreePool (Uhc);
+}
+
+
+/**
+ Uninstall all Uhci Interface
+
+ @param Controller Controller handle
+ @param This Protocol instance pointer.
+
+ @return VOID
+
+**/
+STATIC
+VOID
+UhciCleanDevUp (
+ IN EFI_HANDLE Controller,
+ IN EFI_USB_HC_PROTOCOL *This
+ )
+{
+ USB_HC_DEV *Uhc;
+
+ //
+ // Uninstall the USB_HC and USB_HC2 protocol, then disable the controller
+ //
+ Uhc = UHC_FROM_USB_HC_PROTO (This);
+ UhciStopHc (Uhc, STALL_1_SECOND);
+
+ gBS->UninstallProtocolInterface (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ &Uhc->UsbHc
+ );
+
+ gBS->UninstallProtocolInterface (
+ Controller,
+ &gEfiUsb2HcProtocolGuid,
+ &Uhc->Usb2Hc
+ );
+
+ UhciFreeAllAsyncReq (Uhc);
+ UhciDestoryFrameList (Uhc);
+
+ Uhc->PciIo->Attributes (
+ Uhc->PciIo,
+ EfiPciIoAttributeOperationDisable,
+ EFI_PCI_DEVICE_ENABLE,
+ NULL
+ );
+
+ UhciFreeDev (Uhc);
+}
+
+
+/**
+ 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;
+
+ //
+ // Open PCIIO, then enable the EHC device and turn off emulation
+ //
+ Uhc = NULL;
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ &PciIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ UhciTurnOffUsbEmulation (PciIo);
+
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationEnable,
+ EFI_PCI_DEVICE_ENABLE,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto CLOSE_PCIIO;
+ }
+
+ Uhc = UhciAllocateDev (PciIo);
+
+ 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,
+ INTERRUPT_POLLING_TIME
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_UHC;
+ }
+
+ //
+ // Install both USB_HC_PROTOCOL and USB2_HC_PROTOCOL
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiUsbHcProtocolGuid,
+ &Uhc->UsbHc,
+ &gEfiUsb2HcProtocolGuid,
+ &Uhc->Usb2Hc,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_UHC;
+ }
+
+ //
+ // Install the component name protocol
+ //
+ Uhc->CtrlNameTable = NULL;
+
+ AddUnicodeString (
+ "eng",
+ gUhciComponentName.SupportedLanguages,
+ &Uhc->CtrlNameTable,
+ L"Usb Universal Host Controller"
+ );
+
+ //
+ // Start the UHCI hardware, also set its reclamation point to 64 bytes
+ //
+ UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, USBCMD_RS | USBCMD_MAXP);
+
+ return EFI_SUCCESS;
+
+FREE_UHC:
+ UhciFreeDev (Uhc);
+
+CLOSE_PCIIO:
+ 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_USB_HC_PROTOCOL *UsbHc;
+ EFI_USB2_HC_PROTOCOL *Usb2Hc;
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ &UsbHc,
+ 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;
+ }
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsb2HcProtocolGuid,
+ &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, UsbHc);
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ return EFI_SUCCESS;
+}
+
+EFI_DRIVER_BINDING_PROTOCOL gUhciDriverBinding = {
+ UhciDriverBindingSupported,
+ UhciDriverBindingStart,
+ UhciDriverBindingStop,
+ 0x20,
+ NULL,
+ NULL
+};
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.h b/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.h new file mode 100644 index 0000000000..c37894d3a8 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.h @@ -0,0 +1,140 @@ +/** @file
+
+Copyright (c) 2004 - 2007, 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:
+
+ Uhci.h
+
+Abstract:
+
+ The definition for UHCI driver model and HC protocol routines.
+
+Revision History
+
+
+**/
+
+#ifndef _UHCI_H
+#define _UHCI_H
+
+//
+// The package level header files this module uses
+//
+#include <PiDxe.h>
+//
+// The protocols, PPI and GUID defintions for this module
+//
+#include <Protocol/Usb2HostController.h>
+#include <Protocol/UsbHostController.h>
+#include <Protocol/PciIo.h>
+//
+// The Library classes this module consumes
+//
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include <IndustryStandard/Pci22.h>
+
+typedef struct _USB_HC_DEV USB_HC_DEV;
+
+#include "UsbHcMem.h"
+#include "UhciQueue.h"
+#include "UhciReg.h"
+#include "UhciSched.h"
+#include "UhciDebug.h"
+
+enum {
+ //
+ // Stall times
+ //
+ STALL_1_MS = 1000,
+ STALL_1_SECOND = 1000 *STALL_1_MS,
+
+ UHC_SYN_POLL = 50,
+ FORCE_GLOBAL_RESUME_TIME = 20 *STALL_1_MS,
+ ROOT_PORT_REST_TIME = 50 *STALL_1_MS,
+ PORT_RESET_RECOVERY_TIME = 10 *STALL_1_MS,
+ INTERRUPT_POLLING_TIME = 50 * 10000UL,
+
+ //
+ // UHC raises TPL to TPL_NOTIFY to serialize all its operations
+ // to protect shared data structures.
+ //
+ UHCI_TPL = TPL_NOTIFY,
+
+ USB_HC_DEV_SIGNATURE = EFI_SIGNATURE_32 ('u', 'h', 'c', 'i'),
+};
+
+#pragma pack(1)
+typedef struct {
+ UINT8 PI;
+ UINT8 SubClassCode;
+ UINT8 BaseCode;
+} USB_CLASSC;
+#pragma pack()
+
+#define UHC_FROM_USB_HC_PROTO(This) CR(This, USB_HC_DEV, UsbHc, USB_HC_DEV_SIGNATURE)
+#define UHC_FROM_USB2_HC_PROTO(This) CR(This, USB_HC_DEV, Usb2Hc, USB_HC_DEV_SIGNATURE)
+
+//
+// USB_HC_DEV support the UHCI hardware controller. It schedules
+// the asynchronous interrupt transfer with the same method as
+// EHCI: a reversed tree structure. For synchronous interrupt,
+// control and bulk transfer, it uses three static queue head to
+// schedule them. SyncIntQh is for interrupt transfer. LsCtrlQh is
+// for LOW speed control transfer, and FsCtrlBulkQh is for FULL
+// speed control or bulk transfer. This is because FULL speed contrl
+// or bulk transfer can reclaim the unused bandwidth. Some USB
+// device requires this bandwidth reclamation capability.
+//
+typedef struct _USB_HC_DEV {
+ UINT32 Signature;
+ EFI_USB_HC_PROTOCOL UsbHc;
+ EFI_USB2_HC_PROTOCOL Usb2Hc;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ //
+ // Schedule data structures
+ //
+ UINT32 *FrameBase;
+ UHCI_QH_SW *SyncIntQh;
+ UHCI_QH_SW *CtrlQh;
+ UHCI_QH_SW *BulkQh;
+
+ //
+ // Structures to maintain asynchronus interrupt transfers.
+ // When asynchronous interrutp transfer is unlinked from
+ // the frame list, the hardware may still hold a pointer
+ // to it. To synchronize with hardware, its resoureces are
+ // released in two steps using Recycle and RecycleWait.
+ // Check the asynchronous interrupt management routines.
+ //
+ LIST_ENTRY AsyncIntList;
+ EFI_EVENT AsyncIntMonitor;
+ UHCI_ASYNC_REQUEST *Recycle;
+ UHCI_ASYNC_REQUEST *RecycleWait;
+
+
+ UINTN RootPorts;
+ USBHC_MEM_POOL *MemPool;
+ EFI_UNICODE_STRING_TABLE *CtrlNameTable;
+ VOID *FrameMapping;
+} USB_HC_DEV;
+
+extern EFI_DRIVER_BINDING_PROTOCOL gUhciDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gUhciComponentName;
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.inf b/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.inf new file mode 100644 index 0000000000..34d7b74fd0 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.inf @@ -0,0 +1,101 @@ +#/** @file
+# Component name for module Uhci
+#
+# Copyright (c) 2006, Intel Corporation. All right reserved.
+#
+# 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.
+#
+#
+#**/
+
+################################################################################
+#
+# Defines Section - statements that will be processed to create a Makefile.
+#
+################################################################################
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Uhci
+ FILE_GUID = 2FB92EFA-2EE0-4bae-9EB6-7464125E1EF7
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = UhciDriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+################################################################################
+#
+# Sources Section - list of files that are required for the build to succeed.
+#
+################################################################################
+
+[Sources.common]
+ UhciSched.c
+ UhciDebug.c
+ UsbHcMem.h
+ UhciDebug.h
+ UhciQueue.c
+ UhciReg.c
+ UsbHcMem.c
+ UhciQueue.h
+ Uhci.c
+ Uhci.h
+ UhciReg.h
+ UhciSched.h
+ ComponentName.c
+
+
+################################################################################
+#
+# Package Dependency Section - list of Package files that are required for
+# this module.
+#
+################################################################################
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+################################################################################
+#
+# Library Class Section - list of Library Classes that are required for
+# this module.
+#
+################################################################################
+
+[LibraryClasses]
+ MemoryAllocationLib
+ BaseLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ DebugLib
+
+
+################################################################################
+#
+# Protocol C Name Section - list of Protocol and Protocol Notify C Names
+# that this module uses or produces.
+#
+################################################################################
+
+[Protocols]
+ gEfiPciIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUsbHcProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUsb2HcProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.msa b/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.msa new file mode 100644 index 0000000000..75ebf35ba1 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.msa @@ -0,0 +1,84 @@ +<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>Uhci</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>2FB92EFA-2EE0-4bae-9EB6-7464125E1EF7</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module Uhci</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2006, Intel Corporation. All right reserved.</Copyright>
+ <License>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.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>Uhci</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseMemoryLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>MemoryAllocationLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>ComponentName.c</Filename>
+ <Filename>uhci.h</Filename>
+ <Filename>UhciSched.h</Filename>
+ <Filename>UhciReg.h</Filename>
+ <Filename>uhci.c</Filename>
+ <Filename>UhciQueue.h</Filename>
+ <Filename>UsbHcMem.c</Filename>
+ <Filename>UhciReg.c</Filename>
+ <Filename>UhciQueue.c</Filename>
+ <Filename>UhciDebug.h</Filename>
+ <Filename>UsbHcMem.h</Filename>
+ <Filename>UhciDebug.c</Filename>
+ <Filename>UhciSched.c</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUsb2HcProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUsbHcProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiPciIoProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>UhciDriverEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea>
\ No newline at end of file diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.c b/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.c new file mode 100644 index 0000000000..d47314ffbf --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.c @@ -0,0 +1,183 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ UhciDebug.c
+
+Abstract:
+
+ This file provides the information dump support for Uhci when in debug mode.
+ You can dynamically adjust the debug level by changing variable gEHCDebugLevel
+ and gEHCErrorLevel.
+
+Revision History
+
+
+**/
+
+#include "Uhci.h"
+#include "UhciDebug.h"
+
+#ifdef EFI_DEBUG
+
+UINTN mUhciDebugMask = USB_DEBUG_FORCE_OUTPUT;
+
+
+/**
+ Debug debug print interface for UHCI
+
+ @param Format String to use for the print, followed by print arguments
+
+ @return None
+
+**/
+VOID
+UhciDebug (
+ IN CHAR8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+
+ VA_START (Marker, Format);
+ DebugVPrint (DEBUG_INFO, Format, Marker);
+ VA_END (Marker);
+}
+
+
+/**
+ Debug error print interface for UHCI
+
+ @param Format String to use for the print, followed by print arguments
+
+ @return None
+
+**/
+VOID
+UhciError (
+ IN CHAR8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+
+ VA_START (Marker, Format);
+ DebugVPrint (DEBUG_ERROR, Format, Marker);
+ VA_END (Marker);
+}
+
+
+
+/**
+ Debug print interface for UHCI
+
+ @param Level Level to control debug print
+ @param Format String to use for the print, followed by print arguments
+
+ @return None
+
+**/
+VOID
+UhciDebugPrint (
+ IN UINTN Level,
+ IN CHAR8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+
+ VA_START (Marker, Format);
+
+ if (Level & mUhciDebugMask) {
+ if (mUhciDebugMask & USB_DEBUG_FORCE_OUTPUT) {
+ DebugVPrint (DEBUG_ERROR, Format, Marker);
+ } else {
+ DebugVPrint (DEBUG_INFO, Format, Marker);
+ }
+ }
+
+ VA_END (Marker);
+}
+
+
+/**
+ Dump the content of QH structure
+
+ @param QhSw Pointer to software QH structure
+
+ @return None
+
+**/
+VOID
+UhciDumpQh (
+ IN UHCI_QH_SW *QhSw
+ )
+{
+ UINTN Level;
+
+ Level = UHCI_DEBUG_QH;
+
+ UhciDebugPrint (Level, "&QhSw @ 0x%x\n", QhSw);
+ UhciDebugPrint (Level, "QhSw.NextQh - 0x%x\n", QhSw->NextQh);
+ UhciDebugPrint (Level, "QhSw.TDs - 0x%x\n", QhSw->TDs);
+ UhciDebugPrint (Level, "QhSw.QhHw:\n");
+ UhciDebugPrint (Level, " Horizon Link - %x\n", QhSw->QhHw.HorizonLink);
+ UhciDebugPrint (Level, " Vertical Link - %x\n\n", QhSw->QhHw.VerticalLink);
+}
+
+
+/**
+ Dump the content of TD structure.
+
+ @param TdSw Pointer to software TD structure
+ @param IsCur Whether dump the whole list, or only dump the current TD
+
+ @return None
+
+**/
+VOID
+UhciDumpTds (
+ IN UHCI_TD_SW *TdSw
+ )
+{
+ UHCI_TD_SW *CurTdSw;
+ UINTN Level;
+
+ Level = UHCI_DEBUG_TD;
+ CurTdSw = TdSw;
+
+ while (CurTdSw != NULL) {
+ UhciDebugPrint (Level, "TdSw @ 0x%x\n", CurTdSw);
+ UhciDebugPrint (Level, "TdSw.NextTd - 0x%x\n", CurTdSw->NextTd);
+ UhciDebugPrint (Level, "TdSw.DataLen - %d\n", CurTdSw->DataLen);
+ UhciDebugPrint (Level, "TdSw.Data - 0x%x\n", CurTdSw->Data);
+ UhciDebugPrint (Level, "TdHw:\n");
+ UhciDebugPrint (Level, " NextLink - 0x%x\n", CurTdSw->TdHw.NextLink);
+ UhciDebugPrint (Level, " ActualLen - %d\n", CurTdSw->TdHw.ActualLen);
+ UhciDebugPrint (Level, " Status - 0x%x\n", CurTdSw->TdHw.Status);
+ UhciDebugPrint (Level, " IOC - %d\n", CurTdSw->TdHw.IntOnCpl);
+ UhciDebugPrint (Level, " IsIsoCh - %d\n", CurTdSw->TdHw.IsIsoch);
+ UhciDebugPrint (Level, " LowSpeed - %d\n", CurTdSw->TdHw.LowSpeed);
+ UhciDebugPrint (Level, " ErrorCount - %d\n", CurTdSw->TdHw.ErrorCount);
+ UhciDebugPrint (Level, " ShortPacket - %d\n", CurTdSw->TdHw.ShortPacket);
+ UhciDebugPrint (Level, " PidCode - 0x%x\n", CurTdSw->TdHw.PidCode);
+ UhciDebugPrint (Level, " DevAddr - %d\n", CurTdSw->TdHw.DeviceAddr);
+ UhciDebugPrint (Level, " EndPoint - %d\n", CurTdSw->TdHw.EndPoint);
+ UhciDebugPrint (Level, " DataToggle - %d\n", CurTdSw->TdHw.DataToggle);
+ UhciDebugPrint (Level, " MaxPacketLen - %d\n", CurTdSw->TdHw.MaxPacketLen);
+ UhciDebugPrint (Level, " DataBuffer - 0x%x\n\n",CurTdSw->TdHw.DataBuffer);
+
+ CurTdSw = CurTdSw->NextTd;
+ }
+}
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.h b/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.h new file mode 100644 index 0000000000..d71791a414 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.h @@ -0,0 +1,134 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ UhciDebug.h
+
+Abstract:
+
+ This file contains the definination for host controller debug support routines
+
+Revision History
+
+
+**/
+
+#ifndef _EFI_UHCI_DEBUG_H_
+#define _EFI_UHCI_DEBUG_H_
+
+//
+// DEBUG support
+//
+#define USB_DEBUG_FORCE_OUTPUT (UINTN) (1 << 0)
+#define UHCI_DEBUG_QH (UINTN) (1 << 2)
+#define UHCI_DEBUG_TD (UINTN) (1 << 3)
+
+VOID
+UhciDebugPrint (
+ IN UINTN Level,
+ IN CHAR8 *Format,
+ ...
+ )
+/*++
+
+Routine Description:
+
+ Debug print interface for UHCI
+
+Arguments:
+
+ Level - Level to control debug print
+ Format - String to use for the print, followed by print arguments
+
+Returns:
+
+ None
+
+--*/
+;
+
+
+/**
+ Debug print interface for UHCI
+
+ @param Format String to use for the print, followed by print arguments
+
+ @return None
+
+**/
+VOID
+UhciDebug (
+ IN CHAR8 *Format,
+ ...
+ )
+;
+
+
+/**
+ Debug error print interface for UHCI
+
+ @param Format String to use for the print, followed by print arguments
+
+ @return None
+
+**/
+VOID
+UhciError (
+ IN CHAR8 *Format,
+ ...
+ )
+;
+
+
+/**
+ Dump the content of QH structure
+
+ @param QhSw Pointer to software QH structure
+
+ @return None
+
+**/
+VOID
+UhciDumpQh (
+ IN UHCI_QH_SW *QhSw
+ )
+;
+
+
+/**
+ Dump the content of TD structure.
+
+ @param TdSw Pointer to software TD structure
+
+ @return None
+
+**/
+VOID
+UhciDumpTds (
+ IN UHCI_TD_SW *TdSw
+ )
+;
+
+
+#ifdef EFI_DEBUG
+ #define UHCI_DEBUG(arg) UhciDebug arg
+ #define UHCI_ERROR(arg) UhciError arg
+ #define UHCI_DUMP_TDS(arg) UhciDumpTds arg
+ #define UHCI_DUMP_QH(arg) UhciDumpQh arg
+#else
+ #define UHCI_DEBUG(arg)
+ #define UHCI_ERROR(arg)
+ #define UHCI_DUMP_TDS(arg)
+ #define UHCI_DUMP_QH(arg)
+#endif
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c b/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c new file mode 100644 index 0000000000..843c9e38aa --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c @@ -0,0 +1,703 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ UhciQueue.c
+
+Abstract:
+
+ The UHCI register operation routines.
+
+Revision History
+
+
+**/
+
+#include "Uhci.h"
+
+
+/**
+ Map address of request structure buffer
+
+ @param Uhc The UHCI device
+ @param Request The user request buffer
+ @param MappedAddr Mapped address of request
+ @param Map Identificaion of this mapping to return
+
+ @return EFI_SUCCESS : Success
+ @return EFI_DEVICE_ERROR : Fail to map the user request
+
+**/
+EFI_STATUS
+UhciMapUserRequest (
+ IN USB_HC_DEV *Uhc,
+ IN OUT VOID *Request,
+ OUT UINT8 **MappedAddr,
+ OUT VOID **Map
+ )
+{
+ EFI_STATUS Status;
+ UINTN Len;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ Len = sizeof (EFI_USB_DEVICE_REQUEST);
+ Status = Uhc->PciIo->Map (
+ Uhc->PciIo,
+ EfiPciIoOperationBusMasterRead,
+ Request,
+ &Len,
+ &PhyAddr,
+ Map
+ );
+
+ if (!EFI_ERROR (Status)) {
+ *MappedAddr = (UINT8 *) (UINTN) PhyAddr;
+ }
+
+ return Status;
+}
+
+
+/**
+ Map address of user data buffer
+
+ @param Uhc The UHCI device
+ @param Direction direction of the data transfer
+ @param Data The user data buffer
+ @param Len Length of the user data
+ @param PktId Packet identificaion
+ @param MappedAddr mapped address to return
+ @param Map identificaion of this mapping to return
+
+ @return EFI_SUCCESS : Success
+ @return EFI_DEVICE_ERROR : Fail to map the user data
+
+**/
+EFI_STATUS
+UhciMapUserData (
+ IN USB_HC_DEV *Uhc,
+ IN EFI_USB_DATA_DIRECTION Direction,
+ IN VOID *Data,
+ IN OUT UINTN *Len,
+ OUT UINT8 *PktId,
+ OUT UINT8 **MappedAddr,
+ OUT VOID **Map
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+
+ Status = EFI_SUCCESS;
+
+ switch (Direction) {
+ case EfiUsbDataIn:
+ //
+ // BusMasterWrite means cpu read
+ //
+ *PktId = INPUT_PACKET_ID;
+ Status = Uhc->PciIo->Map (
+ Uhc->PciIo,
+ EfiPciIoOperationBusMasterWrite,
+ Data,
+ Len,
+ &PhyAddr,
+ Map
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ *MappedAddr = (UINT8 *) (UINTN) PhyAddr;
+ break;
+
+ case EfiUsbDataOut:
+ *PktId = OUTPUT_PACKET_ID;
+ Status = Uhc->PciIo->Map (
+ Uhc->PciIo,
+ EfiPciIoOperationBusMasterRead,
+ Data,
+ Len,
+ &PhyAddr,
+ Map
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ *MappedAddr = (UINT8 *) (UINTN) PhyAddr;
+ break;
+
+ case EfiUsbNoData:
+ if ((Len != NULL) && (*Len != 0)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto EXIT;
+ }
+
+ *PktId = OUTPUT_PACKET_ID;
+ *Len = 0;
+ *MappedAddr = NULL;
+ *Map = NULL;
+ break;
+
+ default:
+ Status = EFI_INVALID_PARAMETER;
+ }
+
+EXIT:
+ return Status;
+}
+
+
+
+/**
+ Link the TD To QH
+
+ @param Qh The queue head for the TD to link to
+ @param Td The TD to link
+
+ @return VOID
+
+**/
+VOID
+UhciLinkTdToQh (
+ IN UHCI_QH_SW *Qh,
+ IN UHCI_TD_SW *Td
+ )
+{
+ ASSERT ((Qh != NULL) && (Td != NULL));
+
+ Qh->QhHw.VerticalLink = QH_VLINK (Td, FALSE);
+ Qh->TDs = (VOID *) Td;
+}
+
+
+/**
+ Unlink TD from the QH
+
+ @param Qh The queue head to unlink from
+ @param Td The TD to unlink
+
+ @return VOID
+
+**/
+VOID
+UhciUnlinkTdFromQh (
+ IN UHCI_QH_SW *Qh,
+ IN UHCI_TD_SW *Td
+ )
+{
+ ASSERT ((Qh != NULL) && (Td != NULL));
+
+ Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
+ Qh->TDs = NULL;
+}
+
+
+/**
+ Append a new TD To the previous TD
+
+ @param PrevTd Previous UHCI_TD_SW to be linked to
+ @param ThisTd TD to link
+
+ @return VOID
+
+**/
+STATIC
+VOID
+UhciAppendTd (
+ IN UHCI_TD_SW *PrevTd,
+ IN UHCI_TD_SW *ThisTd
+ )
+{
+ ASSERT ((PrevTd != NULL) && (ThisTd != NULL));
+
+ PrevTd->TdHw.NextLink = TD_LINK (ThisTd, TRUE, FALSE);
+ PrevTd->NextTd = (VOID *) ThisTd;
+}
+
+
+/**
+ Delete a list of TDs
+
+ @param Uhc The UHCI device
+ @param FirstTd TD link list head
+
+ @return VOID
+
+**/
+VOID
+UhciDestoryTds (
+ IN USB_HC_DEV *Uhc,
+ IN UHCI_TD_SW *FirstTd
+ )
+{
+ UHCI_TD_SW *NextTd;
+ UHCI_TD_SW *ThisTd;
+
+ NextTd = FirstTd;
+
+ while (NextTd != NULL) {
+ ThisTd = NextTd;
+ NextTd = ThisTd->NextTd;
+ UsbHcFreeMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_SW));
+ }
+}
+
+
+/**
+ Create an initialize a new queue head
+
+ @param Uhc The UHCI device
+ @param Interval The polling interval for the queue
+
+ @return The newly created queue header
+
+**/
+UHCI_QH_SW *
+UhciCreateQh (
+ IN USB_HC_DEV *Uhc,
+ IN UINTN Interval
+ )
+{
+ UHCI_QH_SW *Qh;
+
+ Qh = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_QH_SW));
+
+ if (Qh == NULL) {
+ return NULL;
+ }
+
+ Qh->QhHw.HorizonLink = QH_HLINK (NULL, TRUE);
+ Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
+ Qh->Interval = Interval;
+ Qh->TDs = NULL;
+ Qh->NextQh = NULL;
+
+ return Qh;
+}
+
+
+/**
+ Create and intialize a TD
+
+ @param Uhc The UHCI device
+
+ @return The newly allocated and initialized TD
+
+**/
+STATIC
+UHCI_TD_SW *
+UhciCreateTd (
+ IN USB_HC_DEV *Uhc
+ )
+{
+ UHCI_TD_SW *Td;
+
+ Td = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_TD_SW));
+ if (Td == NULL) {
+ return NULL;
+ }
+
+ Td->TdHw.NextLink = TD_LINK (NULL, FALSE, TRUE);
+ Td->NextTd = NULL;
+ Td->Data = NULL;
+ Td->DataLen = 0;
+
+ return Td;
+}
+
+
+/**
+ Create and initialize a TD for Setup Stage of a control transfer
+
+ @param Uhc The UHCI device
+ @param DevAddr Device address
+ @param Request Device request
+ @param IsLow Full speed or low speed
+
+ @return The created setup Td Pointer
+
+**/
+STATIC
+UHCI_TD_SW *
+UhciCreateSetupTd (
+ IN USB_HC_DEV *Uhc,
+ IN UINT8 DevAddr,
+ IN UINT8 *Request,
+ IN BOOLEAN IsLow
+ )
+{
+ UHCI_TD_SW *Td;
+
+ Td = UhciCreateTd (Uhc);
+
+ if (Td == NULL) {
+ return NULL;
+ }
+
+ Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);
+ Td->TdHw.ShortPacket = FALSE;
+ Td->TdHw.IsIsoch = FALSE;
+ Td->TdHw.IntOnCpl = FALSE;
+ Td->TdHw.ErrorCount = 0x03;
+ Td->TdHw.Status |= USBTD_ACTIVE;
+ Td->TdHw.DataToggle = 0;
+ Td->TdHw.EndPoint = 0;
+ Td->TdHw.LowSpeed = IsLow ? 1 : 0;
+ Td->TdHw.DeviceAddr = DevAddr & 0x7F;
+ Td->TdHw.MaxPacketLen = (UINT32) (sizeof (EFI_USB_DEVICE_REQUEST) - 1);
+ Td->TdHw.PidCode = SETUP_PACKET_ID;
+ Td->TdHw.DataBuffer = (UINT32) (UINTN) Request;
+
+ Td->Data = Request;
+ Td->DataLen = sizeof (EFI_USB_DEVICE_REQUEST);
+
+ return Td;
+}
+
+
+/**
+ Create a TD for data
+
+ @param Uhc The UHCI device
+ @param DevAddr Device address
+ @param Endpoint Endpoint number
+ @param DataPtr Data buffer
+ @param Len Data length
+ @param PktId Packet ID
+ @param Toggle Data toggle value
+ @param IsLow Full speed or low speed
+
+ @return Data Td pointer if success, otherwise NUL
+
+**/
+STATIC
+UHCI_TD_SW *
+UhciCreateDataTd (
+ IN USB_HC_DEV *Uhc,
+ IN UINT8 DevAddr,
+ IN UINT8 Endpoint,
+ IN UINT8 *DataPtr,
+ IN UINTN Len,
+ IN UINT8 PktId,
+ IN UINT8 Toggle,
+ IN BOOLEAN IsLow
+ )
+{
+ UHCI_TD_SW *Td;
+
+ //
+ // Code as length - 1, and the max valid length is 0x500
+ //
+ ASSERT (Len <= 0x500);
+
+ Td = UhciCreateTd (Uhc);
+
+ if (Td == NULL) {
+ return NULL;
+ }
+
+ Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);
+ Td->TdHw.ShortPacket = FALSE;
+ Td->TdHw.IsIsoch = FALSE;
+ Td->TdHw.IntOnCpl = FALSE;
+ Td->TdHw.ErrorCount = 0X03;
+ Td->TdHw.Status = USBTD_ACTIVE;
+ Td->TdHw.LowSpeed = IsLow ? 1 : 0;
+ Td->TdHw.DataToggle = Toggle & 0x01;
+ Td->TdHw.EndPoint = Endpoint & 0x0F;
+ Td->TdHw.DeviceAddr = DevAddr & 0x7F;
+ Td->TdHw.MaxPacketLen = (UINT32) (Len - 1);
+ Td->TdHw.PidCode = (UINT8) PktId;
+ Td->TdHw.DataBuffer = (UINT32) (UINTN) DataPtr;
+
+ Td->Data = DataPtr;
+ Td->DataLen = (UINT16) Len;
+
+ return Td;
+}
+
+
+/**
+ Create TD for the Status Stage of control transfer
+
+ @param Uhc The UHCI device
+ @param DevAddr Device address
+ @param PktId Packet ID
+ @param IsLow Full speed or low speed
+
+ @return Status Td Pointer
+
+**/
+STATIC
+UHCI_TD_SW *
+UhciCreateStatusTd (
+ IN USB_HC_DEV *Uhc,
+ IN UINT8 DevAddr,
+ IN UINT8 PktId,
+ IN BOOLEAN IsLow
+ )
+{
+ UHCI_TD_SW *Td;
+
+ Td = UhciCreateTd (Uhc);
+
+ if (Td == NULL) {
+ return NULL;
+ }
+
+ Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);
+ Td->TdHw.ShortPacket = FALSE;
+ Td->TdHw.IsIsoch = FALSE;
+ Td->TdHw.IntOnCpl = FALSE;
+ Td->TdHw.ErrorCount = 0x03;
+ Td->TdHw.Status |= USBTD_ACTIVE;
+ Td->TdHw.MaxPacketLen = 0x7FF; //0x7FF: there is no data (refer to UHCI spec)
+ Td->TdHw.DataToggle = 1;
+ Td->TdHw.EndPoint = 0;
+ Td->TdHw.LowSpeed = IsLow ? 1 : 0;
+ Td->TdHw.DeviceAddr = DevAddr & 0x7F;
+ Td->TdHw.PidCode = (UINT8) PktId;
+ Td->TdHw.DataBuffer = (UINT32) (UINTN) NULL;
+
+ Td->Data = NULL;
+ Td->DataLen = 0;
+
+ return Td;
+}
+
+
+/**
+ Create Tds list for Control Transfer
+
+ @param Uhc The UHCI device
+ @param DeviceAddr The device address
+ @param DataPktId Packet Identification of Data Tds
+ @param Request A pointer to request structure buffer to transfer
+ @param Data A pointer to user data buffer to transfer
+ @param DataLen Length of user data to transfer
+ @param MaxPacket Maximum packet size for control transfer
+ @param IsLow Full speed or low speed
+
+ @return The Td list head for the control transfer
+
+**/
+UHCI_TD_SW *
+UhciCreateCtrlTds (
+ IN USB_HC_DEV *Uhc,
+ IN UINT8 DeviceAddr,
+ IN UINT8 DataPktId,
+ IN UINT8 *Request,
+ IN UINT8 *Data,
+ IN UINTN DataLen,
+ IN UINT8 MaxPacket,
+ IN BOOLEAN IsLow
+ )
+{
+ UHCI_TD_SW *SetupTd;
+ UHCI_TD_SW *FirstDataTd;
+ UHCI_TD_SW *DataTd;
+ UHCI_TD_SW *PrevDataTd;
+ UHCI_TD_SW *StatusTd;
+ UINT8 DataToggle;
+ UINT8 StatusPktId;
+ UINTN ThisTdLen;
+
+
+ DataTd = NULL;
+ SetupTd = NULL;
+ FirstDataTd = NULL;
+ PrevDataTd = NULL;
+ StatusTd = NULL;
+
+ //
+ // Create setup packets for the transfer
+ //
+ SetupTd = UhciCreateSetupTd (Uhc, DeviceAddr, Request, IsLow);
+
+ if (SetupTd == NULL) {
+ return NULL;
+ }
+
+ //
+ // Create data packets for the transfer
+ //
+ DataToggle = 1;
+
+ while (DataLen > 0) {
+ //
+ // PktSize is the data load size in each Td.
+ //
+ ThisTdLen = (DataLen > MaxPacket ? MaxPacket : DataLen);
+
+ DataTd = UhciCreateDataTd (
+ Uhc,
+ DeviceAddr,
+ 0,
+ Data,
+ ThisTdLen,
+ DataPktId,
+ DataToggle,
+ IsLow
+ );
+
+ if (DataTd == NULL) {
+ goto FREE_TD;
+ }
+
+ if (FirstDataTd == NULL) {
+ FirstDataTd = DataTd;
+ FirstDataTd->NextTd = NULL;
+ } else {
+ UhciAppendTd (PrevDataTd, DataTd);
+ }
+
+ DataToggle ^= 1;
+ PrevDataTd = DataTd;
+ Data += ThisTdLen;
+ DataLen -= ThisTdLen;
+ }
+
+ //
+ // Status packet is on the opposite direction to data packets
+ //
+ if (OUTPUT_PACKET_ID == DataPktId) {
+ StatusPktId = INPUT_PACKET_ID;
+ } else {
+ StatusPktId = OUTPUT_PACKET_ID;
+ }
+
+ StatusTd = UhciCreateStatusTd (Uhc, DeviceAddr, StatusPktId, IsLow);
+
+ if (StatusTd == NULL) {
+ goto FREE_TD;
+ }
+
+ //
+ // Link setup Td -> data Tds -> status Td together
+ //
+ if (FirstDataTd != NULL) {
+ UhciAppendTd (SetupTd, FirstDataTd);
+ UhciAppendTd (PrevDataTd, StatusTd);
+ } else {
+ UhciAppendTd (SetupTd, StatusTd);
+ }
+
+ return SetupTd;
+
+FREE_TD:
+ if (SetupTd != NULL) {
+ UhciDestoryTds (Uhc, SetupTd);
+ }
+
+ if (FirstDataTd != NULL) {
+ UhciDestoryTds (Uhc, FirstDataTd);
+ }
+
+ return NULL;
+}
+
+
+/**
+ Create Tds list for Bulk/Interrupt Transfer
+
+ @param Uhc USB_HC_DEV
+ @param DevAddr Address of Device
+ @param EndPoint Endpoint Number
+ @param PktId Packet Identification of Data Tds
+ @param Data A pointer to user data buffer to transfer
+ @param DataLen Length of user data to transfer
+ @param DataToggle Data Toggle Pointer
+ @param MaxPacket Maximum packet size for Bulk/Interrupt transfer
+ @param IsLow Is Low Speed Device
+
+ @return The Tds list head for the bulk transfer
+
+**/
+UHCI_TD_SW *
+UhciCreateBulkOrIntTds (
+ IN USB_HC_DEV *Uhc,
+ IN UINT8 DevAddr,
+ IN UINT8 EndPoint,
+ IN UINT8 PktId,
+ IN UINT8 *Data,
+ IN UINTN DataLen,
+ IN OUT UINT8 *DataToggle,
+ IN UINT8 MaxPacket,
+ IN BOOLEAN IsLow
+ )
+{
+ UHCI_TD_SW *DataTd;
+ UHCI_TD_SW *FirstDataTd;
+ UHCI_TD_SW *PrevDataTd;
+ UINTN ThisTdLen;
+
+ DataTd = NULL;
+ FirstDataTd = NULL;
+ PrevDataTd = NULL;
+
+ //
+ // Create data packets for the transfer
+ //
+ while (DataLen > 0) {
+ //
+ // PktSize is the data load size that each Td.
+ //
+ ThisTdLen = DataLen;
+
+ if (DataLen > MaxPacket) {
+ ThisTdLen = MaxPacket;
+ }
+
+ DataTd = UhciCreateDataTd (
+ Uhc,
+ DevAddr,
+ EndPoint,
+ Data,
+ ThisTdLen,
+ PktId,
+ *DataToggle,
+ IsLow
+ );
+
+ if (DataTd == NULL) {
+ goto FREE_TD;
+ }
+
+ if (PktId == INPUT_PACKET_ID) {
+ DataTd->TdHw.ShortPacket = TRUE;
+ }
+
+ if (FirstDataTd == NULL) {
+ FirstDataTd = DataTd;
+ FirstDataTd->NextTd = NULL;
+ } else {
+ UhciAppendTd (PrevDataTd, DataTd);
+ }
+
+ *DataToggle ^= 1;
+ PrevDataTd = DataTd;
+ Data += ThisTdLen;
+ DataLen -= ThisTdLen;
+ }
+
+ return FirstDataTd;
+
+FREE_TD:
+ if (FirstDataTd != NULL) {
+ UhciDestoryTds (Uhc, FirstDataTd);
+ }
+
+ return NULL;
+}
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.h b/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.h new file mode 100644 index 0000000000..8b03ec6959 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.h @@ -0,0 +1,283 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ UhciQueue.h
+
+Abstract:
+
+ The definition for UHCI register operation routines.
+
+Revision History
+
+
+**/
+
+#ifndef _EFI_UHCI_QUEUE_H_
+#define _EFI_UHCI_QUEUE_H_
+
+//
+// Macroes used to set various links in UHCI's driver.
+// In this UHCI driver, QH's horizontal link always pointers to other QH,
+// and its vertical link always pointers to TD. TD's next pointer always
+// pointers to other sibling TD. Frame link always pointers to QH because
+// ISO transfer isn't supported.
+//
+// We should use UINT32 to access these pointers to void race conditions
+// with hardware.
+//
+#define QH_HLINK(Pointer, Terminate) \
+ (((UINT32) ((UINTN) (Pointer)) & 0xFFFFFFF0) | 0x02 | ((Terminate) ? 0x01 : 0))
+
+#define QH_VLINK(Pointer, Terminate) \
+ (((UINT32) ((UINTN) (Pointer)) & 0xFFFFFFF0) | ((Terminate) ? 0x01 : 0))
+
+#define TD_LINK(Pointer, VertFirst, Terminate) \
+ (((UINT32) ((UINTN) (Pointer)) & 0xFFFFFFF0) | \
+ ((VertFirst) ? 0x04 : 0) | ((Terminate) ? 0x01 : 0))
+
+#define LINK_TERMINATED(Link) (((Link) & 0x01) != 0)
+
+#define UHCI_ADDR(QhOrTd) ((VOID *) (UINTN) ((QhOrTd) & 0xFFFFFFF0))
+
+#pragma pack(1)
+//
+// Both links in QH has this internal structure:
+// Next pointer: 28, Reserved: 2, NextIsQh: 1, Terminate: 1
+// This is the same as frame list entry.
+//
+typedef struct {
+ UINT32 HorizonLink;
+ UINT32 VerticalLink;
+} UHCI_QH_HW;
+
+//
+// Next link in TD has this internal structure:
+// Next pointer: 28, Reserved: 1, Vertical First: 1, NextIsQh: 1, Terminate: 1
+//
+typedef struct {
+ UINT32 NextLink;
+ UINT32 ActualLen : 11;
+ UINT32 Reserved1 : 5;
+ UINT32 Status : 8;
+ UINT32 IntOnCpl : 1;
+ UINT32 IsIsoch : 1;
+ UINT32 LowSpeed : 1;
+ UINT32 ErrorCount : 2;
+ UINT32 ShortPacket : 1;
+ UINT32 Reserved2 : 2;
+ UINT32 PidCode : 8;
+ UINT32 DeviceAddr : 7;
+ UINT32 EndPoint : 4;
+ UINT32 DataToggle : 1;
+ UINT32 Reserved3 : 1;
+ UINT32 MaxPacketLen: 11;
+ UINT32 DataBuffer;
+} UHCI_TD_HW;
+#pragma pack()
+
+typedef struct _UHCI_TD_SW UHCI_TD_SW;
+typedef struct _UHCI_QH_SW UHCI_QH_SW;
+
+typedef struct _UHCI_QH_SW {
+ UHCI_QH_HW QhHw;
+ UHCI_QH_SW *NextQh;
+ UHCI_TD_SW *TDs;
+ UINTN Interval;
+} UHCI_QH_SW;
+
+typedef struct _UHCI_TD_SW {
+ UHCI_TD_HW TdHw;
+ UHCI_TD_SW *NextTd;
+ UINT8 *Data;
+ UINT16 DataLen;
+} UHCI_TD_SW;
+
+
+/**
+ Link the TD To QH
+
+ @param Qh The queue head for the TD to link to
+ @param Td The TD to link
+
+ @return VOID
+
+**/
+VOID
+UhciLinkTdToQh (
+ IN UHCI_QH_SW *Qh,
+ IN UHCI_TD_SW *Td
+ )
+;
+
+
+/**
+ Unlink TD from the QH
+
+ @param Qh The queue head to unlink from
+ @param Td The TD to unlink
+
+ @return VOID
+
+**/
+VOID
+UhciUnlinkTdFromQh (
+ IN UHCI_QH_SW *Qh,
+ IN UHCI_TD_SW *Td
+ )
+;
+
+
+/**
+ Map address of request structure buffer
+
+ @param Uhc The UHCI device
+ @param Request The user request buffer
+ @param MappedAddr Mapped address of request
+ @param Map Identificaion of this mapping to return
+
+ @return EFI_SUCCESS : Success
+ @return EFI_DEVICE_ERROR : Fail to map the user request
+
+**/
+EFI_STATUS
+UhciMapUserRequest (
+ IN USB_HC_DEV *Uhc,
+ IN OUT VOID *Request,
+ OUT UINT8 **MappedAddr,
+ OUT VOID **Map
+ )
+;
+
+
+/**
+ Map address of user data buffer
+
+ @param Uhc The UHCI device
+ @param Direction direction of the data transfer
+ @param Data The user data buffer
+ @param Len Length of the user data
+ @param PktId Packet identificaion
+ @param MappedAddr mapped address to return
+ @param Map identificaion of this mapping to return
+
+ @return EFI_SUCCESS : Success
+ @return EFI_DEVICE_ERROR : Fail to map the user data
+
+**/
+EFI_STATUS
+UhciMapUserData (
+ IN USB_HC_DEV *Uhc,
+ IN EFI_USB_DATA_DIRECTION Direction,
+ IN VOID *Data,
+ IN OUT UINTN *Len,
+ OUT UINT8 *PktId,
+ OUT UINT8 **MappedAddr,
+ OUT VOID **Map
+ )
+;
+
+
+/**
+ Delete a list of TDs
+
+ @param Uhc The UHCI device
+ @param FirstTd TD link list head
+
+ @return VOID
+
+**/
+VOID
+UhciDestoryTds (
+ IN USB_HC_DEV *Uhc,
+ IN UHCI_TD_SW *FirstTd
+ )
+;
+
+
+/**
+ Create an initialize a new queue head
+
+ @param Uhc The UHCI device
+ @param Interval The polling interval for the queue
+
+ @return The newly created queue header
+
+**/
+UHCI_QH_SW *
+UhciCreateQh (
+ IN USB_HC_DEV *Uhc,
+ IN UINTN Interval
+ )
+;
+
+
+/**
+ Create Tds list for Control Transfer
+
+ @param Uhc The UHCI device
+ @param DeviceAddr The device address
+ @param DataPktId Packet Identification of Data Tds
+ @param Request A pointer to request structure buffer to transfer
+ @param Data A pointer to user data buffer to transfer
+ @param DataLen Length of user data to transfer
+ @param MaxPacket Maximum packet size for control transfer
+ @param IsLow Full speed or low speed
+
+ @return The Td list head for the control transfer
+
+**/
+UHCI_TD_SW *
+UhciCreateCtrlTds (
+ IN USB_HC_DEV *Uhc,
+ IN UINT8 DeviceAddr,
+ IN UINT8 DataPktId,
+ IN UINT8 *Request,
+ IN UINT8 *Data,
+ IN UINTN DataLen,
+ IN UINT8 MaxPacket,
+ IN BOOLEAN IsLow
+ )
+;
+
+
+/**
+ Create Tds list for Bulk/Interrupt Transfer
+
+ @param Uhc USB_HC_DEV
+ @param DevAddr Address of Device
+ @param EndPoint Endpoint Number
+ @param PktId Packet Identification of Data Tds
+ @param Data A pointer to user data buffer to transfer
+ @param DataLen Length of user data to transfer
+ @param DataToggle Data Toggle Pointer
+ @param MaxPacket Maximum packet size for Bulk/Interrupt transfer
+ @param IsLow Is Low Speed Device
+
+ @return The Tds list head for the bulk transfer
+
+**/
+UHCI_TD_SW *
+UhciCreateBulkOrIntTds (
+ IN USB_HC_DEV *Uhc,
+ IN UINT8 DevAddr,
+ IN UINT8 EndPoint,
+ IN UINT8 PktId,
+ IN UINT8 *Data,
+ IN UINTN DataLen,
+ IN OUT UINT8 *DataToggle,
+ IN UINT8 MaxPacket,
+ IN BOOLEAN IsLow
+ )
+;
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.c b/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.c new file mode 100644 index 0000000000..63326292f3 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.c @@ -0,0 +1,303 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ UhciReg.c
+
+Abstract:
+
+ The UHCI register operation routines.
+
+Revision History
+
+
+**/
+
+#include "Uhci.h"
+
+
+/**
+ Read a UHCI register
+
+ @param PciIo The EFI_PCI_IO_PROTOCOL to use
+ @param Offset Register offset to USB_BAR_INDEX
+
+ @return Content of register
+
+**/
+UINT16
+UhciReadReg (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT32 Offset
+ )
+{
+ UINT16 Data;
+ EFI_STATUS Status;
+
+ Status = PciIo->Io.Read (
+ PciIo,
+ EfiPciIoWidthUint16,
+ USB_BAR_INDEX,
+ Offset,
+ 1,
+ &Data
+ );
+
+ if (EFI_ERROR (Status)) {
+ UHCI_ERROR (("UhciReadReg: PciIo Io.Read error: %r at offset %d\n", Status, Offset));
+
+ Data = 0xFFFF;
+ }
+
+ return Data;
+}
+
+
+/**
+ Write data to UHCI register
+
+ @param PciIo The EFI_PCI_IO_PROTOCOL to use
+ @param Offset Register offset to USB_BAR_INDEX
+ @param Data Data to write
+
+ @return VOID
+
+**/
+VOID
+UhciWriteReg (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT32 Offset,
+ IN UINT16 Data
+ )
+{
+ EFI_STATUS Status;
+
+ Status = PciIo->Io.Write (
+ PciIo,
+ EfiPciIoWidthUint16,
+ USB_BAR_INDEX,
+ Offset,
+ 1,
+ &Data
+ );
+
+ if (EFI_ERROR (Status)) {
+ UHCI_ERROR (("UhciWriteReg: PciIo Io.Write error: %r at offset %d\n", Status, Offset));
+ }
+}
+
+
+/**
+ Set a bit of the UHCI Register
+
+ @param PciIo The EFI_PCI_IO_PROTOCOL to use
+ @param Offset Register offset to USB_BAR_INDEX
+ @param Bit The bit to set
+
+ @return None
+
+**/
+VOID
+UhciSetRegBit (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT32 Offset,
+ IN UINT16 Bit
+ )
+{
+ UINT16 Data;
+
+ Data = UhciReadReg (PciIo, Offset);
+ Data |= Bit;
+ UhciWriteReg (PciIo, Offset, Data);
+}
+
+
+/**
+ Clear a bit of the UHCI Register
+
+ @param PciIo The PCI_IO protocol to access the PCI
+ @param Offset Register offset to USB_BAR_INDEX
+ @param Bit The bit to clear
+
+ @return None
+
+**/
+VOID
+UhciClearRegBit (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT32 Offset,
+ IN UINT16 Bit
+ )
+{
+ UINT16 Data;
+
+ Data = UhciReadReg (PciIo, Offset);
+ Data &= ~Bit;
+ UhciWriteReg (PciIo, Offset, Data);
+}
+
+
+/**
+ Clear all the interrutp status bits, these bits
+ are Write-Clean
+
+ @param Uhc The UHCI device
+
+ @return None
+
+**/
+VOID
+UhciAckAllInterrupt (
+ IN USB_HC_DEV *Uhc
+ )
+{
+ UhciWriteReg (Uhc->PciIo, USBSTS_OFFSET, 0x3F);
+
+ //
+ // If current HC is halted, re-enable it. Host Controller Process Error
+ // is a temporary error status.
+ //
+ if (!UhciIsHcWorking (Uhc->PciIo)) {
+ UHCI_ERROR (("UhciAckAllInterrupt: re-enable the UHCI from system error\n"));
+ Uhc->UsbHc.SetState (&Uhc->UsbHc, EfiUsbHcStateOperational);
+ }
+}
+
+
+
+/**
+ Stop the host controller
+
+ @param Uhc The UHCI device
+ @param Timeout Max time allowed
+
+ @retval EFI_SUCCESS The host controller is stopped
+ @retval EFI_TIMEOUT Failed to stop the host controller
+
+**/
+EFI_STATUS
+UhciStopHc (
+ IN USB_HC_DEV *Uhc,
+ IN UINTN Timeout
+ )
+{
+ UINT16 UsbSts;
+ UINTN Index;
+
+ UhciClearRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_RS);
+
+ //
+ // ensure the HC is in halt status after send the stop command
+ // Timeout is in us unit.
+ //
+ for (Index = 0; Index < (Timeout / 50) + 1; Index++) {
+ UsbSts = UhciReadReg (Uhc->PciIo, USBSTS_OFFSET);
+
+ if ((UsbSts & USBSTS_HCH) == USBSTS_HCH) {
+ return EFI_SUCCESS;
+ }
+
+ gBS->Stall (50);
+ }
+
+ return EFI_TIMEOUT;
+}
+
+
+/**
+ Check whether the host controller operates well
+
+ @param PciIo The PCI_IO protocol to use
+
+ @retval TRUE Host controller is working
+ @retval FALSE Host controller is halted or system error
+
+**/
+BOOLEAN
+UhciIsHcWorking (
+ IN EFI_PCI_IO_PROTOCOL *PciIo
+ )
+{
+ UINT16 UsbSts;
+
+ UsbSts = UhciReadReg (PciIo, USBSTS_OFFSET);
+
+ if (UsbSts & (USBSTS_HCPE | USBSTS_HSE | USBSTS_HCH)) {
+ UHCI_ERROR (("UhciIsHcWorking: current USB state is %x\n", UsbSts));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Set the UHCI frame list base address. It can't use
+ UhciWriteReg which access memory in UINT16.
+
+ @param PciIo The EFI_PCI_IO_PROTOCOL to use
+ @param Addr Address to set
+
+ @return VOID
+
+**/
+VOID
+UhciSetFrameListBaseAddr (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN VOID *Addr
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Data;
+
+ Data = (UINT32) ((UINTN) Addr & 0xFFFFF000);
+
+ Status = PciIo->Io.Write (
+ PciIo,
+ EfiPciIoWidthUint32,
+ USB_BAR_INDEX,
+ (UINT64) USB_FRAME_BASE_OFFSET,
+ 1,
+ &Data
+ );
+
+ if (EFI_ERROR (Status)) {
+ UHCI_ERROR (("UhciSetFrameListBaseAddr: PciIo Io.Write error: %r\n", Status));
+ }
+}
+
+
+/**
+ Disable USB Emulation
+
+ @param PciIo The EFI_PCI_IO_PROTOCOL protocol to use
+
+ @return VOID
+
+**/
+VOID
+UhciTurnOffUsbEmulation (
+ IN EFI_PCI_IO_PROTOCOL *PciIo
+ )
+{
+ UINT16 Command;
+
+ Command = 0;
+
+ PciIo->Pci.Write (
+ PciIo,
+ EfiPciIoWidthUint16,
+ USB_EMULATION_OFFSET,
+ 1,
+ &Command
+ );
+}
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.h b/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.h new file mode 100644 index 0000000000..95a98dbab9 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.h @@ -0,0 +1,264 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ UhciReg.h
+
+Abstract:
+
+ The definition for UHCI register operation routines.
+
+Revision History
+
+
+**/
+
+#ifndef _EFI_UHCI_REG_H_
+#define _EFI_UHCI_REG_H_
+
+#define BIT(a) (1 << (a))
+
+enum {
+ UHCI_FRAME_NUM = 1024,
+
+ //
+ // Register offset and PCI related staff
+ //
+ CLASSC_OFFSET = 0x09,
+ USBBASE_OFFSET = 0x20,
+ USB_BAR_INDEX = 4,
+ PCI_CLASSC_PI_UHCI = 0x00,
+
+ USBCMD_OFFSET = 0,
+ USBSTS_OFFSET = 2,
+ USBINTR_OFFSET = 4,
+ USBPORTSC_OFFSET = 0x10,
+ USB_FRAME_NO_OFFSET = 6,
+ USB_FRAME_BASE_OFFSET = 8,
+ USB_EMULATION_OFFSET = 0xC0,
+
+ //
+ // Packet IDs
+ //
+ SETUP_PACKET_ID = 0x2D,
+ INPUT_PACKET_ID = 0x69,
+ OUTPUT_PACKET_ID = 0xE1,
+ ERROR_PACKET_ID = 0x55,
+
+ //
+ // USB port status and control bit definition.
+ //
+ USBPORTSC_CCS = BIT(0), // Current Connect Status
+ USBPORTSC_CSC = BIT(1), // Connect Status Change
+ USBPORTSC_PED = BIT(2), // Port Enable / Disable
+ USBPORTSC_PEDC = BIT(3), // Port Enable / Disable Change
+ USBPORTSC_LSL = BIT(4), // Line Status Low BIT
+ USBPORTSC_LSH = BIT(5), // Line Status High BIT
+ USBPORTSC_RD = BIT(6), // Resume Detect
+ USBPORTSC_LSDA = BIT(8), // Low Speed Device Attached
+ USBPORTSC_PR = BIT(9), // Port Reset
+ USBPORTSC_SUSP = BIT(12), // Suspend
+
+ USB_MAX_ROOTHUB_PORT = 0x0F, // Max number of root hub port
+
+ //
+ // Command register bit definitions
+ //
+ USBCMD_RS = BIT(0), // Run/Stop
+ USBCMD_HCRESET = BIT(1), // Host reset
+ USBCMD_GRESET = BIT(2), // Global reset
+ USBCMD_EGSM = BIT(3), // Global Suspend Mode
+ USBCMD_FGR = BIT(4), // Force Global Resume
+ USBCMD_SWDBG = BIT(5), // SW Debug mode
+ USBCMD_CF = BIT(6), // Config Flag (sw only)
+ USBCMD_MAXP = BIT(7), // Max Packet (0 = 32, 1 = 64)
+
+ //
+ // USB Status register bit definitions
+ //
+ USBSTS_USBINT = BIT(0), // Interrupt due to IOC
+ USBSTS_ERROR = BIT(1), // Interrupt due to error
+ USBSTS_RD = BIT(2), // Resume Detect
+ USBSTS_HSE = BIT(3), // Host System Error
+ USBSTS_HCPE = BIT(4), // Host Controller Process Error
+ USBSTS_HCH = BIT(5), // HC Halted
+
+ USBTD_ACTIVE = BIT(7), // TD is still active
+ USBTD_STALLED = BIT(6), // TD is stalled
+ USBTD_BUFFERR = BIT(5), // Buffer underflow or overflow
+ USBTD_BABBLE = BIT(4), // Babble condition
+ USBTD_NAK = BIT(3), // NAK is received
+ USBTD_CRC = BIT(2), // CRC/Time out error
+ USBTD_BITSTUFF = BIT(1), // Bit stuff error
+};
+
+
+/**
+ Read a UHCI register
+
+ @param PciIo The EFI_PCI_IO_PROTOCOL to use
+ @param Offset Register offset to USB_BAR_INDEX
+
+ @return Content of register
+
+**/
+UINT16
+UhciReadReg (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT32 Offset
+ )
+;
+
+
+
+/**
+ Write data to UHCI register
+
+ @param PciIo The EFI_PCI_IO_PROTOCOL to use
+ @param Offset Register offset to USB_BAR_INDEX
+ @param Data Data to write
+
+ @return VOID
+
+**/
+VOID
+UhciWriteReg (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT32 Offset,
+ IN UINT16 Data
+ )
+;
+
+
+
+/**
+ Set a bit of the UHCI Register
+
+ @param PciIo The EFI_PCI_IO_PROTOCOL to use
+ @param Offset Register offset to USB_BAR_INDEX
+ @param Bit The bit to set
+
+ @return None
+
+**/
+VOID
+UhciSetRegBit (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT32 Offset,
+ IN UINT16 Bit
+ )
+;
+
+
+
+/**
+ Clear a bit of the UHCI Register
+
+ @param PciIo The PCI_IO protocol to access the PCI
+ @param Offset Register offset to USB_BAR_INDEX
+ @param Bit The bit to clear
+
+ @return None
+
+**/
+VOID
+UhciClearRegBit (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT32 Offset,
+ IN UINT16 Bit
+ )
+;
+
+
+/**
+ Clear all the interrutp status bits, these bits
+ are Write-Clean
+
+ @param Uhc The UHCI device
+
+ @return None
+
+**/
+VOID
+UhciAckAllInterrupt (
+ IN USB_HC_DEV *Uhc
+ )
+;
+
+
+/**
+ Stop the host controller
+
+ @param Uhc The UHCI device
+ @param Timeout Max time allowed
+
+ @retval EFI_SUCCESS The host controller is stopped
+ @retval EFI_TIMEOUT Failed to stop the host controller
+
+**/
+EFI_STATUS
+UhciStopHc (
+ IN USB_HC_DEV *Uhc,
+ IN UINTN Timeout
+ )
+;
+
+
+
+/**
+ Check whether the host controller operates well
+
+ @param PciIo The PCI_IO protocol to use
+
+ @retval TRUE Host controller is working
+ @retval FALSE Host controller is halted or system error
+
+**/
+BOOLEAN
+UhciIsHcWorking (
+ IN EFI_PCI_IO_PROTOCOL *PciIo
+ )
+;
+
+
+/**
+ Set the UHCI frame list base address. It can't use
+ UhciWriteReg which access memory in UINT16.
+
+ @param PciIo The EFI_PCI_IO_PROTOCOL to use
+ @param Addr Address to set
+
+ @return VOID
+
+**/
+VOID
+UhciSetFrameListBaseAddr (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN VOID *Addr
+ )
+;
+
+
+/**
+ Disable USB Emulation
+
+ @param PciIo The EFI_PCI_IO_PROTOCOL protocol to use
+
+ @return VOID
+
+**/
+VOID
+UhciTurnOffUsbEmulation (
+ IN EFI_PCI_IO_PROTOCOL *PciIo
+ )
+;
+#endif
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.c b/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.c new file mode 100644 index 0000000000..222322a2b0 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.c @@ -0,0 +1,1051 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ UhciSched.c
+
+Abstract:
+
+ The EHCI register operation routines.
+
+Revision History
+
+
+**/
+
+#include "Uhci.h"
+
+
+/**
+ Create Frame List Structure
+
+ @param Uhc UHCI device
+
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources
+ @retval EFI_UNSUPPORTED Map memory fail
+ @retval EFI_SUCCESS Success
+
+**/
+EFI_STATUS
+UhciInitFrameList (
+ IN USB_HC_DEV *Uhc
+ )
+{
+ EFI_PHYSICAL_ADDRESS MappedAddr;
+ EFI_STATUS Status;
+ VOID *Buffer;
+ VOID *Mapping;
+ UINTN Pages;
+ UINTN Bytes;
+ UINTN Index;
+
+ //
+ // The Frame List is a common buffer that will be
+ // accessed by both the cpu and the usb bus master
+ // at the same time. The Frame List ocupies 4K bytes,
+ // and must be aligned on 4-Kbyte boundaries.
+ //
+ Bytes = 4096;
+ Pages = EFI_SIZE_TO_PAGES (Bytes);
+
+ Status = Uhc->PciIo->AllocateBuffer (
+ Uhc->PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ Pages,
+ &Buffer,
+ 0
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Uhc->PciIo->Map (
+ Uhc->PciIo,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ Buffer,
+ &Bytes,
+ &MappedAddr,
+ &Mapping
+ );
+
+ if (EFI_ERROR (Status) || (Bytes != 4096)) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_ERROR;
+ }
+
+ Uhc->FrameBase = (UINT32 *) (UINTN) MappedAddr;
+ Uhc->FrameMapping = Mapping;
+
+ //
+ // Allocate the QH used by sync interrupt/control/bulk transfer.
+ // FS ctrl/bulk queue head is set to loopback so additional BW
+ // can be reclaimed. Notice, LS don't support bulk transfer and
+ // also doesn't support BW reclamation.
+ //
+ Uhc->SyncIntQh = UhciCreateQh (Uhc, 1);
+ Uhc->CtrlQh = UhciCreateQh (Uhc, 1);
+ Uhc->BulkQh = UhciCreateQh (Uhc, 1);
+
+ if ((Uhc->SyncIntQh == NULL) || (Uhc->CtrlQh == NULL) || (Uhc->BulkQh == NULL)) {
+ Uhc->PciIo->Unmap (Uhc->PciIo, Mapping);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // +-------------+
+ // | |
+ // Link the three together: SyncIntQh->CtrlQh->BulkQh <---------+
+ // Each frame entry is linked to this sequence of QH. These QH
+ // will remain on the schedul, never got removed
+ //
+ Uhc->SyncIntQh->QhHw.HorizonLink = QH_HLINK (Uhc->CtrlQh, FALSE);
+ Uhc->SyncIntQh->NextQh = Uhc->CtrlQh;
+
+ Uhc->CtrlQh->QhHw.HorizonLink = QH_HLINK (Uhc->BulkQh, FALSE);
+ Uhc->CtrlQh->NextQh = Uhc->BulkQh;
+
+ //
+ // Some old platform such as Intel's Tiger 4 has a difficult time
+ // in supporting the full speed bandwidth reclamation in the previous
+ // mentioned form. Most new platforms don't suffer it.
+ //
+#ifdef UHCI_NO_BW_RECLAMATION
+ Uhc->BulkQh->QhHw.HorizonLink = QH_HLINK (NULL, TRUE);
+#else
+ Uhc->BulkQh->QhHw.HorizonLink = QH_HLINK (Uhc->BulkQh, FALSE);
+#endif
+
+ Uhc->BulkQh->NextQh = NULL;
+
+ for (Index = 0; Index < UHCI_FRAME_NUM; Index++) {
+ Uhc->FrameBase[Index] = QH_HLINK (Uhc->SyncIntQh, FALSE);
+ }
+
+ //
+ // Tell the Host Controller where the Frame List lies,
+ // by set the Frame List Base Address Register.
+ //
+ UhciSetFrameListBaseAddr (Uhc->PciIo, (VOID *) (Uhc->FrameBase));
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ if (Uhc->SyncIntQh != NULL) {
+ UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW));
+ }
+
+ if (Uhc->CtrlQh != NULL) {
+ UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW));
+ }
+
+ if (Uhc->BulkQh != NULL) {
+ UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW));
+ }
+
+ Uhc->PciIo->FreeBuffer (Uhc->PciIo, Pages, Buffer);
+ return Status;
+}
+
+
+/**
+ Destory FrameList buffer
+
+ @param Uhc The UHCI device
+
+ @return VOID
+
+**/
+VOID
+UhciDestoryFrameList (
+ IN USB_HC_DEV *Uhc
+ )
+{
+ //
+ // Unmap the common buffer for framelist entry,
+ // and free the common buffer.
+ // Uhci's frame list occupy 4k memory.
+ //
+ Uhc->PciIo->Unmap (Uhc->PciIo, Uhc->FrameMapping);
+
+ Uhc->PciIo->FreeBuffer (
+ Uhc->PciIo,
+ EFI_SIZE_TO_PAGES (4096),
+ (VOID *) Uhc->FrameBase
+ );
+
+ if (Uhc->SyncIntQh != NULL) {
+ UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW));
+ }
+
+ if (Uhc->CtrlQh != NULL) {
+ UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW));
+ }
+
+ if (Uhc->BulkQh != NULL) {
+ UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW));
+ }
+
+ Uhc->FrameBase = NULL;
+ Uhc->SyncIntQh = NULL;
+ Uhc->CtrlQh = NULL;
+ Uhc->BulkQh = NULL;
+}
+
+
+/**
+ Convert the poll rate to the maxium 2^n that is smaller
+ than Interval
+
+ @param Interval The poll rate to convert
+
+ @return The converted poll rate
+
+**/
+UINTN
+UhciConvertPollRate (
+ IN UINTN Interval
+ )
+{
+ UINTN BitCount;
+
+ ASSERT (Interval != 0);
+
+ //
+ // Find the index (1 based) of the highest non-zero bit
+ //
+ BitCount = 0;
+
+ while (Interval != 0) {
+ Interval >>= 1;
+ BitCount++;
+ }
+
+ return (UINTN)1 << (BitCount - 1);
+}
+
+
+/**
+ Link a queue head (for asynchronous interrupt transfer) to
+ the frame list.
+
+ @param FrameBase The base of the frame list
+ @param Qh The queue head to link into
+
+ @return None
+
+**/
+VOID
+UhciLinkQhToFrameList (
+ UINT32 *FrameBase,
+ UHCI_QH_SW *Qh
+ )
+{
+ UINTN Index;
+ UHCI_QH_SW *Prev;
+ UHCI_QH_SW *Next;
+
+ ASSERT ((FrameBase != NULL) && (Qh != NULL));
+
+ for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
+ //
+ // First QH can't be NULL because we always keep static queue
+ // heads on the frame list
+ //
+ ASSERT (!LINK_TERMINATED (FrameBase[Index]));
+ Next = UHCI_ADDR (FrameBase[Index]);
+ Prev = NULL;
+
+ //
+ // Now, insert the queue head (Qh) into this frame:
+ // 1. Find a queue head with the same poll interval, just insert
+ // Qh after this queue head, then we are done.
+ //
+ // 2. Find the position to insert the queue head into:
+ // Previous head's interval is bigger than Qh's
+ // Next head's interval is less than Qh's
+ // Then, insert the Qh between then
+ //
+ // This method is very much the same as that used by EHCI.
+ // Because each QH's interval is round down to 2^n, poll
+ // rate is correct.
+ //
+ while (Next->Interval > Qh->Interval) {
+ Prev = Next;
+ Next = Next->NextQh;
+ }
+
+ ASSERT (Next != NULL);
+
+ //
+ // The entry may have been linked into the frame by early insertation.
+ // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
+ // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
+ // It isn't necessary to compare all the QH with the same interval to
+ // Qh. This is because if there is other QH with the same interval, Qh
+ // should has been inserted after that at FrameBase[0] and at FrameBase[0] it is
+ // impossible (Next == Qh)
+ //
+ if (Next == Qh) {
+ continue;
+ }
+
+ if (Next->Interval == Qh->Interval) {
+ //
+ // If there is a QH with the same interval, it locates at
+ // FrameBase[0], and we can simply insert it after this QH. We
+ // are all done.
+ //
+ ASSERT ((Index == 0) && (Qh->NextQh == NULL));
+
+ Prev = Next;
+ Next = Next->NextQh;
+
+ Qh->NextQh = Next;
+ Prev->NextQh = Qh;
+
+ Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink;
+ Prev->QhHw.HorizonLink = QH_HLINK (Qh, FALSE);
+ break;
+ }
+
+ //
+ // OK, find the right position, insert it in. If Qh's next
+ // link has already been set, it is in position. This is
+ // guarranted by 2^n polling interval.
+ //
+ if (Qh->NextQh == NULL) {
+ Qh->NextQh = Next;
+ Qh->QhHw.HorizonLink = QH_HLINK (Next, FALSE);
+ }
+
+ if (Prev == NULL) {
+ FrameBase[Index] = QH_HLINK (Qh, FALSE);
+ } else {
+ Prev->NextQh = Qh;
+ Prev->QhHw.HorizonLink = QH_HLINK (Qh, FALSE);
+ }
+ }
+}
+
+
+/**
+ Unlink QH from the frame list is easier: find all
+ the precedence node, and pointer there next to QhSw's
+ next.
+
+ @param FrameBase The base address of the frame list
+ @param Qh The queue head to unlink
+
+ @return None
+
+**/
+VOID
+UhciUnlinkQhFromFrameList (
+ UINT32 *FrameBase,
+ UHCI_QH_SW *Qh
+ )
+{
+ UINTN Index;
+ UHCI_QH_SW *Prev;
+ UHCI_QH_SW *This;
+
+ ASSERT ((FrameBase != NULL) && (Qh != NULL));
+
+ for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
+ //
+ // Frame link can't be NULL because we always keep static
+ // queue heads on the frame list
+ //
+ ASSERT (!LINK_TERMINATED (FrameBase[Index]));
+ This = UHCI_ADDR (FrameBase[Index]);
+ Prev = NULL;
+
+ //
+ // Walk through the frame's QH list to find the
+ // queue head to remove
+ //
+ while ((This != NULL) && (This != Qh)) {
+ Prev = This;
+ This = This->NextQh;
+ }
+
+ //
+ // Qh may have already been unlinked from this frame
+ // by early action.
+ //
+ if (This == NULL) {
+ continue;
+ }
+
+ if (Prev == NULL) {
+ //
+ // Qh is the first entry in the frame
+ //
+ FrameBase[Index] = Qh->QhHw.HorizonLink;
+ } else {
+ Prev->NextQh = Qh->NextQh;
+ Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink;
+ }
+ }
+}
+
+
+/**
+ Check TDs Results
+
+ @param Uhc This UHCI device
+ @param Td UHCI_TD_SW to check
+ @param IsLow Is Low Speed Device
+ @param QhResult Return the result of this TD list
+
+ @return Whether the TD's result is finialized.
+
+**/
+STATIC
+BOOLEAN
+UhciCheckTdStatus (
+ IN USB_HC_DEV *Uhc,
+ IN UHCI_TD_SW *Td,
+ IN BOOLEAN IsLow,
+ OUT UHCI_QH_RESULT *QhResult
+ )
+{
+ UINTN Len;
+ UINT8 State;
+ UHCI_TD_HW *TdHw;
+ BOOLEAN Finished;
+
+ Finished = TRUE;
+
+ //
+ // Initialize the data toggle to that of the first
+ // TD. The next toggle to use is either:
+ // 1. first TD's toggle if no TD is executed OK
+ // 2. the next toggle of last executed-OK TD
+ //
+ QhResult->Result = EFI_USB_NOERROR;
+ QhResult->NextToggle = (UINT8)Td->TdHw.DataToggle;
+ QhResult->Complete = 0;
+
+ while (Td != NULL) {
+ TdHw = &Td->TdHw;
+ State = (UINT8)TdHw->Status;
+
+ //
+ // UHCI will set STALLED bit when it abort the execution
+ // of TD list. There are several reasons:
+ // 1. BABBLE error happened
+ // 2. Received a STALL response
+ // 3. Error count decreased to zero.
+ //
+ // It also set CRC/Timeout/NAK/Buffer Error/BitStuff Error
+ // bits when corresponding conditions happen. But these
+ // conditions are not deadly, that is a TD can successfully
+ // completes even these bits are set. But it is likely that
+ // upper layer won't distinguish these condtions. So, only
+ // set these bits when TD is actually halted.
+ //
+ if (State & USBTD_STALLED) {
+ if (State & USBTD_BABBLE) {
+ QhResult->Result |= EFI_USB_ERR_BABBLE;
+
+ } else if (TdHw->ErrorCount != 0) {
+ QhResult->Result |= EFI_USB_ERR_STALL;
+ }
+
+ if (State & USBTD_CRC) {
+ QhResult->Result |= EFI_USB_ERR_CRC;
+ }
+
+ if (State & USBTD_BUFFERR) {
+ QhResult->Result |= EFI_USB_ERR_BUFFER;
+ }
+
+ if (Td->TdHw.Status & USBTD_BITSTUFF) {
+ QhResult->Result |= EFI_USB_ERR_BITSTUFF;
+ }
+
+ if (TdHw->ErrorCount == 0) {
+ QhResult->Result |= EFI_USB_ERR_TIMEOUT;
+ }
+
+ Finished = TRUE;
+ goto ON_EXIT;
+
+ } else if (State & USBTD_ACTIVE) {
+ //
+ // The TD is still active, no need to check further.
+ //
+ QhResult->Result |= EFI_USB_ERR_NOTEXECUTE;
+
+ Finished = FALSE;
+ goto ON_EXIT;
+
+ } else {
+ //
+ // Update the next data toggle, it is always the
+ // next to the last known-good TD's data toggle if
+ // any TD is executed OK
+ //
+ QhResult->NextToggle = 1 - (UINT8)TdHw->DataToggle;
+
+ //
+ // This TD is finished OK or met short packet read. Update the
+ // transfer length if it isn't a SETUP.
+ //
+ Len = (TdHw->ActualLen + 1) & 0x7FF;
+
+ if (TdHw->PidCode != SETUP_PACKET_ID) {
+ QhResult->Complete += Len;
+ }
+
+ //
+ // Short packet condition for full speed input TD, also
+ // terminate the transfer
+ //
+ if (!IsLow && (TdHw->ShortPacket == 1) && (Len < Td->DataLen)) {
+ UHCI_DEBUG (("UhciCheckTdStatus: short packet read occured\n"));
+
+ Finished = TRUE;
+ goto ON_EXIT;
+ }
+ }
+
+ Td = Td->NextTd;
+ }
+
+ON_EXIT:
+ //
+ // Check whether HC is halted. Don't move this up. It must be
+ // called after data toggle is successfully updated.
+ //
+ if (!UhciIsHcWorking (Uhc->PciIo)) {
+ QhResult->Result |= EFI_USB_ERR_SYSTEM;
+ Finished = TRUE;
+ }
+
+ if (Finished) {
+ Uhc->PciIo->Flush (Uhc->PciIo);
+ }
+
+ UhciAckAllInterrupt (Uhc);
+ return Finished;
+}
+
+
+
+/**
+ Check the result of the transfer
+
+ @param Uhc The UHCI device
+ @param Td The first TDs of the transfer
+ @param TimeOut TimeOut value in milliseconds
+ @param IsLow Is Low Speed Device
+ @param QhResult The variable to return result
+
+ @retval EFI_SUCCESS The transfer finished with success
+ @retval EFI_DEVICE_ERROR Transfer failed
+
+**/
+EFI_STATUS
+UhciExecuteTransfer (
+ IN USB_HC_DEV *Uhc,
+ IN UHCI_QH_SW *Qh,
+ IN UHCI_TD_SW *Td,
+ IN UINTN TimeOut,
+ IN BOOLEAN IsLow,
+ OUT UHCI_QH_RESULT *QhResult
+ )
+{
+ UINTN Index;
+ UINTN Delay;
+ BOOLEAN Finished;
+ EFI_STATUS Status;
+
+ Finished = FALSE;
+ Status = EFI_SUCCESS;
+ Delay = (TimeOut * STALL_1_MS / UHC_SYN_POLL) + 1;
+
+ for (Index = 0; Index < Delay; Index++) {
+ Finished = UhciCheckTdStatus (Uhc, Td, IsLow, QhResult);
+
+ //
+ // Transfer is OK or some error occured (TD inactive)
+ //
+ if (Finished) {
+ break;
+ }
+
+ gBS->Stall (UHC_SYN_POLL);
+ }
+
+ if (!Finished) {
+ UHCI_ERROR (("UhciExecuteTransfer: execution not finished for %dms\n", TimeOut));
+ UHCI_DUMP_QH ((Qh));
+ UHCI_DUMP_TDS ((Td));
+
+ Status = EFI_TIMEOUT;
+
+ } else if (QhResult->Result != EFI_USB_NOERROR) {
+ UHCI_ERROR (("UhciExecuteTransfer: execution failed with result %x\n", QhResult->Result));
+ UHCI_DUMP_QH ((Qh));
+ UHCI_DUMP_TDS ((Td));
+
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
+
+/**
+ Update Async Request, QH and TDs
+
+ @param AsyncReq The UHCI asynchronous transfer to update
+ @param Result Transfer reslut
+ @param ErrTdPos Error TD Position
+
+ @return VOID
+
+**/
+STATIC
+VOID
+UhciUpdateAsyncReq (
+ IN UHCI_ASYNC_REQUEST *AsyncReq,
+ IN UINT32 Result,
+ IN UINT32 NextToggle
+ )
+{
+ UHCI_QH_SW *Qh;
+ UHCI_TD_SW *FirstTd;
+ UHCI_TD_SW *Td;
+
+ Qh = AsyncReq->QhSw;
+ FirstTd = AsyncReq->FirstTd;
+
+ if (Result == EFI_USB_NOERROR) {
+ //
+ // The last transfer succeeds. Then we need to update
+ // the Qh and Td for next round of transfer.
+ // 1. Update the TD's data toggle
+ // 2. Activate all the TDs
+ // 3. Link the TD to the queue head again since during
+ // execution, queue head's TD pointer is changed by
+ // hardware.
+ //
+ for (Td = FirstTd; Td != NULL; Td = Td->NextTd) {
+ Td->TdHw.DataToggle = NextToggle;
+ NextToggle ^= 1;
+ Td->TdHw.Status |= USBTD_ACTIVE;
+ }
+
+ UhciLinkTdToQh (Qh, FirstTd);
+ return ;
+ }
+}
+
+
+/**
+ Create Async Request node, and Link to List
+
+ @param Uhc The UHCI device
+ @param Qh The queue head of the transfer
+ @param FirstTd First TD of the transfer
+ @param DevAddr Device Address
+ @param EndPoint EndPoint Address
+ @param DataLen Data length
+ @param Interval Polling Interval when inserted to frame list
+ @param Mapping Mapping value
+ @param Data Data buffer, unmapped
+ @param Callback Callback after interrupt transfeer
+ @param Context Callback Context passed as function parameter
+ @param IsLow Is Low Speed
+
+ @retval EFI_SUCCESS An asynchronous transfer is created
+ @retval EFI_INVALID_PARAMETER Paremeter is error
+ @retval EFI_OUT_OF_RESOURCES Failed because of resource shortage.
+
+**/
+EFI_STATUS
+UhciCreateAsyncReq (
+ IN USB_HC_DEV *Uhc,
+ IN UHCI_QH_SW *Qh,
+ IN UHCI_TD_SW *FirstTd,
+ IN UINT8 DevAddr,
+ IN UINT8 EndPoint,
+ IN UINTN DataLen,
+ IN UINTN Interval,
+ IN VOID *Mapping,
+ IN UINT8 *Data,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
+ IN VOID *Context,
+ IN BOOLEAN IsLow
+ )
+{
+ UHCI_ASYNC_REQUEST *AsyncReq;
+
+ AsyncReq = AllocatePool (sizeof (UHCI_ASYNC_REQUEST));
+
+ if (AsyncReq == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Fill Request field. Data is allocated host memory, not mapped
+ //
+ AsyncReq->Signature = UHCI_ASYNC_INT_SIGNATURE;
+ AsyncReq->DevAddr = DevAddr;
+ AsyncReq->EndPoint = EndPoint;
+ AsyncReq->DataLen = DataLen;
+ AsyncReq->Interval = Interval;
+ AsyncReq->Mapping = Mapping;
+ AsyncReq->Data = Data;
+ AsyncReq->Callback = Callback;
+ AsyncReq->Context = Context;
+ AsyncReq->QhSw = Qh;
+ AsyncReq->FirstTd = FirstTd;
+ AsyncReq->IsLow = IsLow;
+
+ //
+ // Insert the new interrupt transfer to the head of the list.
+ // The interrupt transfer's monitor function scans the whole
+ // list from head to tail. The new interrupt transfer MUST be
+ // added to the head of the list.
+ //
+ InsertHeadList (&(Uhc->AsyncIntList), &(AsyncReq->Link));
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Free an asynchronous request's resource such as memory
+
+ @param Uhc The UHCI device
+ @param AsyncReq The asynchronous request to free
+
+ @return None
+
+**/
+STATIC
+VOID
+UhciFreeAsyncReq (
+ IN USB_HC_DEV *Uhc,
+ IN UHCI_ASYNC_REQUEST *AsyncReq
+ )
+{
+ ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
+
+ UhciDestoryTds (Uhc, AsyncReq->FirstTd);
+ UsbHcFreeMem (Uhc->MemPool, AsyncReq->QhSw, sizeof (UHCI_QH_SW));
+
+ if (AsyncReq->Mapping != NULL) {
+ Uhc->PciIo->Unmap (Uhc->PciIo, AsyncReq->Mapping);
+ }
+
+ if (AsyncReq->Data != NULL) {
+ gBS->FreePool (AsyncReq->Data);
+ }
+
+ gBS->FreePool (AsyncReq);
+}
+
+
+/**
+ Unlink an asynchronous request's from UHC's asynchronus list.
+ also remove the queue head from the frame list. If FreeNow,
+ release its resource also. Otherwise, add the request to the
+ UHC's recycle list to wait for a while before release the memory.
+ Until then, hardware won't hold point to the request.
+
+ @param Uhc The UHCI device
+ @param AsyncReq The asynchronous request to free
+ @param FreeNow If TRUE, free the resource immediately, otherwise
+ add the request to recycle wait list.
+
+ @return None
+
+**/
+STATIC
+VOID
+UhciUnlinkAsyncReq (
+ IN USB_HC_DEV *Uhc,
+ IN UHCI_ASYNC_REQUEST *AsyncReq,
+ IN BOOLEAN FreeNow
+ )
+{
+ ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
+
+ RemoveEntryList (&(AsyncReq->Link));
+ UhciUnlinkQhFromFrameList (Uhc->FrameBase, AsyncReq->QhSw);
+
+ if (FreeNow) {
+ UhciFreeAsyncReq (Uhc, AsyncReq);
+ } else {
+ //
+ // To sychronize with hardware, mark the queue head as inactive
+ // then add AsyncReq to UHC's recycle list
+ //
+ AsyncReq->QhSw->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
+ AsyncReq->Recycle = Uhc->RecycleWait;
+ Uhc->RecycleWait = AsyncReq;
+ }
+}
+
+
+/**
+ Delete Async Interrupt QH and TDs
+
+ @param Uhc The UHCI device
+ @param DevAddr Device Address
+ @param EndPoint EndPoint Address
+ @param Toggle The next data toggle to use
+
+ @retval EFI_SUCCESS The request is deleted
+ @retval EFI_INVALID_PARAMETER Paremeter is error
+ @retval EFI_NOT_FOUND The asynchronous isn't found
+
+**/
+EFI_STATUS
+UhciRemoveAsyncReq (
+ IN USB_HC_DEV *Uhc,
+ IN UINT8 DevAddr,
+ IN UINT8 EndPoint,
+ OUT UINT8 *Toggle
+ )
+{
+ EFI_STATUS Status;
+ UHCI_ASYNC_REQUEST *AsyncReq;
+ UHCI_QH_RESULT QhResult;
+ LIST_ENTRY *Link;
+ BOOLEAN Found;
+
+ Status = EFI_SUCCESS;
+
+ //
+ // If no asynchronous interrupt transaction exists
+ //
+ if (IsListEmpty (&(Uhc->AsyncIntList))) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Find the asynchronous transfer to this device/endpoint pair
+ //
+ Found = FALSE;
+ Link = Uhc->AsyncIntList.ForwardLink;
+
+ do {
+ AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link);
+ Link = Link->ForwardLink;
+
+ if ((AsyncReq->DevAddr == DevAddr) && (AsyncReq->EndPoint == EndPoint)) {
+ Found = TRUE;
+ break;
+ }
+
+ } while (Link != &(Uhc->AsyncIntList));
+
+ if (!Found) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Check the result of the async transfer then update it
+ // to get the next data toggle to use.
+ //
+ UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
+ *Toggle = QhResult.NextToggle;
+
+ //
+ // Don't release the request now, keep it to synchronize with hardware.
+ //
+ UhciUnlinkAsyncReq (Uhc, AsyncReq, FALSE);
+ return Status;
+}
+
+
+/**
+ Recycle the asynchronouse request. When a queue head
+ is unlinked from frame list, host controller hardware
+ may still hold a cached pointer to it. To synchronize
+ with hardware, the request is released in two steps:
+ first it is linked to the UHC's RecycleWait list. At
+ the next time UhciMonitorAsyncReqList is fired, it is
+ moved to UHC's Recylelist. Then, at another timer
+ activation, all the requests on Recycle list is freed.
+ This guarrantes that each unlink queue head keeps
+ existing for at least 50ms, far enough for the hardware
+ to clear its cache.
+
+ @param Uhc The UHCI device
+
+ @return None
+
+**/
+STATIC
+VOID
+UhciRecycleAsyncReq (
+ IN USB_HC_DEV *Uhc
+ )
+{
+ UHCI_ASYNC_REQUEST *Req;
+ UHCI_ASYNC_REQUEST *Next;
+
+ Req = Uhc->Recycle;
+
+ while (Req != NULL) {
+ Next = Req->Recycle;
+ UhciFreeAsyncReq (Uhc, Req);
+ Req = Next;
+ }
+
+ Uhc->Recycle = Uhc->RecycleWait;
+ Uhc->RecycleWait = NULL;
+}
+
+
+
+/**
+ Release all the asynchronous transfers on the lsit.
+
+ @param Uhc The UHCI device
+
+ @return VOID
+
+**/
+VOID
+UhciFreeAllAsyncReq (
+ IN USB_HC_DEV *Uhc
+ )
+{
+ LIST_ENTRY *Head;
+ UHCI_ASYNC_REQUEST *AsyncReq;
+
+ //
+ // Call UhciRecycleAsyncReq twice. The requests on Recycle
+ // will be released at the first call; The requests on
+ // RecycleWait will be released at the second call.
+ //
+ UhciRecycleAsyncReq (Uhc);
+ UhciRecycleAsyncReq (Uhc);
+
+ Head = &(Uhc->AsyncIntList);
+
+ if (IsListEmpty (Head)) {
+ return;
+ }
+
+ while (!IsListEmpty (Head)) {
+ AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Head->ForwardLink);
+ UhciUnlinkAsyncReq (Uhc, AsyncReq, TRUE);
+ }
+}
+
+
+/**
+ Interrupt transfer periodic check handler
+
+ @param Event The event of the time
+ @param Context Context of the event, pointer to USB_HC_DEV
+
+ @return VOID
+
+**/
+VOID
+UhciMonitorAsyncReqList (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UHCI_ASYNC_REQUEST *AsyncReq;
+ LIST_ENTRY *Link;
+ USB_HC_DEV *Uhc;
+ VOID *Data;
+ BOOLEAN Finished;
+ UHCI_QH_RESULT QhResult;
+
+ Uhc = (USB_HC_DEV *) Context;
+
+ //
+ // Recycle the asynchronous requests expired, and promote
+ // requests waiting to be recycled the next time when this
+ // timer expires
+ //
+ UhciRecycleAsyncReq (Uhc);
+
+ if (IsListEmpty (&(Uhc->AsyncIntList))) {
+ return ;
+ }
+
+ //
+ // This loop must be delete safe
+ //
+ Link = Uhc->AsyncIntList.ForwardLink;
+
+ do {
+ AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link);
+ Link = Link->ForwardLink;
+
+ Finished = UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
+
+ if (!Finished) {
+ continue;
+ }
+
+ //
+ // Copy the data to temporary buffer if there are some
+ // data transferred. We may have zero-length packet
+ //
+ Data = NULL;
+
+ if (QhResult.Complete != 0) {
+ Data = AllocatePool (QhResult.Complete);
+
+ if (Data == NULL) {
+ return ;
+ }
+
+ CopyMem (Data, AsyncReq->FirstTd->Data, QhResult.Complete);
+ }
+
+ UhciUpdateAsyncReq (AsyncReq, QhResult.Result, QhResult.NextToggle);
+
+ //
+ // Now, either transfer is SUCCESS or met errors since
+ // we have skipped to next transfer earlier if current
+ // transfer is still active.
+ //
+ if (QhResult.Result == EFI_USB_NOERROR) {
+ AsyncReq->Callback (Data, QhResult.Complete, AsyncReq->Context, QhResult.Result);
+ } else {
+ //
+ // Leave error recovery to its related device driver.
+ // A common case of the error recovery is to re-submit
+ // the interrupt transfer. When an interrupt transfer
+ // is re-submitted, its position in the linked list is
+ // changed. It is inserted to the head of the linked
+ // list, while this function scans the whole list from
+ // head to tail. Thus, the re-submitted interrupt transfer's
+ // callback function will not be called again in this round.
+ //
+ AsyncReq->Callback (NULL, 0, AsyncReq->Context, QhResult.Result);
+ }
+
+ if (Data != NULL) {
+ gBS->FreePool (Data);
+ }
+ } while (Link != &(Uhc->AsyncIntList));
+}
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.h b/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.h new file mode 100644 index 0000000000..cba65c661b --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.h @@ -0,0 +1,305 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ UhciSched.h
+
+Abstract:
+
+ The definition for EHCI register operation routines.
+
+Revision History
+
+
+**/
+
+#ifndef _EFI_UHCI_SCHED_H_
+#define _EFI_UHCI_SCHED_H_
+
+
+enum {
+ UHCI_ASYNC_INT_SIGNATURE = EFI_SIGNATURE_32 ('u', 'h', 'c', 'a'),
+
+ //
+ // The failure mask for USB transfer return status. If any of
+ // these bit is set, the transfer failed. EFI_USB_ERR_NOEXECUTE
+ // and EFI_USB_ERR_NAK are not considered as error condition:
+ // the transfer is still going on.
+ //
+ USB_ERR_FAIL_MASK = EFI_USB_ERR_STALL | EFI_USB_ERR_BUFFER |
+ EFI_USB_ERR_BABBLE | EFI_USB_ERR_CRC |
+ EFI_USB_ERR_TIMEOUT | EFI_USB_ERR_BITSTUFF |
+ EFI_USB_ERR_SYSTEM,
+
+};
+
+//
+// Structure to return the result of UHCI QH execution.
+// Result is the final result of the QH's QTD. NextToggle
+// is the next data toggle to use. Complete is the actual
+// length of data transferred.
+//
+typedef struct {
+ UINT32 Result;
+ UINT8 NextToggle;
+ UINTN Complete;
+} UHCI_QH_RESULT;
+
+typedef struct _UHCI_ASYNC_REQUEST UHCI_ASYNC_REQUEST;
+
+//
+// Structure used to manager the asynchronous interrupt transfers.
+//
+typedef struct _UHCI_ASYNC_REQUEST{
+ UINTN Signature;
+ LIST_ENTRY Link;
+ UHCI_ASYNC_REQUEST *Recycle;
+
+ //
+ // Endpoint attributes
+ //
+ UINT8 DevAddr;
+ UINT8 EndPoint;
+ BOOLEAN IsLow;
+ UINTN Interval;
+
+ //
+ // Data and UHC structures
+ //
+ UHCI_QH_SW *QhSw;
+ UHCI_TD_SW *FirstTd;
+ UINT8 *Data; // Allocated host memory, not mapped memory
+ UINTN DataLen;
+ VOID *Mapping;
+
+ //
+ // User callback and its context
+ //
+ EFI_ASYNC_USB_TRANSFER_CALLBACK Callback;
+ VOID *Context;
+} UHCI_ASYNC_REQUEST;
+
+#define UHCI_ASYNC_INT_FROM_LINK(a) \
+ CR (a, UHCI_ASYNC_REQUEST, Link, UHCI_ASYNC_INT_SIGNATURE)
+
+EFI_STATUS
+UhciInitFrameList (
+ IN USB_HC_DEV *Uhc
+ )
+/*++
+
+Routine Description:
+
+ Create Frame List Structure
+
+Arguments:
+
+ Uhc - UHCI device
+
+Returns:
+
+ EFI_OUT_OF_RESOURCES - Can't allocate memory resources
+ EFI_UNSUPPORTED - Map memory fail
+ EFI_SUCCESS - Success
+
+--*/
+;
+
+
+/**
+ Destory FrameList buffer
+
+ @param Uhc The UHCI device
+
+ @return VOID
+
+**/
+VOID
+UhciDestoryFrameList (
+ IN USB_HC_DEV *Uhc
+ )
+;
+
+
+/**
+ Convert the poll rate to the maxium 2^n that is smaller
+ than Interval
+
+ @param Interval The poll rate to convert
+
+ @return The converted poll rate
+
+**/
+UINTN
+UhciConvertPollRate (
+ IN UINTN Interval
+ )
+;
+
+
+/**
+ Link a queue head (for asynchronous interrupt transfer) to
+ the frame list.
+
+ @param FrameBase The base of the frame list
+ @param Qh The queue head to link into
+
+ @return None
+
+**/
+VOID
+UhciLinkQhToFrameList (
+ UINT32 *FrameBase,
+ UHCI_QH_SW *Qh
+ )
+;
+
+
+/**
+ Unlink QH from the frame list is easier: find all
+ the precedence node, and pointer there next to QhSw's
+ next.
+
+ @param FrameBase The base address of the frame list
+ @param Qh The queue head to unlink
+
+ @return None
+
+**/
+VOID
+UhciUnlinkQhFromFrameList (
+ UINT32 *FrameBase,
+ UHCI_QH_SW *Qh
+ )
+;
+
+
+/**
+ Check the result of the transfer
+
+ @param Uhc The UHCI device
+ @param Td The first TDs of the transfer
+ @param TimeOut TimeOut value in milliseconds
+ @param IsLow Is Low Speed Device
+ @param QhResult The variable to return result
+
+ @retval EFI_SUCCESS The transfer finished with success
+ @retval EFI_DEVICE_ERROR Transfer failed
+
+**/
+EFI_STATUS
+UhciExecuteTransfer (
+ IN USB_HC_DEV *Uhc,
+ IN UHCI_QH_SW *Qh,
+ IN UHCI_TD_SW *Td,
+ IN UINTN TimeOut,
+ IN BOOLEAN IsLow,
+ OUT UHCI_QH_RESULT *QhResult
+ )
+;
+
+
+/**
+ Create Async Request node, and Link to List
+
+ @param Uhc The UHCI device
+ @param Qh The queue head of the transfer
+ @param FirstTd First TD of the transfer
+ @param DevAddr Device Address
+ @param EndPoint EndPoint Address
+ @param Toggle Data Toggle
+ @param DataLen Data length
+ @param Interval Polling Interval when inserted to frame list
+ @param Mapping Mapping value
+ @param Data Data buffer, unmapped
+ @param Callback Callback after interrupt transfeer
+ @param Context Callback Context passed as function parameter
+ @param IsLow Is Low Speed
+
+ @retval EFI_SUCCESS An asynchronous transfer is created
+ @retval EFI_INVALID_PARAMETER Paremeter is error
+ @retval EFI_OUT_OF_RESOURCES Failed because of resource shortage.
+
+**/
+EFI_STATUS
+UhciCreateAsyncReq (
+ IN USB_HC_DEV *Uhc,
+ IN UHCI_QH_SW *Qh,
+ IN UHCI_TD_SW *FirstTd,
+ IN UINT8 DevAddr,
+ IN UINT8 EndPoint,
+ IN UINTN DataLen,
+ IN UINTN Interval,
+ IN VOID *Mapping,
+ IN UINT8 *Data,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
+ IN VOID *Context,
+ IN BOOLEAN IsLow
+ )
+;
+
+
+/**
+ Delete Async Interrupt QH and TDs
+
+ @param Uhc The UHCI device
+ @param DevAddr Device Address
+ @param EndPoint EndPoint Address
+ @param Toggle The next data toggle to use
+
+ @retval EFI_SUCCESS The request is deleted
+ @retval EFI_INVALID_PARAMETER Paremeter is error
+ @retval EFI_NOT_FOUND The asynchronous isn't found
+
+**/
+EFI_STATUS
+UhciRemoveAsyncReq (
+ IN USB_HC_DEV *Uhc,
+ IN UINT8 DevAddr,
+ IN UINT8 EndPoint,
+ OUT UINT8 *Toggle
+ )
+;
+
+
+/**
+ Release all the asynchronous transfers on the lsit.
+
+ @param Uhc The UHCI device
+
+ @return VOID
+
+**/
+VOID
+UhciFreeAllAsyncReq (
+ IN USB_HC_DEV *Uhc
+ )
+;
+
+
+/**
+ Interrupt transfer periodic check handler
+
+ @param Event The event of the time
+ @param Context Context of the event, pointer to USB_HC_DEV
+
+ @return VOID
+
+**/
+VOID
+UhciMonitorAsyncReqList (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+;
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.c b/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.c new file mode 100644 index 0000000000..32082b15bf --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.c @@ -0,0 +1,548 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciMem.c
+
+Abstract:
+
+
+Revision History
+
+**/
+
+#include "Uhci.h"
+
+
+UINTN mUsbHcDebugLevel = DEBUG_INFO;
+
+
+/**
+ Allocate a block of memory to be used by the buffer pool
+
+ @param Pool The buffer pool to allocate memory for
+ @param Pages How many pages to allocate
+
+ @return The allocated memory block or NULL if failed
+
+**/
+STATIC
+USBHC_MEM_BLOCK *
+UsbHcAllocMemBlock (
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Pages
+ )
+{
+ USBHC_MEM_BLOCK *Block;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ VOID *BufHost;
+ VOID *Mapping;
+ EFI_PHYSICAL_ADDRESS MappedAddr;
+ UINTN Bytes;
+ EFI_STATUS Status;
+
+ PciIo = Pool->PciIo;
+
+ Block = AllocateZeroPool (sizeof (USBHC_MEM_BLOCK));
+ if (Block == NULL) {
+ return NULL;
+ }
+
+ //
+ // each bit in the bit array represents USBHC_MEM_UNIT
+ // bytes of memory in the memory block.
+ //
+ ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE);
+
+ Block->BufLen = EFI_PAGES_TO_SIZE (Pages);
+ Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8);
+ Block->Bits = AllocateZeroPool (Block->BitsLen);
+
+ if (Block->Bits == NULL) {
+ gBS->FreePool (Block);
+ return NULL;
+ }
+
+ //
+ // Allocate the number of Pages of memory, then map it for
+ // bus master read and write.
+ //
+ Status = PciIo->AllocateBuffer (
+ PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ Pages,
+ &BufHost,
+ 0
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_BITARRAY;
+ }
+
+ Bytes = EFI_PAGES_TO_SIZE (Pages);
+ Status = PciIo->Map (
+ PciIo,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ BufHost,
+ &Bytes,
+ &MappedAddr,
+ &Mapping
+ );
+
+ if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (Pages))) {
+ goto FREE_BUFFER;
+ }
+
+ //
+ // Check whether the data structure used by the host controller
+ // should be restricted into the same 4G
+ //
+ if (Pool->Check4G && (Pool->Which4G != USB_HC_HIGH_32BIT (MappedAddr))) {
+ PciIo->Unmap (PciIo, Mapping);
+ goto FREE_BUFFER;
+ }
+
+ Block->BufHost = BufHost;
+ Block->Buf = (UINT8 *) ((UINTN) MappedAddr);
+ Block->Mapping = Mapping;
+
+ DEBUG ((mUsbHcDebugLevel, "UsbHcAllocMemBlock: block %x created with buffer %x\n",
+ Block, Block->Buf));
+
+ return Block;
+
+FREE_BUFFER:
+ PciIo->FreeBuffer (PciIo, Pages, BufHost);
+
+FREE_BITARRAY:
+ gBS->FreePool (Block->Bits);
+ gBS->FreePool (Block);
+ return NULL;
+}
+
+
+/**
+ Free the memory block from the memory pool
+
+ @param Pool The memory pool to free the block from
+ @param Block The memory block to free
+
+ @return VOID
+
+**/
+STATIC
+VOID
+UsbHcFreeMemBlock (
+ IN USBHC_MEM_POOL *Pool,
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ ASSERT ((Pool != NULL) && (Block != NULL));
+
+ PciIo = Pool->PciIo;
+
+ //
+ // Unmap the common buffer then free the structures
+ //
+ PciIo->Unmap (PciIo, Block->Mapping);
+ PciIo->FreeBuffer (PciIo, EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost);
+
+ gBS->FreePool (Block->Bits);
+ gBS->FreePool (Block);
+}
+
+
+/**
+ Alloc some memory from the block
+
+ @param Block The memory block to allocate memory from
+ @param Mem The variable to store the memory allocated
+ @param Units Number of memory units to allocate
+
+ @return EFI_SUCCESS : The needed memory is allocated
+ @return EFI_NOT_FOUND : Can't find the free memory
+
+**/
+STATIC
+VOID *
+UsbHcAllocMemFromBlock (
+ IN USBHC_MEM_BLOCK *Block,
+ IN UINTN Units
+ )
+{
+ UINTN Byte;
+ UINT8 Bit;
+ UINTN StartByte;
+ UINT8 StartBit;
+ UINTN Available;
+ UINTN Count;
+
+ ASSERT ((Block != 0) && (Units != 0));
+
+ StartByte = 0;
+ StartBit = 0;
+ Available = 0;
+
+ for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {
+ //
+ // If current bit is zero, the corresponding memory unit is
+ // available, otherwise we need to restart our searching.
+ // Available counts the consective number of zero bit.
+ //
+ if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) {
+ Available++;
+
+ if (Available >= Units) {
+ break;
+ }
+
+ NEXT_BIT (Byte, Bit);
+
+ } else {
+ NEXT_BIT (Byte, Bit);
+
+ Available = 0;
+ StartByte = Byte;
+ StartBit = Bit;
+ }
+ }
+
+ if (Available < Units) {
+ return NULL;
+ }
+
+ //
+ // Mark the memory as allocated
+ //
+ Byte = StartByte;
+ Bit = StartBit;
+
+ for (Count = 0; Count < Units; Count++) {
+ ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
+
+ Block->Bits[Byte] |= USB_HC_BIT (Bit);
+ NEXT_BIT (Byte, Bit);
+ }
+
+ return Block->Buf + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT;
+}
+
+
+/**
+ Insert the memory block to the pool's list of the blocks
+
+ @param Head The head of the memory pool's block list
+ @param Block The memory block to insert
+
+ @return VOID
+
+**/
+STATIC
+VOID
+UsbHcInsertMemBlockToPool (
+ IN USBHC_MEM_BLOCK *Head,
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ ASSERT ((Head != NULL) && (Block != NULL));
+ Block->Next = Head->Next;
+ Head->Next = Block;
+}
+
+
+/**
+ Is the memory block empty?
+
+ @param Block The memory block to check
+
+ @return TRUE : The memory block is empty
+ @return FALSE : The memory block isn't empty
+
+**/
+STATIC
+BOOLEAN
+UsbHcIsMemBlockEmpty (
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < Block->BitsLen; Index++) {
+ if (Block->Bits[Index] != 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Unlink the memory block from the pool's list
+
+ @param Head The block list head of the memory's pool
+ @param BlockToUnlink The memory block to unlink.
+
+ @return VOID
+
+**/
+STATIC
+VOID
+UsbHcUnlinkMemBlock (
+ IN USBHC_MEM_BLOCK *Head,
+ IN USBHC_MEM_BLOCK *BlockToUnlink
+ )
+{
+ USBHC_MEM_BLOCK *Block;
+
+ ASSERT ((Head != NULL) && (BlockToUnlink != NULL));
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ if (Block->Next == BlockToUnlink) {
+ Block->Next = BlockToUnlink->Next;
+ BlockToUnlink->Next = NULL;
+ break;
+ }
+ }
+}
+
+
+/**
+ Initialize the memory management pool for the host controller
+
+ @param Pool The USB memory pool to initialize
+ @param PciIo The PciIo that can be used to access the host controller
+ @param Check4G Whether the host controller requires allocated memory
+ from one 4G address space.
+ @param Which4G The 4G memory area each memory allocated should be from
+
+ @return EFI_SUCCESS : The memory pool is initialized
+ @return EFI_OUT_OF_RESOURCE : Fail to init the memory pool
+
+**/
+USBHC_MEM_POOL *
+UsbHcInitMemPool (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN BOOLEAN Check4G,
+ IN UINT32 Which4G
+ )
+{
+ USBHC_MEM_POOL *Pool;
+
+ Pool = AllocatePool (sizeof (USBHC_MEM_POOL));
+
+ if (Pool == NULL) {
+ return Pool;
+ }
+
+ Pool->PciIo = PciIo;
+ Pool->Check4G = Check4G;
+ Pool->Which4G = Which4G;
+ Pool->Head = UsbHcAllocMemBlock (Pool, USBHC_MEM_DEFAULT_PAGES);
+
+ if (Pool->Head == NULL) {
+ gBS->FreePool (Pool);
+ Pool = NULL;
+ }
+
+ return Pool;
+}
+
+
+/**
+ Release the memory management pool
+
+ @param Pool The USB memory pool to free
+
+ @return EFI_SUCCESS : The memory pool is freed
+ @return EFI_DEVICE_ERROR : Failed to free the memory pool
+
+**/
+EFI_STATUS
+UsbHcFreeMemPool (
+ IN USBHC_MEM_POOL *Pool
+ )
+{
+ USBHC_MEM_BLOCK *Block;
+
+ ASSERT (Pool->Head != NULL);
+
+ //
+ // Unlink all the memory blocks from the pool, then free them.
+ // UsbHcUnlinkMemBlock can't be used to unlink and free the
+ // first block.
+ //
+ for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) {
+ UsbHcUnlinkMemBlock (Pool->Head, Block);
+ UsbHcFreeMemBlock (Pool, Block);
+ }
+
+ UsbHcFreeMemBlock (Pool, Pool->Head);
+ gBS->FreePool (Pool);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Allocate some memory from the host controller's memory pool
+ which can be used to communicate with host controller.
+
+ @param Pool The host controller's memory pool
+ @param Size Size of the memory to allocate
+
+ @return The allocated memory or NULL
+
+**/
+VOID *
+UsbHcAllocateMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Size
+ )
+{
+ USBHC_MEM_BLOCK *Head;
+ USBHC_MEM_BLOCK *Block;
+ USBHC_MEM_BLOCK *NewBlock;
+ VOID *Mem;
+ UINTN AllocSize;
+ UINTN Pages;
+
+ Mem = NULL;
+ AllocSize = USBHC_MEM_ROUND (Size);
+ Head = Pool->Head;
+ ASSERT (Head != NULL);
+
+ //
+ // First check whether current memory blocks can satisfy the allocation.
+ //
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT);
+
+ if (Mem != NULL) {
+ ZeroMem (Mem, Size);
+ break;
+ }
+ }
+
+ if (Mem != NULL) {
+ return Mem;
+ }
+
+ //
+ // Create a new memory block if there is not enough memory
+ // in the pool. If the allocation size is larger than the
+ // default page number, just allocate a large enough memory
+ // block. Otherwise allocate default pages.
+ //
+ if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) {
+ Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;
+ } else {
+ Pages = USBHC_MEM_DEFAULT_PAGES;
+ }
+
+ NewBlock = UsbHcAllocMemBlock (Pool, Pages);
+
+ if (NewBlock == NULL) {
+ DEBUG ((mUsbHcDebugLevel, "UsbHcAllocateMem: failed to allocate block\n"));
+ return NULL;
+ }
+
+ //
+ // Add the new memory block to the pool, then allocate memory from it
+ //
+ UsbHcInsertMemBlockToPool (Head, NewBlock);
+ Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT);
+
+ if (Mem != NULL) {
+ ZeroMem (Mem, Size);
+ }
+
+ return Mem;
+}
+
+
+/**
+ Free the allocated memory back to the memory pool
+
+ @param Pool The memory pool of the host controller
+ @param Mem The memory to free
+ @param Size The size of the memory to free
+
+ @return VOID
+
+**/
+VOID
+UsbHcFreeMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ )
+{
+ USBHC_MEM_BLOCK *Head;
+ USBHC_MEM_BLOCK *Block;
+ UINT8 *ToFree;
+ UINTN AllocSize;
+ UINTN Byte;
+ UINTN Bit;
+ UINTN Count;
+
+ Head = Pool->Head;
+ AllocSize = USBHC_MEM_ROUND (Size);
+ ToFree = (UINT8 *) Mem;
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ //
+ // scan the memory block list for the memory block that
+ // completely contains the memory to free.
+ //
+ if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) {
+ //
+ // compute the start byte and bit in the bit array
+ //
+ Byte = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) / 8;
+ Bit = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) % 8;
+
+ //
+ // reset associated bits in bit arry
+ //
+ for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) {
+ ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
+
+ Block->Bits[Byte] ^= (UINT8) USB_HC_BIT (Bit);
+ NEXT_BIT (Byte, Bit);
+ }
+
+ break;
+ }
+ }
+
+ //
+ // If Block == NULL, it means that the current memory isn't
+ // in the host controller's pool. This is critical because
+ // the caller has passed in a wrong memory point
+ //
+ ASSERT (Block != NULL);
+
+ //
+ // Release the current memory block if it is empty and not the head
+ //
+ if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) {
+ DEBUG ((mUsbHcDebugLevel, "UsbHcFreeMem: block %x is empty, recycle\n", Block));
+
+ UsbHcUnlinkMemBlock (Head, Block);
+ UsbHcFreeMemBlock (Pool, Block);
+ }
+
+ return ;
+}
diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.h b/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.h new file mode 100644 index 0000000000..df9ab0ec38 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.h @@ -0,0 +1,167 @@ +/** @file
+
+Copyright (c) 2007, 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:
+
+ EhciMem.h
+
+Abstract:
+
+ This file contains the definination for host controller memory management routines
+
+Revision History
+
+**/
+
+#ifndef _EFI_EHCI_MEM_H_
+#define _EFI_EHCI_MEM_H_
+
+#include <IndustryStandard/Pci22.h>
+
+#define USB_HC_BIT(a) ((UINTN)(1 << (a)))
+
+#define USB_HC_BIT_IS_SET(Data, Bit) \
+ ((BOOLEAN)(((Data) & USB_HC_BIT(Bit)) == USB_HC_BIT(Bit)))
+
+#define USB_HC_HIGH_32BIT(Addr64) \
+ ((UINT32)(RShiftU64((UINTN)(Addr64), 32) & 0XFFFFFFFF))
+
+typedef struct _USBHC_MEM_BLOCK USBHC_MEM_BLOCK;
+
+typedef struct _USBHC_MEM_BLOCK {
+ UINT8 *Bits; // Bit array to record which unit is allocated
+ UINTN BitsLen;
+ UINT8 *Buf;
+ UINT8 *BufHost;
+ UINTN BufLen; // Memory size in bytes
+ VOID *Mapping;
+ USBHC_MEM_BLOCK *Next;
+} USBHC_MEM_BLOCK;
+
+//
+// USBHC_MEM_POOL is used to manage the memory used by USB
+// host controller. EHCI requires the control memory and transfer
+// data to be on the same 4G memory.
+//
+typedef struct _USBHC_MEM_POOL {
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ BOOLEAN Check4G;
+ UINT32 Which4G;
+ USBHC_MEM_BLOCK *Head;
+} USBHC_MEM_POOL;
+
+enum {
+ USBHC_MEM_UNIT = 64, // Memory allocation unit, must be 2^n, n>4
+
+ USBHC_MEM_UNIT_MASK = USBHC_MEM_UNIT - 1,
+ USBHC_MEM_DEFAULT_PAGES = 16,
+};
+
+#define USBHC_MEM_ROUND(Len) (((Len) + USBHC_MEM_UNIT_MASK) & (~USBHC_MEM_UNIT_MASK))
+
+//
+// Advance the byte and bit to the next bit, adjust byte accordingly.
+//
+#define NEXT_BIT(Byte, Bit) \
+ do { \
+ (Bit)++; \
+ if ((Bit) > 7) { \
+ (Byte)++; \
+ (Bit) = 0; \
+ } \
+ } while (0)
+
+
+
+USBHC_MEM_POOL *
+UsbHcInitMemPool (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN BOOLEAN Check4G,
+ IN UINT32 Which4G
+ )
+/*++
+
+Routine Description:
+
+ Initialize the memory management pool for the host controller
+
+Arguments:
+
+ Pool - The USB memory pool to initialize
+ PciIo - The PciIo that can be used to access the host controller
+ Check4G - Whether the host controller requires allocated memory
+ from one 4G address space.
+ Which4G - The 4G memory area each memory allocated should be from
+
+Returns:
+
+ EFI_SUCCESS : The memory pool is initialized
+ EFI_OUT_OF_RESOURCE : Fail to init the memory pool
+
+--*/
+;
+
+
+
+/**
+ Release the memory management pool
+
+ @param Pool The USB memory pool to free
+
+ @return EFI_SUCCESS : The memory pool is freed
+ @return EFI_DEVICE_ERROR : Failed to free the memory pool
+
+**/
+EFI_STATUS
+UsbHcFreeMemPool (
+ IN USBHC_MEM_POOL *Pool
+ )
+;
+
+
+
+/**
+ Allocate some memory from the host controller's memory pool
+ which can be used to communicate with host controller.
+
+ @param Pool The host controller's memory pool
+ @param Size Size of the memory to allocate
+
+ @return The allocated memory or NULL
+
+**/
+VOID *
+UsbHcAllocateMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Size
+ )
+;
+
+
+
+/**
+ Free the allocated memory back to the memory pool
+
+ @param Pool The memory pool of the host controller
+ @param Mem The memory to free
+ @param Size The size of the memory to free
+
+ @return VOID
+
+**/
+VOID
+UsbHcFreeMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ )
+;
+#endif
|