MdeModulePkg: Add PEI USB drivers and related PPIs

Signed-off-by: jljusten
Reviewed-by: mdkinney
Reviewed-by: geekboy15a

git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11901 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
jljusten
2011-06-27 23:30:55 +00:00
parent 366f81a016
commit 4b1bf81c20
36 changed files with 15879 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,227 @@
/** @file
Private Header file for Usb Host Controller PEIM
Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
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.
**/
#ifndef _RECOVERY_EHC_H_
#define _RECOVERY_EHC_H_
#include <PiPei.h>
#include <Ppi/UsbController.h>
#include <Ppi/Usb2HostController.h>
#include <Library/DebugLib.h>
#include <Library/PeimEntryPoint.h>
#include <Library/PeiServicesLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/TimerLib.h>
#include <Library/IoLib.h>
typedef struct _PEI_USB2_HC_DEV PEI_USB2_HC_DEV;
#define EFI_LIST_ENTRY LIST_ENTRY
#include "UsbHcMem.h"
#include "EhciReg.h"
#include "EhciUrb.h"
#include "EhciSched.h"
#define EFI_USB_SPEED_FULL 0x0000
#define EFI_USB_SPEED_LOW 0x0001
#define EFI_USB_SPEED_HIGH 0x0002
#define PAGESIZE 4096
#define EHC_1_MICROSECOND 1
#define EHC_1_MILLISECOND (1000 * EHC_1_MICROSECOND)
#define EHC_1_SECOND (1000 * EHC_1_MILLISECOND)
//
// EHCI register operation timeout, set by experience
//
#define EHC_RESET_TIMEOUT (1 * EHC_1_SECOND)
#define EHC_GENERIC_TIMEOUT (10 * EHC_1_MILLISECOND)
//
// Wait for roothub port power stable, refers to Spec[EHCI1.0-2.3.9]
//
#define EHC_ROOT_PORT_RECOVERY_STALL (20 * EHC_1_MILLISECOND)
//
// Sync and Async transfer polling interval, set by experience,
// and the unit of Async is 100us, means 50ms as interval.
//
#define EHC_SYNC_POLL_INTERVAL (6 * EHC_1_MILLISECOND)
#define EHC_ASYNC_POLL_INTERVAL (50 * 10000U)
//
//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) BASE_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 USB2_HC_DEV_SIGNATURE SIGNATURE_32 ('e', 'h', 'c', 'i')
struct _PEI_USB2_HC_DEV {
UINTN Signature;
PEI_USB2_HOST_CONTROLLER_PPI Usb2HostControllerPpi;
EFI_PEI_PPI_DESCRIPTOR PpiDescriptor;
UINT32 UsbHostControllerBaseAddress;
PEI_URB *Urb;
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.
//
PEI_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.
//
PEI_EHC_QH *ReclaimHead;
//
// Peroidic (interrupt) transfer schedule data:
//
VOID *PeriodFrame; // Mapped as common buffer
VOID *PeriodFrameHost;
VOID *PeriodFrameMap;
PEI_EHC_QH *PeriodOne;
EFI_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;
};
#define PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS(a) CR (a, PEI_USB2_HC_DEV, Usb2HostControllerPpi, USB2_HC_DEV_SIGNATURE)
/**
@param EhcDev EHCI Device.
@retval EFI_SUCCESS EHCI successfully initialized.
@retval EFI_ABORTED EHCI was failed to be initialized.
**/
EFI_STATUS
InitializeUsbHC (
IN PEI_USB2_HC_DEV *EhcDev
);
/**
Initialize the memory management pool for the host controller.
@param Ehc The EHCI device.
@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.
@retval EFI_SUCCESS The memory pool is initialized.
@retval EFI_OUT_OF_RESOURCE Fail to init the memory pool.
**/
USBHC_MEM_POOL *
UsbHcInitMemPool (
IN PEI_USB2_HC_DEV *Ehc,
IN BOOLEAN Check4G,
IN UINT32 Which4G
)
;
/**
Release the memory management pool.
@param Pool The USB memory pool to free.
@retval EFI_DEVICE_ERROR Fail to free the memory pool.
@retval EFI_SUCCESS The memory pool is freed.
**/
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 Ehc The EHCI device.
@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 PEI_USB2_HC_DEV *Ehc,
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.
**/
VOID
UsbHcFreeMem (
IN USBHC_MEM_POOL *Pool,
IN VOID *Mem,
IN UINTN Size
)
;
#endif

View File

