Import EhciDxe and UhciDxe into MdeModulePkg.

git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3191 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
vanjeff 2007-07-11 06:46:38 +00:00
parent 20b1aab609
commit 913cb9dc64
30 changed files with 13427 additions and 0 deletions

View File

@ -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
);
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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 ;
}

View File

@ -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

View File

@ -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
);
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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
);
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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 ;
}

View File

@ -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