@@ -0,0 +1,66 @@
## @file
# Component description file for EhcPeim PEIM to produce gPeiUsb2HostControllerPpiGuid
# based on gPeiUsbControllerPpiGuid which is used to enable recovery function from USB Drivers.
#
# Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
#
# 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]
INF_VERSION = 0x00010005
BASE_NAME = EhciPei
FILE_GUID = BAB4F20F-0981-4b5f-A047-6EF83BEEAB3C
MODULE_TYPE = PEIM
VERSION_STRING = 1.0
ENTRY_POINT = EhcPeimEntry
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 IPF EBC
#
[Sources]
EhcPeim.c
EhcPeim.h
EhciUrb.c
EhciSched.c
UsbHcMem.c
EhciReg.h
EhciSched.h
EhciUrb.h
UsbHcMem.h
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
IoLib
TimerLib
BaseMemoryLib
PeimEntryPoint
PeiServicesLib
[Ppis]
gPeiUsb2HostControllerPpiGuid # PPI ALWAYS_PRODUCED
gPeiUsbControllerPpiGuid # PPI ALWAYS_CONSUMED
[Depex]
gEfiPeiMemoryDiscoveredPpiGuid AND gPeiUsbControllerPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid

View File

@@ -0,0 +1,310 @@
/** @file
Private Header file for Usb Host Controller PEIM
Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
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.
**/
#ifndef _EFI_EHCI_REG_H_
#define _EFI_EHCI_REG_H_
//
// Capability register offset
//
#define EHC_CAPLENGTH_OFFSET 0 // Capability register length offset
#define EHC_HCSPARAMS_OFFSET 0x04 // Structural Parameters 04-07h
#define EHC_HCCPARAMS_OFFSET 0x08 // Capability parameters offset
//
// Capability register bit definition
//
#define HCSP_NPORTS 0x0F // Number of root hub port
#define HCCP_64BIT 0x01 // 64-bit addressing capability
//
// Operational register offset
//
#define EHC_USBCMD_OFFSET 0x0 // USB command register offset
#define EHC_USBSTS_OFFSET 0x04 // Statue register offset
#define EHC_USBINTR_OFFSET 0x08 // USB interrutp offset
#define EHC_FRINDEX_OFFSET 0x0C // Frame index offset
#define EHC_CTRLDSSEG_OFFSET 0x10 // Control data structure segment offset
#define EHC_FRAME_BASE_OFFSET 0x14 // Frame list base address offset
#define EHC_ASYNC_HEAD_OFFSET 0x18 // Next asynchronous list address offset
#define EHC_CONFIG_FLAG_OFFSET 0x40 // Configure flag register offset
#define EHC_PORT_STAT_OFFSET 0x44 // Port status/control offset
#define EHC_FRAME_LEN 1024
//
// Register bit definition
//
#define CONFIGFLAG_ROUTE_EHC 0x01 // Route port to EHC
#define USBCMD_RUN 0x01 // Run/stop
#define USBCMD_RESET 0x02 // Start the host controller reset
#define USBCMD_ENABLE_PERIOD 0x10 // Enable periodic schedule
#define USBCMD_ENABLE_ASYNC 0x20 // Enable asynchronous schedule
#define USBCMD_IAAD 0x40 // Interrupt on async advance doorbell
#define USBSTS_IAA 0x20 // Interrupt on async advance
#define USBSTS_PERIOD_ENABLED 0x4000 // Periodic schedule status
#define USBSTS_ASYNC_ENABLED 0x8000 // Asynchronous schedule status
#define USBSTS_HALT 0x1000 // Host controller halted
#define USBSTS_SYS_ERROR 0x10 // Host system error
#define USBSTS_INTACK_MASK 0x003F // Mask for the interrupt ACK, the WC
// (write clean) bits in USBSTS register
#define PORTSC_CONN 0x01 // Current Connect Status
#define PORTSC_CONN_CHANGE 0x02 // Connect Status Change
#define PORTSC_ENABLED 0x04 // Port Enable / Disable
#define PORTSC_ENABLE_CHANGE 0x08 // Port Enable / Disable Change
#define PORTSC_OVERCUR 0x10 // Over current Active
#define PORTSC_OVERCUR_CHANGE 0x20 // Over current Change
#define PORSTSC_RESUME 0x40 // Force Port Resume
#define PORTSC_SUSPEND 0x80 // Port Suspend State
#define PORTSC_RESET 0x100 // Port Reset
#define PORTSC_LINESTATE_K 0x400 // Line Status K-state
#define PORTSC_LINESTATE_J 0x800 // Line Status J-state
#define PORTSC_POWER 0x1000 // Port Power
#define PORTSC_OWNER 0x2000 // Port Owner
#define PORTSC_CHANGE_MASK 0x2A // Mask of the port change bits,
// they are WC (write clean)
//
// PCI Configuration Registers
//
#define 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()
/**
Read EHCI capability register.
@param Ehc The EHCI device.
@param Offset Capability register address.
@retval the register content read.
**/
UINT32
EhcReadCapRegister (
IN PEI_USB2_HC_DEV *Ehc,
IN UINT32 Offset
)
;
/**
Read Ehc Operation register.
@param Ehc The EHCI device.
@param Offset The operation register offset.
@retval the register content read.
**/
UINT32
EhcReadOpReg (
IN PEI_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.
**/
VOID
EhcWriteOpReg (
IN PEI_USB2_HC_DEV *Ehc,
IN UINT32 Offset,
IN UINT32 Data
)
;
/**
Stop the legacy USB SMI support.
@param Ehc The EHCI device.
**/
VOID
EhcClearLegacySupport (
IN PEI_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).
@retval EFI_TIMEOUT Time out happened while waiting door bell to set.
@retval EFI_SUCCESS Synchronized with the hardware.
**/
EFI_STATUS
EhcSetAndWaitDoorBell (
IN PEI_USB2_HC_DEV *Ehc,
IN UINT32 Timeout
)
;
/**
Clear all the interrutp status bits, these bits
are Write-Clean.
@param Ehc The EHCI device.
**/
VOID
EhcAckAllInterrupt (
IN PEI_USB2_HC_DEV *Ehc
)
;
/**
Check whether Ehc is halted.
@param Ehc The EHCI device.
@retval TRUE The controller is halted.
@retval FALSE The controller isn't halted.
**/
BOOLEAN
EhcIsHalt (
IN PEI_USB2_HC_DEV *Ehc
)
;
/**
Check whether system error occurred.
@param Ehc The EHCI device.
@retval TRUE System error happened.
@retval FALSE No system error.
**/
BOOLEAN
EhcIsSysError (
IN PEI_USB2_HC_DEV *Ehc
)
;
/**
Reset the host controller.
@param Ehc The EHCI device.
@param Timeout Time to wait before abort (in millisecond, ms).
@retval EFI_TIMEOUT The transfer failed due to time out.
@retval Others Failed to reset the host.
**/
EFI_STATUS
EhcResetHC (
IN PEI_USB2_HC_DEV *Ehc,
IN UINT32 Timeout
)
;
/**
Halt the host controller.
@param Ehc The EHCI device.
@param Timeout Time to wait before abort.
@retval EFI_TIMEOUT Failed to halt the controller before Timeout.
@retval EFI_SUCCESS The EHCI is halt.
**/
EFI_STATUS
EhcHaltHC (
IN PEI_USB2_HC_DEV *Ehc,
IN UINT32 Timeout
)
;
/**
Set the EHCI to run
@param Ehc The EHCI device.
@param Timeout Time to wait before abort.
@retval EFI_SUCCESS The EHCI is running.
@retval Others Failed to set the EHCI to run.
**/
EFI_STATUS
EhcRunHC (
IN PEI_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.
@retval EFI_SUCCESS The EHCI has come out of halt state.
@retval EFI_TIMEOUT Time out happened.
**/
EFI_STATUS
EhcInitHC (
IN PEI_USB2_HC_DEV *Ehc
)
;
#endif

View File

@@ -0,0 +1,873 @@
/** @file
PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
which is used to enable recovery function from USB Drivers.
Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions
of the BSD License which accompanies this distribution. The
full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "EhcPeim.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.
**/
EFI_STATUS
EhcCreateHelpQ (
IN PEI_USB2_HC_DEV *Ehc
)
{
USB_ENDPOINT Ep;
PEI_EHC_QH *Qh;
QH_HW *QhHw;
PEI_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 PEI_USB2_HC_DEV *Ehc
)
{
EFI_PHYSICAL_ADDRESS PhyAddr;
VOID *Map;
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
//
//
// The Frame List ocupies 4K bytes,
// and must be aligned on 4-Kbyte boundaries.
//
Status = PeiServicesAllocatePages (
EfiBootServicesCode,
1,
&PhyAddr
);
Map = NULL;
Ehc->PeriodFrameHost = (VOID *)(UINTN)PhyAddr;
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 (
Ehc,
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.
**/
VOID
EhcFreeSched (
IN PEI_USB2_HC_DEV *Ehc
)
{
EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);
EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);
if (Ehc->PeriodOne != NULL) {
UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));
Ehc->PeriodOne = NULL;
}
if (Ehc->ReclaimHead != NULL) {
UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));
Ehc->ReclaimHead = NULL;
}
if (Ehc->ShortReadStop != NULL) {
UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (PEI_EHC_QTD));
Ehc->ShortReadStop = NULL;
}
if (Ehc->MemPool != NULL) {
UsbHcFreeMemPool (Ehc->MemPool);
Ehc->MemPool = NULL;
}
if (Ehc->PeriodFrame != NULL) {
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.
**/
VOID
EhcLinkQhToAsync (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_EHC_QH *Qh
)
{
PEI_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.
**/
VOID
EhcUnlinkQhFromAsync (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_EHC_QH *Qh
)
{
PEI_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_TIMEOUT);
return;
}
/**
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.
**/
VOID
EhcLinkQhToPeriod (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_EHC_QH *Qh
)
{
UINT32 *Frames;
UINTN Index;
PEI_EHC_QH *Prev;
PEI_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.
**/
VOID
EhcUnlinkQhFromPeriod (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_EHC_QH *Qh
)
{
UINT32 *Frames;
UINTN Index;
PEI_EHC_QH *Prev;
PEI_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.
@retval TRUE URB transfer is finialized.
@retval FALSE URB transfer is not finialized.
**/
BOOLEAN
EhcCheckUrbResult (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_URB *Urb
)
{
EFI_LIST_ENTRY *Entry;
PEI_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, PEI_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)) {
Finished = TRUE;
goto ON_EXIT;
}
}
}
}
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.
@retval EFI_DEVICE_ERROR The transfer failed due to transfer error.
@retval EFI_TIMEOUT The transfer failed due to time out.
@retval EFI_SUCCESS The transfer finished OK.
**/
EFI_STATUS
EhcExecTransfer (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_URB *Urb,
IN UINTN TimeOut
)
{
EFI_STATUS Status;
UINTN Index;
UINTN Loop;
BOOLEAN Finished;
Status = EFI_SUCCESS;
Loop = (TimeOut * EHC_1_MILLISECOND / EHC_SYNC_POLL_INTERVAL) + 1;
Finished = FALSE;
for (Index = 0; Index < Loop; Index++) {
Finished = EhcCheckUrbResult (Ehc, Urb);
if (Finished) {
break;
}
MicroSecondDelay (EHC_SYNC_POLL_INTERVAL);
}
if (!Finished) {
Status = EFI_TIMEOUT;
} else if (Urb->Result != EFI_USB_NOERROR) {
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_NOT_FOUND No transfer for the device is found.
@retval EFI_SUCCESS An asynchronous transfer is removed.
**/
EFI_STATUS
EhciDelAsyncIntTransfer (
IN PEI_USB2_HC_DEV *Ehc,
IN UINT8 DevAddr,
IN UINT8 EpNum,
OUT UINT8 *DataToggle
)
{
EFI_LIST_ENTRY *Entry;
EFI_LIST_ENTRY *Next;
PEI_URB *Urb;
EFI_USB_DATA_DIRECTION Direction;
Direction = (((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);
EpNum &= 0x0F;
EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
Urb = EFI_LIST_CONTAINER (Entry, PEI_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);
EhcFreeUrb (Ehc, Urb);
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/**
Remove all the asynchronous interrutp transfers.
@param Ehc The EHCI device.
**/
VOID
EhciDelAllAsyncIntTransfers (
IN PEI_USB2_HC_DEV *Ehc
)
{
EFI_LIST_ENTRY *Entry;
EFI_LIST_ENTRY *Next;
PEI_URB *Urb;
EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
Urb = EFI_LIST_CONTAINER (Entry, PEI_URB, UrbList);
EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);
RemoveEntryList (&Urb->UrbList);
EhcFreeUrb (Ehc, Urb);
}
}
/**
Flush data from PCI controller specific address to mapped system
memory address.
@param Ehc The EHCI device.
@param Urb The URB to unmap.
@retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
@retval EFI_SUCCESS Success to flush data to mapped system memory.
**/
EFI_STATUS
EhcFlushAsyncIntMap (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_URB *Urb
)
{
EFI_PHYSICAL_ADDRESS PhyAddr;
Urb->DataMap = NULL;
PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) Urb->Data;
Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);
return EFI_SUCCESS;
}
/**
Update the queue head for next round of asynchronous transfer.
@param Urb The URB to update.
**/
VOID
EhcUpdateAsyncRequest (
IN PEI_URB *Urb
)
{
EFI_LIST_ENTRY *Entry;
PEI_EHC_QTD *FirstQtd;
QH_HW *QhHw;
PEI_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, PEI_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 ;
}
/**
Remove all the asynchronous interrutp transfers.
@param Event Interrupt event.
@param Context Pointer to PEI_USB2_HC_DEV.
**/
VOID
EFIAPI
EhcMoniteAsyncRequests (
IN EFI_EVENT Event,
IN VOID *Context
)
{
PEI_USB2_HC_DEV *Ehc;
EFI_LIST_ENTRY *Entry;
EFI_LIST_ENTRY *Next;
BOOLEAN Finished;
UINT8 *ProcBuf;
PEI_URB *Urb;
EFI_STATUS Status;
UINTN PageNumber;
Ehc = (PEI_USB2_HC_DEV *) Context;
EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
Urb = EFI_LIST_CONTAINER (Entry, PEI_URB, UrbList);
//
// Check the result of URB execution. If it is still
// active, check the next one.
//
Finished = EhcCheckUrbResult (Ehc, Urb);
if (!Finished) {
continue;
}
//
// Flush any PCI posted write transactions from a PCI host
// bridge to system memory.
//
Status = EhcFlushAsyncIntMap (Ehc, Urb);
//
// 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);
PageNumber = Urb->Completed/PAGESIZE +1;
Status = PeiServicesAllocatePages (
EfiBootServicesCode,
PageNumber,
(EFI_PHYSICAL_ADDRESS *)ProcBuf
);
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) {
(Urb->Callback) (ProcBuf, Urb->Completed, Urb->Context, Urb->Result);
}
if (ProcBuf != NULL) {
}
}
}

View File

@@ -0,0 +1,180 @@
/** @file
Private Header file for Usb Host Controller PEIM
Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
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.
**/
#ifndef _EFI_EHCI_SCHED_H_
#define _EFI_EHCI_SCHED_H_
/**
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 PEI_USB2_HC_DEV *Ehc
)
;
/**
Free the schedule data. It may be partially initialized.
@param Ehc The EHCI device.
**/
VOID
EhcFreeSched (
IN PEI_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.
**/
VOID
EhcLinkQhToAsync (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_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.
**/
VOID
EhcUnlinkQhFromAsync (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_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.
**/
VOID
EhcLinkQhToPeriod (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_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.
**/
VOID
EhcUnlinkQhFromPeriod (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_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.
@retval EFI_DEVICE_ERROR The transfer failed due to transfer error.
@retval EFI_TIMEOUT The transfer failed due to time out.
@retval EFI_SUCCESS The transfer finished OK.
**/
EFI_STATUS
EhcExecTransfer (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_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_NOT_FOUND No transfer for the device is found.
@retval EFI_SUCCESS An asynchronous transfer is removed.
**/
EFI_STATUS
EhciDelAsyncIntTransfer (
IN PEI_USB2_HC_DEV *Ehc,
IN UINT8 DevAddr,
IN UINT8 EpNum,
OUT UINT8 *DataToggle
)
;
/**
Remove all the asynchronous interrutp transfers.
@param Ehc The EHCI device.
**/
VOID
EhciDelAllAsyncIntTransfers (
IN PEI_USB2_HC_DEV *Ehc
)
;
/**
Remove all the asynchronous interrutp transfers.
@param Event Interrupt event.
@param Context Pointer to PEI_USB2_HC_DEV.
**/
VOID
EFIAPI
EhcMoniteAsyncRequests (
IN EFI_EVENT Event,
IN VOID *Context
)
;
#endif

View File

@@ -0,0 +1,610 @@
/** @file
PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
which is used to enable recovery function from USB Drivers.
Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions
of the BSD License which accompanies this distribution. The
full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "EhcPeim.h"
/**
Delete a single asynchronous interrupt transfer for
the device and endpoint.
@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.
@retval the pointer to the created QTD or NULL if failed to create one.
**/
PEI_EHC_QTD *
EhcCreateQtd (
IN PEI_USB2_HC_DEV *Ehc,
IN UINT8 *Data,
IN UINTN DataLen,
IN UINT8 PktId,
IN UINT8 Toggle,
IN UINTN MaxPacket
)
{
PEI_EHC_QTD *Qtd;
QTD_HW *QtdHw;
UINTN Index;
UINTN Len;
UINTN ThisBufLen;
ASSERT (Ehc != NULL);
Qtd = UsbHcAllocateMem (Ehc, Ehc->MemPool, sizeof (PEI_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.
**/
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.
@retval the pointer to the created queue head or NULL if failed to create one.
**/
PEI_EHC_QH *
EhcCreateQh (
IN PEI_USB2_HC_DEV *Ehci,
IN USB_ENDPOINT *Ep
)
{
PEI_EHC_QH *Qh;
QH_HW *QhHw;
Qh = UsbHcAllocateMem (Ehci, Ehci->MemPool, sizeof (PEI_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.
@retval The converted interval.
**/
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 (UINTN)1 << (BitCount - 1);
}
/**
Free a list of QTDs.
@param Ehc The EHCI device.
@param Qtds The list head of the QTD.
**/
VOID
EhcFreeQtds (
IN PEI_USB2_HC_DEV *Ehc,
IN EFI_LIST_ENTRY *Qtds
)
{
EFI_LIST_ENTRY *Entry;
EFI_LIST_ENTRY *Next;
PEI_EHC_QTD *Qtd;
EFI_LIST_FOR_EACH_SAFE (Entry, Next, Qtds) {
Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
RemoveEntryList (&Qtd->QtdList);
UsbHcFreeMem (Ehc->MemPool, Qtd, sizeof (PEI_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.
**/
VOID
EhcFreeUrb (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_URB *Urb
)
{
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 (PEI_EHC_QH));
}
}
/**
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.
**/
EFI_STATUS
EhcCreateQtds (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_URB *Urb
)
{
USB_ENDPOINT *Ep;
PEI_EHC_QH *Qh;
PEI_EHC_QTD *Qtd;
PEI_EHC_QTD *StatusQtd;
PEI_EHC_QTD *NextQtd;
EFI_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 = (UINT8) (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, PEI_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, PEI_EHC_QTD, QtdList);
Qtd->QtdHw.NextQtd = QTD_LINK (NextQtd, FALSE);
}
//
// Link the QTDs to the queue head
//
NextQtd = EFI_LIST_CONTAINER (Qh->Qtds.ForwardLink, PEI_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.
@retval the pointer to the created URB or NULL.
**/
PEI_URB *
EhcCreateUrb (
IN PEI_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_STATUS Status;
UINTN Len;
PEI_URB *Urb;
VOID *Map;
Map = NULL;
Urb = Ehc->Urb;
Urb->Signature = EHC_URB_SIG;
InitializeListHead (&Urb->UrbList);
Ep = &Urb->Ep;
Ep->DevAddr = DevAddr;
Ep->EpAddr = (UINT8) (EpAddr & 0x0F);
Ep->Direction = (((EpAddr & 0x80) != 0) ? 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;
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);
PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) Request ;
if ( (Len != sizeof (EFI_USB_DEVICE_REQUEST))) {
goto ON_ERROR;
}
Urb->RequestPhy = (VOID *) ((UINTN) PhyAddr);
Urb->RequestMap = Map;
}
if (Data != NULL) {
Len = DataLen;
PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) Data ;
if ( (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,331 @@
/** @file
Private Header file for Usb Host Controller PEIM
Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
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.
**/
#ifndef _EFI_EHCI_URB_H_
#define _EFI_EHCI_URB_H_
typedef struct _PEI_EHC_QTD PEI_EHC_QTD;
typedef struct _PEI_EHC_QH PEI_EHC_QH;
typedef struct _PEI_URB PEI_URB;
#define EHC_CTRL_TRANSFER 0x01
#define EHC_BULK_TRANSFER 0x02
#define EHC_INT_TRANSFER_SYNC 0x04
#define EHC_INT_TRANSFER_ASYNC 0x08
#define EHC_QTD_SIG SIGNATURE_32 ('U', 'S', 'B', 'T')
#define EHC_QH_SIG SIGNATURE_32 ('U', 'S', 'B', 'H')
#define EHC_URB_SIG SIGNATURE_32 ('U', 'S', 'B', 'R')
//
// Hardware related bit definitions
//
#define EHC_TYPE_ITD 0x00
#define EHC_TYPE_QH 0x02
#define EHC_TYPE_SITD 0x04
#define EHC_TYPE_FSTN 0x06
#define QH_NAK_RELOAD 3
#define QH_HSHBW_MULTI 1
#define QTD_MAX_ERR 3
#define QTD_PID_OUTPUT 0x00
#define QTD_PID_INPUT 0x01
#define QTD_PID_SETUP 0x02
#define QTD_STAT_DO_OUT 0
#define QTD_STAT_DO_SS 0
#define QTD_STAT_DO_PING 0x01
#define QTD_STAT_DO_CS 0x02
#define QTD_STAT_TRANS_ERR 0x08
#define QTD_STAT_BABBLE_ERR 0x10
#define QTD_STAT_BUFF_ERR 0x20
#define QTD_STAT_HALTED 0x40
#define QTD_STAT_ACTIVE 0x80
#define QTD_STAT_ERR_MASK (QTD_STAT_TRANS_ERR | QTD_STAT_BABBLE_ERR | QTD_STAT_BUFF_ERR)
#define QTD_MAX_BUFFER 4
#define QTD_BUF_LEN 4096
#define QTD_BUF_MASK 0x0FFF
#define QH_MICROFRAME_0 0x01
#define QH_MICROFRAME_1 0x02
#define QH_MICROFRAME_2 0x04
#define QH_MICROFRAME_3 0x08
#define QH_MICROFRAME_4 0x10
#define QH_MICROFRAME_5 0x20
#define QH_MICROFRAME_6 0x40
#define QH_MICROFRAME_7 0x80
#define 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.
//
struct _PEI_EHC_QTD {
QTD_HW QtdHw;
UINT32 Signature;
EFI_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
};
//
// 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.
//
struct _PEI_EHC_QH {
QH_HW QhHw;
UINT32 Signature;
PEI_EHC_QH *NextQh; // The queue head pointed to by horizontal link
EFI_LIST_ENTRY Qtds; // The list of QTDs to this queue head
UINTN Interval;
};
//
// URB (Usb Request Block) contains information for all kinds of
// usb requests.
//
struct _PEI_URB {
UINT32 Signature;
EFI_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
//
PEI_EHC_QH *Qh;
//
// Transaction result
//
UINT32 Result;
UINTN Completed; // completed data length
UINT8 DataToggle;
};
/**
Delete a single asynchronous interrupt transfer for
the device and endpoint.
@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.
@retval the pointer to the created QTD or NULL if failed to create one.
**/
PEI_EHC_QTD *
EhcCreateQtd (
IN PEI_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.
@retval the pointer to the created queue head or NULL if failed to create one.
**/
PEI_EHC_QH *
EhcCreateQh (
IN PEI_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.
**/
VOID
EhcFreeUrb (
IN PEI_USB2_HC_DEV *Ehc,
IN PEI_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.
@retval the pointer to the created URB or NULL.
**/
PEI_URB *
EhcCreateUrb (
IN PEI_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,493 @@
/** @file
PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
which is used to enable recovery function from USB Drivers.
Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions
of the BSD License which accompanies this distribution. The
full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "EhcPeim.h"
/**
Allocate a block of memory to be used by the buffer pool.
@param Ehc The EHCI device.
@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.
**/
USBHC_MEM_BLOCK *
UsbHcAllocMemBlock (
IN PEI_USB2_HC_DEV *Ehc,
IN USBHC_MEM_POOL *Pool,
IN UINTN Pages
)
{
USBHC_MEM_BLOCK *Block;
VOID *BufHost;
VOID *Mapping;
EFI_PHYSICAL_ADDRESS MappedAddr;
EFI_STATUS Status;
UINTN PageNumber;
EFI_PHYSICAL_ADDRESS TempPtr;
Mapping = NULL;
PageNumber = sizeof(USBHC_MEM_BLOCK)/PAGESIZE +1;
Status = PeiServicesAllocatePages (
EfiBootServicesCode,
PageNumber,
&TempPtr
);
if (EFI_ERROR (Status)) {
return NULL;
}
ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);
//
// 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 = (USBHC_MEM_BLOCK*)(UINTN)TempPtr;
Block->BufLen = EFI_PAGES_TO_SIZE (Pages);
Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8);
PageNumber = (Block->BitsLen)/PAGESIZE +1;
Status = PeiServicesAllocatePages (
EfiBootServicesCode,
PageNumber,
&TempPtr
);
if (EFI_ERROR (Status)) {
return NULL;
}
ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);
Block->Bits = (UINT8 *)(UINTN)TempPtr;
Status = PeiServicesAllocatePages (
EfiBootServicesCode,
Pages,
&TempPtr
);
ZeroMem ((VOID *)(UINTN)TempPtr, Pages*EFI_PAGE_SIZE);
BufHost = (VOID *)(UINTN)TempPtr;
MappedAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) BufHost;
//
// 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))) {
return NULL;
}
Block->BufHost = BufHost;
Block->Buf = (UINT8 *) ((UINTN) MappedAddr);
Block->Mapping = Mapping;
Block->Next = NULL;
return Block;
}
/**
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.
**/
VOID
UsbHcFreeMemBlock (
IN USBHC_MEM_POOL *Pool,
IN USBHC_MEM_BLOCK *Block
)
{
ASSERT ((Pool != NULL) && (Block != NULL));
}
/**
Alloc some memory from the block.
@param Block The memory block to allocate memory from.
@param Units Number of memory units to allocate.
@return The pointer to the allocated memory. If couldn't allocate the needed memory,
the return value is NULL.
**/
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] = (UINT8) (Block->Bits[Byte] | (UINT8) 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.
**/
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.
@retval TRUE The memory block is empty.
@retval FALSE The memory block isn't empty.
**/
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.
**/
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 Ehc The EHCI device.
@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.
@retval EFI_SUCCESS The memory pool is initialized.
@retval EFI_OUT_OF_RESOURCE Fail to init the memory pool.
**/
USBHC_MEM_POOL *
UsbHcInitMemPool (
IN PEI_USB2_HC_DEV *Ehc,
IN BOOLEAN Check4G,
IN UINT32 Which4G
)
{
USBHC_MEM_POOL *Pool;
UINTN PageNumber;
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS TempPtr;
PageNumber = sizeof(USBHC_MEM_POOL)/PAGESIZE +1;
Status = PeiServicesAllocatePages (
EfiBootServicesCode,
PageNumber,
&TempPtr
);
if (EFI_ERROR (Status)) {
return NULL;
}
ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);
Pool = (USBHC_MEM_POOL *) ((UINTN) TempPtr);
Pool->Check4G = Check4G;
Pool->Which4G = Which4G;
Pool->Head = UsbHcAllocMemBlock (Ehc, Pool, USBHC_MEM_DEFAULT_PAGES);
if (Pool->Head == NULL) {
Pool = NULL;
}
return Pool;
}
/**
Release the memory management pool.
@param Pool The USB memory pool to free.
@retval EFI_DEVICE_ERROR Fail to free the memory pool.
@retval EFI_SUCCESS The memory pool is freed.
**/
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) {
UsbHcFreeMemBlock (Pool, Block);
}
UsbHcFreeMemBlock (Pool, Pool->Head);
return EFI_SUCCESS;
}
/**
Allocate some memory from the host controller's memory pool
which can be used to communicate with host controller.
@param Ehc The EHCI device.
@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 PEI_USB2_HC_DEV *Ehc,
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 (Ehc,Pool, Pages);
if (NewBlock == NULL) {
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.
**/
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) (Block->Bits[Byte] ^ 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)) {
UsbHcFreeMemBlock (Pool, Block);
}
return ;
}

View File

@@ -0,0 +1,77 @@
/** @file
Private Header file for Usb Host Controller PEIM
Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
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.
**/
#ifndef _EFI_EHCI_MEM_H_
#define _EFI_EHCI_MEM_H_
#include <Uefi.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;
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_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 {
BOOLEAN Check4G;
UINT32 Which4G;
USBHC_MEM_BLOCK *Head;
} USBHC_MEM_POOL;
//
// Memory allocation unit, must be 2^n, n>4
//
#define USBHC_MEM_UNIT 64
#define USBHC_MEM_UNIT_MASK (USBHC_MEM_UNIT - 1)
#define 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)
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
## @file
# Component description file for UhcPeim PEIM to produce gPeiUsbHostControllerPpiGuid based on gPeiUsbControllerPpiGuid
# which is used to enable recovery function from USB Drivers.
#
# Usb Host Controller PEIM to support recovery from USB device.
# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
#
# 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]
INF_VERSION = 0x00010005
BASE_NAME = UhciPei
FILE_GUID = C463CEAC-FC57-4f36-88B7-356C750C3BCA
MODULE_TYPE = PEIM
VERSION_STRING = 1.0
ENTRY_POINT = UhcPeimEntry
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 IPF EBC
#
[Sources]
UhcPeim.c
UhcPeim.h
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
IoLib
TimerLib
BaseMemoryLib
PeiServicesLib
PeimEntryPoint
DebugLib
[Ppis]
gPeiUsbHostControllerPpiGuid # PPI ALWAYS_PRODUCED
gPeiUsbControllerPpiGuid # PPI ALWAYS_CONSUMED
[Depex]
gEfiPeiMemoryDiscoveredPpiGuid AND gPeiUsbControllerPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid