From 878ddf1fc3540a715f63594ed22b6929e881afb4 Mon Sep 17 00:00:00 2001 From: bbahnsen Date: Fri, 21 Apr 2006 22:54:32 +0000 Subject: Initial import. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3 6f19259b-4bc3-4df7-8a09-765794883524 --- EdkModulePkg/Bus/Pci/Uhci/Dxe/ComponentName.c | 189 ++ EdkModulePkg/Bus/Pci/Uhci/Dxe/Uhci.mbd | 42 + EdkModulePkg/Bus/Pci/Uhci/Dxe/Uhci.msa | 66 + EdkModulePkg/Bus/Pci/Uhci/Dxe/build.xml | 47 + EdkModulePkg/Bus/Pci/Uhci/Dxe/uhchlp.c | 4237 +++++++++++++++++++++++++ EdkModulePkg/Bus/Pci/Uhci/Dxe/uhci.c | 3498 ++++++++++++++++++++ EdkModulePkg/Bus/Pci/Uhci/Dxe/uhci.h | 1187 +++++++ 7 files changed, 9266 insertions(+) create mode 100644 EdkModulePkg/Bus/Pci/Uhci/Dxe/ComponentName.c create mode 100644 EdkModulePkg/Bus/Pci/Uhci/Dxe/Uhci.mbd create mode 100644 EdkModulePkg/Bus/Pci/Uhci/Dxe/Uhci.msa create mode 100644 EdkModulePkg/Bus/Pci/Uhci/Dxe/build.xml create mode 100644 EdkModulePkg/Bus/Pci/Uhci/Dxe/uhchlp.c create mode 100644 EdkModulePkg/Bus/Pci/Uhci/Dxe/uhci.c create mode 100644 EdkModulePkg/Bus/Pci/Uhci/Dxe/uhci.h (limited to 'EdkModulePkg/Bus/Pci/Uhci') diff --git a/EdkModulePkg/Bus/Pci/Uhci/Dxe/ComponentName.c b/EdkModulePkg/Bus/Pci/Uhci/Dxe/ComponentName.c new file mode 100644 index 0000000..71d9339 --- /dev/null +++ b/EdkModulePkg/Bus/Pci/Uhci/Dxe/ComponentName.c @@ -0,0 +1,189 @@ +/*++ + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + 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", (CHAR16 *) 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; + } + // + // 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 = USB_HC_DEV_FROM_THIS (UsbHc); + + return LookupUnicodeString ( + Language, + gUhciComponentName.SupportedLanguages, + UhciDev->ControllerNameTable, + ControllerName + ); + +} diff --git a/EdkModulePkg/Bus/Pci/Uhci/Dxe/Uhci.mbd b/EdkModulePkg/Bus/Pci/Uhci/Dxe/Uhci.mbd new file mode 100644 index 0000000..76d35e7 --- /dev/null +++ b/EdkModulePkg/Bus/Pci/Uhci/Dxe/Uhci.mbd @@ -0,0 +1,42 @@ + + + + + Uhci + 2FB92EFA-2EE0-4bae-9EB6-7464125E1EF7 + 0 + FIX ME! + Copyright (c) 2004-2006, Intel Corporation + + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + 2006-03-12 17:09 + 2006-03-19 15:18 + + + UefiBootServicesTableLib + UefiMemoryLib + UefiLib + UefiDriverEntryPoint + UefiDriverModelLib + DxeReportStatusCodeLib + BaseDebugLibReportStatusCode + EdkDxePrintLib + BaseLib + DxeMemoryAllocationLib + + diff --git a/EdkModulePkg/Bus/Pci/Uhci/Dxe/Uhci.msa b/EdkModulePkg/Bus/Pci/Uhci/Dxe/Uhci.msa new file mode 100644 index 0000000..9e91154 --- /dev/null +++ b/EdkModulePkg/Bus/Pci/Uhci/Dxe/Uhci.msa @@ -0,0 +1,66 @@ + + + + + Uhci + UEFI_DRIVER + BS_DRIVER + 2FB92EFA-2EE0-4bae-9EB6-7464125E1EF7 + 0 + Component description file for Uhci module + FIX ME! + Copyright (c) 2004-2006, Intel Corporation + + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + 0 + 2006-03-12 17:09 + 2006-03-19 15:18 + + + DebugLib + UefiDriverModelLib + UefiDriverEntryPoint + BaseLib + UefiLib + BaseMemoryLib + MemoryAllocationLib + UefiBootServicesTableLib + + + uhci.c + uhchlp.c + ComponentName.c + uhci.h + + + MdePkg + + + PciIo + UsbHc + + + + + + + gUhciDriverBinding + gUhciComponentName + + + diff --git a/EdkModulePkg/Bus/Pci/Uhci/Dxe/build.xml b/EdkModulePkg/Bus/Pci/Uhci/Dxe/build.xml new file mode 100644 index 0000000..37bb3c3 --- /dev/null +++ b/EdkModulePkg/Bus/Pci/Uhci/Dxe/build.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/EdkModulePkg/Bus/Pci/Uhci/Dxe/uhchlp.c b/EdkModulePkg/Bus/Pci/Uhci/Dxe/uhchlp.c new file mode 100644 index 0000000..62d58ee --- /dev/null +++ b/EdkModulePkg/Bus/Pci/Uhci/Dxe/uhchlp.c @@ -0,0 +1,4237 @@ +/*++ + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + UhcHlp.c + +Abstract: + + +Revision History +--*/ + +#include "uhci.h" + +EFI_STATUS +USBReadPortW ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 PortOffset, + IN OUT UINT16 *Data + ) +/*++ + +Routine Description: + + USBReadPort Word + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + PortOffset - Port offset + Data - Data to reutrn + +Returns: + + EFI_SUCCESS + +--*/ +{ + // + // Perform 16bit Read in PCI IO Space + // + return PciIo->Io.Read ( + PciIo, + EfiPciIoWidthUint16, + USB_BAR_INDEX, + (UINT64) PortOffset, + 1, + Data + ); +} + +EFI_STATUS +USBReadPortDW ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 PortOffset, + IN OUT UINT32 *Data + ) +/*++ + +Routine Description: + + USBReadPort DWord + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + PortOffset - Port offset + Data - Data to reutrn + +Returns: + + EFI_SUCCESS + +--*/ +{ + // + // Perform 32bit Read in PCI IO Space + // + return PciIo->Io.Read ( + PciIo, + EfiPciIoWidthUint32, + USB_BAR_INDEX, + (UINT64) PortOffset, + 1, + Data + ); +} + +EFI_STATUS +USBWritePortW ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 PortOffset, + IN UINT16 Data + ) +/*++ + +Routine Description: + + USB Write Port Word + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + PortOffset - Port offset + Data - Data to write + +Returns: + + EFI_SUCCESS + +--*/ +{ + // + // Perform 16bit Write in PCI IO Space + // + return PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint16, + USB_BAR_INDEX, + (UINT64) PortOffset, + 1, + &Data + ); +} + +EFI_STATUS +USBWritePortDW ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 PortOffset, + IN UINT32 Data + ) +/*++ + +Routine Description: + + USB Write Port DWord + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + PortOffset - Port offset + Data - Data to write + +Returns: + + EFI_SUCCESS + +--*/ +{ + // + // Perform 32bit Write in PCI IO Space + // + return PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint32, + USB_BAR_INDEX, + (UINT64) PortOffset, + 1, + &Data + ); +} +// +// USB register-base helper functions +// +EFI_STATUS +WriteUHCCommandReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 CmdAddrOffset, + IN UINT16 UsbCmd + ) +/*++ + +Routine Description: + + Write UHCI Command Register + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + CmdAddrOffset - Command address offset + UsbCmd - Data to write + +Returns: + + EFI_SUCCESS + +--*/ +{ + // + // Write to UHC's Command Register + // + return USBWritePortW (PciIo, CmdAddrOffset, UsbCmd); +} + +EFI_STATUS +ReadUHCCommandReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 CmdAddrOffset, + IN OUT UINT16 *Data + ) +/*++ + +Routine Description: + + Read UHCI Command Register + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + CmdAddrOffset - Command address offset + Data - Data to return + +Returns: + + EFI_SUCCESS + +--*/ +{ + // + // Read from UHC's Command Register + // + return USBReadPortW (PciIo, CmdAddrOffset, Data); +} + +EFI_STATUS +WriteUHCStatusReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusAddrOffset, + IN UINT16 UsbSts + ) +/*++ + +Routine Description: + + Write UHCI Staus Register + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + StatusAddrOffset - Status address offset + UsbSts - Data to write + +Returns: + + EFI_SUCCESS + +--*/ +{ + // + // Write to UHC's Status Register + // + return USBWritePortW (PciIo, StatusAddrOffset, UsbSts); +} + +EFI_STATUS +ReadUHCStatusReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusAddrOffset, + IN OUT UINT16 *Data + ) +/*++ + +Routine Description: + + Read UHCI Staus Register + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + StatusAddrOffset - Status address offset + UsbSts - Data to return + +Returns: + + EFI_SUCCESS + +--*/ +{ + // + // Read from UHC's Status Register + // + return USBReadPortW (PciIo, StatusAddrOffset, Data); +} + + +EFI_STATUS +ClearStatusReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusAddrOffset + ) +/*++ + +Routine Description: + + Clear the content of UHC's Status Register + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + StatusAddrOffset - Status address offset + +Returns: + + EFI_SUCCESS + +--*/ +{ + + return WriteUHCStatusReg (PciIo, StatusAddrOffset, 0x003F); +} + +EFI_STATUS +ReadUHCFrameNumberReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 FrameNumAddrOffset, + IN OUT UINT16 *Data + ) +/*++ + +Routine Description: + + Read from UHC's Frame Number Register + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + FrameNumAddrOffset - Frame number register offset + Data - Data to return +Returns: + + EFI_SUCCESS + +--*/ +{ + + return USBReadPortW (PciIo, FrameNumAddrOffset, Data); +} + +EFI_STATUS +WriteUHCFrameListBaseReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 FlBaseAddrOffset, + IN UINT32 UsbFrameListBaseAddr + ) +/*++ + +Routine Description: + + Write to UHC's Frame List Base Register + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + FlBaseAddrOffset - Frame Base address register + UsbFrameListBaseAddr - Address to write + +Returns: + + EFI_SUCCESS + +--*/ +{ + + return USBWritePortDW (PciIo, FlBaseAddrOffset, UsbFrameListBaseAddr); +} + +EFI_STATUS +ReadRootPortReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 PortAddrOffset, + IN OUT UINT16 *Data + ) +/*++ + +Routine Description: + + Read from UHC's Root Port Register + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + PortAddrOffset - Port Addrress Offset, + Data - Data to return +Returns: + + EFI_SUCCESS + +--*/ +{ + + return USBReadPortW (PciIo, PortAddrOffset, Data); +} + +EFI_STATUS +WriteRootPortReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 PortAddrOffset, + IN UINT16 ControlBits + ) +/*++ + +Routine Description: + + Write to UHC's Root Port Register + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + PortAddrOffset - Port Addrress Offset, + ControlBits - Data to write +Returns: + + EFI_SUCCESS + +--*/ +{ + + return USBWritePortW (PciIo, PortAddrOffset, ControlBits); +} + + + +EFI_STATUS +WaitForUHCHalt ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusRegAddr, + IN UINTN Timeout + ) +/*++ + +Routine Description: + + Wait until UHCI halt or timeout + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + StatusRegAddr - Status Register Address + Timeout - Time out value in us + +Returns: + + EFI_DEVICE_ERROR - Unable to read the status register + EFI_TIMEOUT - Time out + EFI_SUCCESS - Success + +--*/ +{ + UINTN Delay; + EFI_STATUS Status; + UINT16 HcStatus; + + // + // Timeout is in us unit + // + Delay = (Timeout / 50) + 1; + do { + Status = ReadUHCStatusReg (PciIo, StatusRegAddr, &HcStatus); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + if ((HcStatus & USBSTS_HCH) == USBSTS_HCH) { + break; + } + // + // Stall for 50 us + // + gBS->Stall (50); + + } while (Delay--); + + if (Delay == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +BOOLEAN +IsStatusOK ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusRegAddr + ) +/*++ + +Routine Description: + + Judge whether the host controller operates well + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + StatusRegAddr - Status register address + +Returns: + + TRUE - Status is good + FALSE - Status is bad + +--*/ +{ + EFI_STATUS Status; + UINT16 HcStatus; + // + // Detect whether the interrupt is caused by fatal error. + // see "UHCI Design Guid". + // + Status = ReadUHCStatusReg (PciIo, StatusRegAddr, &HcStatus); + if (EFI_ERROR (Status)) { + return FALSE; + } + + if (HcStatus & (USBSTS_HCPE | USBSTS_HSE | USBSTS_HCH)) { + return FALSE; + } else { + return TRUE; + } + +} + + +BOOLEAN +IsHostSysOrProcessErr ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusRegAddr + ) +/*++ + +Routine Description: + + Judge the status is HostSys,ProcessErr error or good + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + StatusRegAddr - Status register address + +Returns: + + TRUE - Status is good + FALSE - Status is bad + +--*/ +{ + EFI_STATUS Status; + UINT16 HcStatus; + // + // Detect whether the interrupt is caused by serious error. + // see "UHCI Design Guid". + // + Status = ReadUHCStatusReg (PciIo, StatusRegAddr, &HcStatus); + if (EFI_ERROR (Status)) { + return FALSE; + } + + if (HcStatus & (USBSTS_HSE | USBSTS_HCPE)) { + return TRUE; + } else { + return FALSE; + } +} + + +UINT16 +GetCurrentFrameNumber ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 FrameNumAddrOffset + ) +/*++ + +Routine Description: + + Get Current Frame Number + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + FrameNumAddrOffset - FrameNum register AddrOffset + +Returns: + + Frame number + +--*/ +{ + // + // Gets value in the USB frame number register. + // + UINT16 FrameNumber; + + ReadUHCFrameNumberReg (PciIo, FrameNumAddrOffset, &FrameNumber); + + return (UINT16) (FrameNumber & 0x03FF); +} + +EFI_STATUS +SetFrameListBaseAddress ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 FlBaseAddrReg, + IN UINT32 Addr + ) +/*++ + +Routine Description: + + Set FrameListBase Address + +Arguments: + + PciIo - EFI_PCI_IO_PROTOCOL + FlBaseAddrReg - FrameListBase register + Addr - Address to set + +Returns: + + EFI_SUCCESS + +--*/ +{ + // + // Sets value in the USB Frame List Base Address register. + // + return WriteUHCFrameListBaseReg (PciIo, FlBaseAddrReg, (UINT32) (Addr & 0xFFFFF000)); +} + +VOID +EnableMaxPacketSize ( + IN USB_HC_DEV *HcDev + ) +/*++ + +Routine Description: + + Enable Max Packet Size + +Arguments: + + HcDev - USB_HC_DEV + +Returns: + + VOID + +--*/ +{ + UINT16 CommandContent; + EFI_STATUS Status; + + Status = ReadUHCCommandReg ( + HcDev->PciIo, + (UINT32) (USBCMD), + &CommandContent + ); + + if ((CommandContent & USBCMD_MAXP) != USBCMD_MAXP) { + CommandContent |= USBCMD_MAXP; + WriteUHCCommandReg ( + HcDev->PciIo, + (UINT32) (USBCMD), + CommandContent + ); + } + + return ; +} + +EFI_STATUS +CreateFrameList ( + IN USB_HC_DEV *HcDev, + IN UINT32 FlBaseAddrReg + ) +/*++ + +Routine Description: + + CreateFrameList + +Arguments: + + HcDev - USB_HC_DEV + FlBaseAddrReg - Frame List register + +Returns: + + EFI_OUT_OF_RESOURCES - Can't allocate memory resources + EFI_UNSUPPORTED - Map memory fail + EFI_SUCCESS - Success + +--*/ +{ + EFI_STATUS Status; + VOID *CommonBuffer; + EFI_PHYSICAL_ADDRESS MappedAddress; + VOID *Mapping; + UINTN BufferSizeInPages; + UINTN BufferSizeInBytes; + + // + // The Frame List is a common buffer that will be + // accessed by both the cpu and the usb bus master + // at the same time. + // The Frame List ocupies 4K bytes, + // and must be aligned on 4-Kbyte boundaries. + // + BufferSizeInBytes = 4096; + BufferSizeInPages = EFI_SIZE_TO_PAGES (BufferSizeInBytes); + Status = HcDev->PciIo->AllocateBuffer ( + HcDev->PciIo, + AllocateAnyPages, + EfiBootServicesData, + BufferSizeInPages, + &CommonBuffer, + 0 + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + Status = HcDev->PciIo->Map ( + HcDev->PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + CommonBuffer, + &BufferSizeInBytes, + &MappedAddress, + &Mapping + ); + if (EFI_ERROR (Status) || (BufferSizeInBytes != 4096)) { + HcDev->PciIo->FreeBuffer (HcDev->PciIo, BufferSizeInPages, CommonBuffer); + return EFI_UNSUPPORTED; + } + + HcDev->FrameListEntry = (FRAMELIST_ENTRY *) ((UINTN) MappedAddress); + + HcDev->FrameListMapping = Mapping; + + InitFrameList (HcDev); + + // + // Tell the Host Controller where the Frame List lies, + // by set the Frame List Base Address Register. + // + SetFrameListBaseAddress ( + HcDev->PciIo, + FlBaseAddrReg, + (UINT32) ((UINTN) HcDev->FrameListEntry) + ); + + return EFI_SUCCESS; +} + +EFI_STATUS +FreeFrameListEntry ( + IN USB_HC_DEV *HcDev + ) +/*++ + +Routine Description: + + Free FrameList buffer + +Arguments: + + HcDev - USB_HC_DEV + +Returns: + + EFI_SUCCESS - success + +--*/ +{ + // + // Unmap the common buffer for framelist entry, + // and free the common buffer. + // Uhci's frame list occupy 4k memory. + // + HcDev->PciIo->Unmap (HcDev->PciIo, HcDev->FrameListMapping); + HcDev->PciIo->FreeBuffer ( + HcDev->PciIo, + EFI_SIZE_TO_PAGES (4096), + (VOID *) (HcDev->FrameListEntry) + ); + return EFI_SUCCESS; +} + +VOID +InitFrameList ( + IN USB_HC_DEV *HcDev + ) +/*++ + +Routine Description: + + Initialize FrameList + +Arguments: + + HcDev - USB_HC_DEV + +Returns: + VOID + +--*/ +{ + FRAMELIST_ENTRY *FrameListPtr; + UINTN Index; + + // + // Validate each Frame List Entry + // + FrameListPtr = HcDev->FrameListEntry; + for (Index = 0; Index < 1024; Index++) { + FrameListPtr->FrameListPtrTerminate = 1; + FrameListPtr->FrameListPtr = 0; + FrameListPtr->FrameListPtrQSelect = 0; + FrameListPtr->FrameListRsvd = 0; + FrameListPtr++; + } +} +// +// ////////////////////////////////////////////////////////////// +// +// QH TD related Helper Functions +// +//////////////////////////////////////////////////////////////// +// +// functions for QH +// +EFI_STATUS +AllocateQHStruct ( + IN USB_HC_DEV *HcDev, + OUT QH_STRUCT **ppQHStruct + ) +/*++ + +Routine Description: + + Allocate QH Struct + +Arguments: + + HcDev - USB_HC_DEV + ppQHStruct - QH_STRUCT content to return +Returns: + + EFI_SUCCESS + +--*/ +{ + *ppQHStruct = NULL; + + // + // QH must align on 16 bytes alignment, + // since the memory allocated by UhciAllocatePool () + // is aligned on 32 bytes, it is no need to adjust + // the allocated memory returned. + // + return UhciAllocatePool (HcDev, (UINT8 **) ppQHStruct, sizeof (QH_STRUCT)); +} + + +EFI_STATUS +CreateQH ( + IN USB_HC_DEV *HcDev, + OUT QH_STRUCT **pptrQH + ) +/*++ + +Routine Description: + + CreateQH + +Arguments: + + HcDev - USB_HC_DEV + ppQHStruct - QH_STRUCT content to return +Returns: + + EFI_SUCCESS - Success + EFI_OUT_OF_RESOURCES - Can't allocate memory +--*/ +{ + EFI_STATUS Status; + + // + // allocate align memory for QH_STRUCT + // + Status = AllocateQHStruct (HcDev, pptrQH); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + // + // init each field of the QH_STRUCT + // + // + // Make QH ready + // + SetQHHorizontalValidorInvalid (*pptrQH, FALSE); + SetQHVerticalValidorInvalid (*pptrQH, FALSE); + + return EFI_SUCCESS; +} + +VOID +SetQHHorizontalLinkPtr ( + IN QH_STRUCT *PtrQH, + IN VOID *ptrNext + ) +/*++ + +Routine Description: + + Set QH Horizontal Link Pointer + +Arguments: + + PtrQH - QH_STRUCT + ptrNext - Data to write + +Returns: + + VOID + +--*/ +{ + // + // Since the QH_STRUCT is aligned on 16-byte boundaries, + // Only the highest 28bit of the address is valid + // (take 32bit address as an example). + // + PtrQH->QH.QHHorizontalPtr = (UINT32) ((UINTN) ptrNext >> 4); +} + +VOID * +GetQHHorizontalLinkPtr ( + IN QH_STRUCT *PtrQH + ) +/*++ + +Routine Description: + + Get QH Horizontal Link Pointer + +Arguments: + + PtrQH - QH_STRUCT + + +Returns: + + Data to return + +--*/ +{ + // + // Restore the 28bit address to 32bit address + // (take 32bit address as an example) + // + return (VOID *) ((UINTN) (PtrQH->QH.QHHorizontalPtr << 4)); +} + +VOID +SetQHHorizontalQHorTDSelect ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN bQH + ) +/*++ + +Routine Description: + + Set QH Horizontal QH or TD + +Arguments: + + PtrQH - QH_STRUCT + bQH - TRUE is QH FALSE is TD + +Returns: + VOID + +--*/ +{ + // + // if QH is connected, the specified bit is set, + // if TD is connected, the specified bit is cleared. + // + PtrQH->QH.QHHorizontalQSelect = bQH ? 1 : 0; +} + + +VOID +SetQHHorizontalValidorInvalid ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN bValid + ) +/*++ + +Routine Description: + + Set QH Horizontal Valid or Invalid + +Arguments: + + PtrQH - QH_STRUCT + bValid - TRUE is Valid FALSE is Invalid + +Returns: + VOID + +--*/ +{ + // + // Valid means the horizontal link pointer is valid, + // else, it's invalid. + // + PtrQH->QH.QHHorizontalTerminate = bValid ? 0 : 1; +} + +VOID +SetQHVerticalLinkPtr ( + IN QH_STRUCT *PtrQH, + IN VOID *ptrNext + ) +/*++ + +Routine Description: + + Set QH Vertical Link Pointer + +Arguments: + + PtrQH - QH_STRUCT + ptrNext - Data to write +Returns: + + VOID + +--*/ +{ + // + // Since the QH_STRUCT is aligned on 16-byte boundaries, + // Only the highest 28bit of the address is valid + // (take 32bit address as an example). + // + PtrQH->QH.QHVerticalPtr = (UINT32) ((UINTN) ptrNext >> 4); +} + +VOID * +GetQHVerticalLinkPtr ( + IN QH_STRUCT *PtrQH + ) +/*++ + +Routine Description: + + Get QH Vertical Link Pointer + +Arguments: + + PtrQH - QH_STRUCT + +Returns: + + Data to return + +--*/ +{ + // + // Restore the 28bit address to 32bit address + // (take 32bit address as an example) + // + return (VOID *) ((UINTN) (PtrQH->QH.QHVerticalPtr << 4)); +} + +VOID +SetQHVerticalQHorTDSelect ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN bQH + ) +/*++ + +Routine Description: + + Set QH Vertical QH or TD + +Arguments: + + PtrQH - QH_STRUCT + bQH - TRUE is QH FALSE is TD + +Returns: + + VOID + +--*/ +{ + // + // Set the specified bit if the Vertical Link Pointer pointing to a QH, + // Clear the specified bit if the Vertical Link Pointer pointing to a TD. + // + PtrQH->QH.QHVerticalQSelect = bQH ? 1 : 0; +} + +BOOLEAN +IsQHHorizontalQHSelect ( + IN QH_STRUCT *PtrQH + ) +/*++ + +Routine Description: + + Is QH Horizontal QH Select + +Arguments: + + PtrQH - QH_STRUCT + +Returns: + + TRUE - QH + FALSE - TD + +--*/ +{ + // + // Retrieve the information about whether the Horizontal Link Pointer + // pointing to a QH or TD. + // + return (BOOLEAN) (PtrQH->QH.QHHorizontalQSelect ? TRUE : FALSE); +} + +VOID +SetQHVerticalValidorInvalid ( + IN QH_STRUCT *PtrQH, + IN BOOLEAN IsValid + ) +/*++ + +Routine Description: + + Set QH Vertical Valid or Invalid + +Arguments: + + PtrQH - QH_STRUCT + IsValid - TRUE is valid FALSE is invalid + +Returns: + + VOID + +--*/ +{ + // + // If TRUE, indicates the Vertical Link Pointer field is valid, + // else, the field is invalid. + // + PtrQH->QH.QHVerticalTerminate = IsValid ? 0 : 1; +} + + +BOOLEAN +GetQHVerticalValidorInvalid ( + IN QH_STRUCT *PtrQH + ) +/*++ + +Routine Description: + + Get QH Vertical Valid or Invalid + +Arguments: + + PtrQH - QH_STRUCT + +Returns: + + TRUE - Valid + FALSE - Invalid + +--*/ +{ + // + // If TRUE, indicates the Vertical Link Pointer field is valid, + // else, the field is invalid. + // + return (BOOLEAN) (!(PtrQH->QH.QHVerticalTerminate)); +} + +BOOLEAN +GetQHHorizontalValidorInvalid ( + IN QH_STRUCT *PtrQH + ) +/*++ + +Routine Description: + + Get QH Horizontal Valid or Invalid + +Arguments: + + PtrQH - QH_STRUCT + +Returns: + + TRUE - Valid + FALSE - Invalid + +--*/ +{ + // + // If TRUE, meaning the Horizontal Link Pointer field is valid, + // else, the field is invalid. + // + return (BOOLEAN) (!(PtrQH->QH.QHHorizontalTerminate)); +} +// +// functions for TD +// +EFI_STATUS +AllocateTDStruct ( + IN USB_HC_DEV *HcDev, + OUT TD_STRUCT **ppTDStruct + ) +/*++ + +Routine Description: + + Allocate TD Struct + +Arguments: + + HcDev - USB_HC_DEV + ppTDStruct - place to store TD_STRUCT pointer +Returns: + + EFI_SUCCESS + +--*/ +{ + *ppTDStruct = NULL; + + // + // TD must align on 16 bytes alignment, + // since the memory allocated by UhciAllocatePool () + // is aligned on 32 bytes, it is no need to adjust + // the allocated memory returned. + // + return UhciAllocatePool ( + HcDev, + (UINT8 **) ppTDStruct, + sizeof (TD_STRUCT) + ); +} + +EFI_STATUS +CreateTD ( + IN USB_HC_DEV *HcDev, + OUT TD_STRUCT **pptrTD + ) +/*++ + +Routine Description: + + Create TD + +Arguments: + + HcDev - USB_HC_DEV + pptrTD - TD_STRUCT pointer to store + +Returns: + + EFI_OUT_OF_RESOURCES - Can't allocate resources + EFI_SUCCESS - Success + +--*/ +{ + EFI_STATUS Status; + // + // create memory for TD_STRUCT, and align the memory. + // + Status = AllocateTDStruct (HcDev, pptrTD); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Make TD ready. + // + SetTDLinkPtrValidorInvalid (*pptrTD, FALSE); + + + return EFI_SUCCESS; +} + +EFI_STATUS +GenSetupStageTD ( + IN USB_HC_DEV *HcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN BOOLEAN bSlow, + IN UINT8 *pDevReq, + IN UINT8 RequestLen, + OUT TD_STRUCT **ppTD + ) +/*++ + +Routine Description: + + Generate Setup Stage TD + +Arguments: + + HcDev - USB_HC_DEV + DevAddr - Device address + Endpoint - Endpoint number + bSlow - Full speed or low speed + pDevReq - Device request + RequestLen - Request length + ppTD - TD_STRUCT to return +Returns: + + EFI_OUT_OF_RESOURCES - Can't allocate memory + EFI_SUCCESS - Success + +--*/ +{ + EFI_STATUS Status; + TD_STRUCT *pTDStruct; + + Status = CreateTD (HcDev, &pTDStruct); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + SetTDLinkPtr (pTDStruct, NULL); + + // + // Depth first fashion + // + SetTDLinkPtrDepthorBreadth (pTDStruct, TRUE); + + // + // initialize as the last TD in the QH context, + // this field will be updated in the TD linkage process. + // + SetTDLinkPtrValidorInvalid (pTDStruct, FALSE); + + // + // Disable Short Packet Detection by default + // + EnableorDisableTDShortPacket (pTDStruct, FALSE); + + // + // Max error counter is 3, retry 3 times when error encountered. + // + SetTDControlErrorCounter (pTDStruct, 3); + + // + // set device speed attribute + // (TRUE - Slow Device; FALSE - Full Speed Device) + // + SetTDLoworFullSpeedDevice (pTDStruct, bSlow); + + // + // Non isochronous transfer TD + // + SetTDControlIsochronousorNot (pTDStruct, FALSE); + + // + // Interrupt On Complete bit be set to zero, + // Disable IOC interrupt. + // + SetorClearTDControlIOC (pTDStruct, FALSE); + + // + // Set TD Active bit + // + SetTDStatusActiveorInactive (pTDStruct, TRUE); + + SetTDTokenMaxLength (pTDStruct, RequestLen); + + SetTDTokenDataToggle0 (pTDStruct); + + SetTDTokenEndPoint (pTDStruct, Endpoint); + + SetTDTokenDeviceAddress (pTDStruct, DevAddr); + + SetTDTokenPacketID (pTDStruct, SETUP_PACKET_ID); + + pTDStruct->pTDBuffer = (UINT8 *) pDevReq; + pTDStruct->TDBufferLength = RequestLen; + SetTDDataBuffer (pTDStruct); + + *ppTD = pTDStruct; + + return EFI_SUCCESS; +} + +EFI_STATUS +GenDataTD ( + IN USB_HC_DEV *HcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 *pData, + IN UINT8 Len, + IN UINT8 PktID, + IN UINT8 Toggle, + IN BOOLEAN bSlow, + OUT TD_STRUCT **ppTD + ) +/*++ + +Routine Description: + + Generate Data Stage TD + +Arguments: + + HcDev - USB_HC_DEV + DevAddr - Device address + Endpoint - Endpoint number + pData - Data buffer + Len - Data length + PktID - Packet ID + Toggle - Data toggle value + bSlow - Full speed or low speed + ppTD - TD_STRUCT to return +Returns: + + EFI_OUT_OF_RESOURCES - Can't allocate memory + EFI_SUCCESS - Success + +--*/ +{ + TD_STRUCT *pTDStruct; + EFI_STATUS Status; + + Status = CreateTD (HcDev, &pTDStruct); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + SetTDLinkPtr (pTDStruct, NULL); + + // + // Depth first fashion + // + SetTDLinkPtrDepthorBreadth (pTDStruct, TRUE); + + // + // Link pointer pointing to TD struct + // + SetTDLinkPtrQHorTDSelect (pTDStruct, FALSE); + + // + // initialize as the last TD in the QH context, + // this field will be updated in the TD linkage process. + // + SetTDLinkPtrValidorInvalid (pTDStruct, FALSE); + + // + // Disable short packet detect + // + EnableorDisableTDShortPacket (pTDStruct, FALSE); + // + // Max error counter is 3 + // + SetTDControlErrorCounter (pTDStruct, 3); + + // + // set device speed attribute + // (TRUE - Slow Device; FALSE - Full Speed Device) + // + SetTDLoworFullSpeedDevice (pTDStruct, bSlow); + + // + // Non isochronous transfer TD + // + SetTDControlIsochronousorNot (pTDStruct, FALSE); + + // + // Disable Interrupt On Complete + // Disable IOC interrupt. + // + SetorClearTDControlIOC (pTDStruct, FALSE); + + // + // Set Active bit + // + SetTDStatusActiveorInactive (pTDStruct, TRUE); + + SetTDTokenMaxLength (pTDStruct, Len); + + if (Toggle) { + SetTDTokenDataToggle1 (pTDStruct); + } else { + SetTDTokenDataToggle0 (pTDStruct); + } + + SetTDTokenEndPoint (pTDStruct, Endpoint); + + SetTDTokenDeviceAddress (pTDStruct, DevAddr); + + SetTDTokenPacketID (pTDStruct, PktID); + + pTDStruct->pTDBuffer = (UINT8 *) pData; + pTDStruct->TDBufferLength = Len; + SetTDDataBuffer (pTDStruct); + *ppTD = pTDStruct; + + return EFI_SUCCESS; +} + + +EFI_STATUS +CreateStatusTD ( + IN USB_HC_DEV *HcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 PktID, + IN BOOLEAN bSlow, + OUT TD_STRUCT **ppTD + ) +/*++ + +Routine Description: + + Generate Status Stage TD + +Arguments: + + HcDev - USB_HC_DEV + DevAddr - Device address + Endpoint - Endpoint number + PktID - Packet ID + bSlow - Full speed or low speed + ppTD - TD_STRUCT to return +Returns: + + EFI_OUT_OF_RESOURCES - Can't allocate memory + EFI_SUCCESS - Success + +--*/ +{ + TD_STRUCT *ptrTDStruct; + EFI_STATUS Status; + + Status = CreateTD (HcDev, &ptrTDStruct); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + SetTDLinkPtr (ptrTDStruct, NULL); + + // + // Depth first fashion + // + SetTDLinkPtrDepthorBreadth (ptrTDStruct, TRUE); + + // + // initialize as the last TD in the QH context, + // this field will be updated in the TD linkage process. + // + SetTDLinkPtrValidorInvalid (ptrTDStruct, FALSE); + + // + // Disable short packet detect + // + EnableorDisableTDShortPacket (ptrTDStruct, FALSE); + + // + // Max error counter is 3 + // + SetTDControlErrorCounter (ptrTDStruct, 3); + + // + // set device speed attribute + // (TRUE - Slow Device; FALSE - Full Speed Device) + // + SetTDLoworFullSpeedDevice (ptrTDStruct, bSlow); + + // + // Non isochronous transfer TD + // + SetTDControlIsochronousorNot (ptrTDStruct, FALSE); + + // + // Disable Interrupt On Complete + // Disable IOC interrupt. + // + SetorClearTDControlIOC (ptrTDStruct, FALSE); + + // + // Set TD Active bit + // + SetTDStatusActiveorInactive (ptrTDStruct, TRUE); + + SetTDTokenMaxLength (ptrTDStruct, 0); + + SetTDTokenDataToggle1 (ptrTDStruct); + + SetTDTokenEndPoint (ptrTDStruct, Endpoint); + + SetTDTokenDeviceAddress (ptrTDStruct, DevAddr); + + SetTDTokenPacketID (ptrTDStruct, PktID); + + ptrTDStruct->pTDBuffer = NULL; + ptrTDStruct->TDBufferLength = 0; + SetTDDataBuffer (ptrTDStruct); + + *ppTD = ptrTDStruct; + + return EFI_SUCCESS; +} + + +VOID +SetTDLinkPtrValidorInvalid ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bValid + ) +/*++ + +Routine Description: + + Set TD Link Pointer Valid or Invalid + +Arguments: + + ptrTDStruct - TD_STRUCT + bValid - TRUE is valid FALSE is invalid + +Returns: + + VOID + +--*/ +{ + // + // Valid means the link pointer is valid, + // else, it's invalid. + // + ptrTDStruct->TDData.TDLinkPtrTerminate = (bValid ? 0 : 1); +} + +VOID +SetTDLinkPtrQHorTDSelect ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bQH + ) +/*++ + +Routine Description: + + Set TD Link Pointer QH or TD Select + +Arguments: + + ptrTDStruct - TD_STRUCT + bQH - TRUE is QH FALSE is TD + +Returns: + + VOID + +--*/ +{ + // + // Indicate whether the Link Pointer pointing to a QH or TD + // + ptrTDStruct->TDData.TDLinkPtrQSelect = (bQH ? 1 : 0); +} + +VOID +SetTDLinkPtrDepthorBreadth ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bDepth + ) +/*++ + +Routine Description: + + Set TD Link Pointer depth or bread priority + +Arguments: + + ptrTDStruct - TD_STRUCT + bDepth - TRUE is Depth FALSE is Breadth + +Returns: + + VOID + +--*/ +{ + // + // If TRUE, indicating the host controller should process in depth first + // fashion, + // else, the host controller should process in breadth first fashion + // + ptrTDStruct->TDData.TDLinkPtrDepthSelect = (bDepth ? 1 : 0); +} + +VOID +SetTDLinkPtr ( + IN TD_STRUCT *ptrTDStruct, + IN VOID *ptrNext + ) +/*++ + +Routine Description: + + Set TD Link Pointer + +Arguments: + + ptrTDStruct - TD_STRUCT + ptrNext - Pointer to set + +Returns: + + VOID + +--*/ +{ + // + // Set TD Link Pointer. Since QH,TD align on 16-byte boundaries, + // only the highest 28 bits are valid. (if take 32bit address as an example) + // + ptrTDStruct->TDData.TDLinkPtr = (UINT32) ((UINTN) ptrNext >> 4); +} + +VOID * +GetTDLinkPtr ( + IN TD_STRUCT *ptrTDStruct + ) +/*++ + +Routine Description: + + Get TD Link Pointer + +Arguments: + + ptrTDStruct - TD_STRUCT + +Returns: + + Pointer to get + +--*/ +{ + // + // Get TD Link Pointer. Restore it back to 32bit + // (if take 32bit address as an example) + // + return (VOID *) ((UINTN) (ptrTDStruct->TDData.TDLinkPtr << 4)); +} + +BOOLEAN +IsTDLinkPtrQHOrTD ( + IN TD_STRUCT *ptrTDStruct + ) +/*++ + +Routine Description: + + Is TD Link Pointer is QH Or TD + +Arguments: + + ptrTDStruct - TODO: add argument description + +Returns: + + TRUE - QH + FALSE - TD + +--*/ +{ + // + // Get the information about whether the Link Pointer field pointing to + // a QH or a TD. + // + return (BOOLEAN) (ptrTDStruct->TDData.TDLinkPtrQSelect); +} + +VOID +EnableorDisableTDShortPacket ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bEnable + ) +/*++ + +Routine Description: + + Enable or Disable TD ShortPacket + +Arguments: + + ptrTDStruct - TD_STRUCT + bEnable - TRUE is Enanble FALSE is Disable + +Returns: + + VOID + +--*/ +{ + // + // TRUE means enable short packet detection mechanism. + // + ptrTDStruct->TDData.TDStatusSPD = (bEnable ? 1 : 0); +} + +VOID +SetTDControlErrorCounter ( + IN TD_STRUCT *ptrTDStruct, + IN UINT8 nMaxErrors + ) +/*++ + +Routine Description: + + Set TD Control ErrorCounter + +Arguments: + + ptrTDStruct - TD_STRUCT + nMaxErrors - Error counter number + +Returns: + + VOID + +--*/ +{ + // + // valid value of nMaxErrors is 0,1,2,3 + // + if (nMaxErrors > 3) { + nMaxErrors = 3; + } + + ptrTDStruct->TDData.TDStatusErr = nMaxErrors; +} + + +VOID +SetTDLoworFullSpeedDevice ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bLowSpeedDevice + ) +{ + // + // TRUE means the TD is targeting at a Low-speed device + // + ptrTDStruct->TDData.TDStatusLS = (bLowSpeedDevice ? 1 : 0); +} + +VOID +SetTDControlIsochronousorNot ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN IsIsochronous + ) +{ + // + // TRUE means the TD belongs to Isochronous transfer type. + // + ptrTDStruct->TDData.TDStatusIOS = (IsIsochronous ? 1 : 0); +} + +VOID +SetorClearTDControlIOC ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN IsSet + ) +{ + // + // If this bit is set, it indicates that the host controller should issue + // an interrupt on completion of the frame in which this TD is executed. + // + ptrTDStruct->TDData.TDStatusIOC = IsSet ? 1 : 0; +} + +VOID +SetTDStatusActiveorInactive ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN IsActive + ) +{ + // + // If this bit is set, it indicates that the TD is active and can be + // executed. + // + if (IsActive) { + ptrTDStruct->TDData.TDStatus |= 0x80; + } else { + ptrTDStruct->TDData.TDStatus &= 0x7F; + } +} + +UINT16 +SetTDTokenMaxLength ( + IN TD_STRUCT *ptrTDStruct, + IN UINT16 MaximumLength + ) +{ + // + // Specifies the maximum number of data bytes allowed for the transfer. + // the legal value extent is 0 ~ 0x500. + // + if (MaximumLength > 0x500) { + MaximumLength = 0x500; + } + ptrTDStruct->TDData.TDTokenMaxLen = MaximumLength - 1; + + return MaximumLength; +} + +VOID +SetTDTokenDataToggle1 ( + IN TD_STRUCT *ptrTDStruct + ) +{ + // + // Set the data toggle bit to DATA1 + // + ptrTDStruct->TDData.TDTokenDataToggle = 1; +} + +VOID +SetTDTokenDataToggle0 ( + IN TD_STRUCT *ptrTDStruct + ) +{ + // + // Set the data toggle bit to DATA0 + // + ptrTDStruct->TDData.TDTokenDataToggle = 0; +} + +UINT8 +GetTDTokenDataToggle ( + IN TD_STRUCT *ptrTDStruct + ) +{ + // + // Get the data toggle value. + // + return (UINT8) (ptrTDStruct->TDData.TDTokenDataToggle); +} + +VOID +SetTDTokenEndPoint ( + IN TD_STRUCT *ptrTDStruct, + IN UINTN EndPoint + ) +{ + // + // Set EndPoint Number the TD is targeting at. + // + ptrTDStruct->TDData.TDTokenEndPt = (UINT8) EndPoint; +} + +VOID +SetTDTokenDeviceAddress ( + IN TD_STRUCT *ptrTDStruct, + IN UINTN DeviceAddress + ) +{ + // + // Set Device Address the TD is targeting at. + // + ptrTDStruct->TDData.TDTokenDevAddr = (UINT8) DeviceAddress; +} + +VOID +SetTDTokenPacketID ( + IN TD_STRUCT *ptrTDStruct, + IN UINT8 PID + ) +{ + // + // Set the Packet Identification to be used for this transaction. + // + ptrTDStruct->TDData.TDTokenPID = PID; +} + +VOID +SetTDDataBuffer ( + IN TD_STRUCT *ptrTDStruct + ) +{ + // + // Set the beginning address of the data buffer that will be used + // during the transaction. + // + ptrTDStruct->TDData.TDBufferPtr = (UINT32) ((UINTN) (ptrTDStruct->pTDBuffer)); +} + +BOOLEAN +IsTDStatusActive ( + IN TD_STRUCT *ptrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether the TD is active. + // + TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x80); +} + +BOOLEAN +IsTDStatusStalled ( + IN TD_STRUCT *ptrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether the device/endpoint addressed by this TD is stalled. + // + TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x40); +} + +BOOLEAN +IsTDStatusBufferError ( + IN TD_STRUCT *ptrTDStruct + ) +{ + UINT8 TDStatus; + // + // Detect whether Data Buffer Error is happened. + // + TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x20); +} + +BOOLEAN +IsTDStatusBabbleError ( + IN TD_STRUCT *ptrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether Babble Error is happened. + // + TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x10); +} + +BOOLEAN +IsTDStatusNAKReceived ( + IN TD_STRUCT *ptrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether NAK is received. + // + TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x08); +} + +BOOLEAN +IsTDStatusCRCTimeOutError ( + IN TD_STRUCT *ptrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether CRC/Time Out Error is encountered. + // + TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x04); +} + +BOOLEAN +IsTDStatusBitStuffError ( + IN TD_STRUCT *ptrTDStruct + ) +{ + UINT8 TDStatus; + + // + // Detect whether Bitstuff Error is received. + // + TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus); + return (BOOLEAN) (TDStatus & 0x02); +} + +UINT16 +GetTDStatusActualLength ( + IN TD_STRUCT *ptrTDStruct + ) +{ + // + // Retrieve the actual number of bytes that were tansferred. + // the value is encoded as n-1. so return the decoded value. + // + return (UINT16) ((ptrTDStruct->TDData.TDStatusActualLength) + 1); +} + +UINT16 +GetTDTokenMaxLength ( + IN TD_STRUCT *ptrTDStruct + ) +{ + // + // Retrieve the maximum number of data bytes allowed for the trnasfer. + // + return (UINT16) ((ptrTDStruct->TDData.TDTokenMaxLen) + 1); +} + +UINT8 +GetTDTokenEndPoint ( + IN TD_STRUCT *ptrTDStruct + ) +{ + // + // Retrieve the endpoint number the transaction is targeting at. + // + return (UINT8) (ptrTDStruct->TDData.TDTokenEndPt); +} + +UINT8 +GetTDTokenDeviceAddress ( + IN TD_STRUCT *ptrTDStruct + ) +{ + // + // Retrieve the device address the transaction is targeting at. + // + return (UINT8) (ptrTDStruct->TDData.TDTokenDevAddr); +} + +UINT8 +GetTDTokenPacketID ( + IN TD_STRUCT *ptrTDStruct + ) +{ + // + // Retrieve the Packet Identification information. + // + return (UINT8) (ptrTDStruct->TDData.TDTokenPID); +} + +UINT8 * +GetTDDataBuffer ( + IN TD_STRUCT *ptrTDStruct + ) +{ + // + // Retrieve the beginning address of the data buffer + // that involved in this transaction. + // + return ptrTDStruct->pTDBuffer; +} + +BOOLEAN +GetTDLinkPtrValidorInvalid ( + IN TD_STRUCT *ptrTDStruct + ) +{ + // + // Retrieve the information of whether the Link Pointer field + // is valid or not. + // + if (ptrTDStruct->TDData.TDLinkPtrTerminate) { + return FALSE; + } else { + return TRUE; + } + +} + +UINTN +CountTDsNumber ( + IN TD_STRUCT *PtrFirstTD + ) +{ + UINTN Number; + TD_STRUCT *ptr; + // + // Count the queued TDs number. + // + Number = 0; + ptr = PtrFirstTD; + while (ptr) { + ptr = (TD_STRUCT *) ptr->ptrNextTD; + Number++; + } + + return Number; +} + + + +VOID +LinkTDToQH ( + IN QH_STRUCT *PtrQH, + IN TD_STRUCT *PtrTD + ) +/*++ + +Routine Description: + + Link TD To QH + +Arguments: + + PtrQH - QH_STRUCT + PtrTD - TD_STRUCT +Returns: + + VOID + +--*/ +{ + if (PtrQH == NULL || PtrTD == NULL) { + return ; + } + // + // Validate QH Vertical Ptr field + // + SetQHVerticalValidorInvalid (PtrQH, TRUE); + + // + // Vertical Ptr pointing to TD structure + // + SetQHVerticalQHorTDSelect (PtrQH, FALSE); + + SetQHVerticalLinkPtr (PtrQH, (VOID *) PtrTD); + + PtrQH->ptrDown = (VOID *) PtrTD; +} + +VOID +LinkTDToTD ( + IN TD_STRUCT *ptrPreTD, + IN TD_STRUCT *PtrTD + ) +/*++ + +Routine Description: + + Link TD To TD + +Arguments: + + ptrPreTD - Previous TD_STRUCT to be linked + PtrTD - TD_STRUCT to link +Returns: + + VOID + +--*/ +{ + if (ptrPreTD == NULL || PtrTD == NULL) { + return ; + } + // + // Depth first fashion + // + SetTDLinkPtrDepthorBreadth (ptrPreTD, TRUE); + + // + // Link pointer pointing to TD struct + // + SetTDLinkPtrQHorTDSelect (ptrPreTD, FALSE); + + // + // Validate the link pointer valid bit + // + SetTDLinkPtrValidorInvalid (ptrPreTD, TRUE); + + SetTDLinkPtr (ptrPreTD, PtrTD); + + ptrPreTD->ptrNextTD = (VOID *) PtrTD; +} +// +// Transfer Schedule related Helper Functions +// +VOID +SetorClearCurFrameListTerminate ( + IN FRAMELIST_ENTRY *pCurEntry, + IN BOOLEAN IsSet + ) +{ + // + // If TRUE, empty the frame. If FALSE, indicate the Pointer field is valid. + // + pCurEntry->FrameListPtrTerminate = (IsSet ? 1 : 0); +} + +VOID +SetCurFrameListQHorTD ( + IN FRAMELIST_ENTRY *pCurEntry, + IN BOOLEAN IsQH + ) +{ + // + // This bit indicates to the hardware whether the item referenced by the + // link pointer is a TD or a QH. + // + pCurEntry->FrameListPtrQSelect = (IsQH ? 1 : 0); +} + +BOOLEAN +IsCurFrameListQHorTD ( + IN FRAMELIST_ENTRY *pCurEntry + ) +{ + // + // TRUE is QH + // FALSE is TD + // + return (BOOLEAN) (pCurEntry->FrameListPtrQSelect); +} + +BOOLEAN +GetCurFrameListTerminate ( + IN FRAMELIST_ENTRY *pCurEntry + ) +{ + // + // TRUE means the frame is empty, + // FALSE means the link pointer field is valid. + // + return (BOOLEAN) (pCurEntry->FrameListPtrTerminate); +} + +VOID +SetCurFrameListPointer ( + IN FRAMELIST_ENTRY *pCurEntry, + IN UINT8 *ptr + ) +{ + // + // Set the pointer field of the frame. + // + pCurEntry->FrameListPtr = (UINT32) ((UINTN) ptr >> 4); +} + +VOID * +GetCurFrameListPointer ( + IN FRAMELIST_ENTRY *pCurEntry + ) +{ + // + // Get the link pointer of the frame. + // + return (VOID *) ((UINTN) (pCurEntry->FrameListPtr << 4)); + +} + +VOID +LinkQHToFrameList ( + IN FRAMELIST_ENTRY *pEntry, + IN UINT16 FrameListIndex, + IN QH_STRUCT *PtrQH + ) +/*++ + +Routine Description: + + Link QH To Frame List + +Arguments: + + pEntry - FRAMELIST_ENTRY + FrameListIndex - Frame List Index + PtrQH - QH to link +Returns: + + VOID + +--*/ +{ + FRAMELIST_ENTRY *pCurFrame; + QH_STRUCT *TempQH; + QH_STRUCT *NextTempQH; + TD_STRUCT *TempTD; + BOOLEAN LINK; + + // + // Get frame list entry that the link process will begin from. + // + pCurFrame = pEntry + FrameListIndex; + + // + // if current frame is empty + // then link the specified QH directly to the Frame List. + // + if (GetCurFrameListTerminate (pCurFrame)) { + + // + // Link new QH to the frame list entry. + // + SetCurFrameListQHorTD (pCurFrame, TRUE); + + SetCurFrameListPointer (pCurFrame, (UINT8 *) PtrQH); + + // + // clear T bit in the Frame List, indicating that the frame list entry + // is no longer empty. + // + SetorClearCurFrameListTerminate (pCurFrame, FALSE); + + return ; + + } else { + // + // current frame list has link pointer + // + if (!IsCurFrameListQHorTD (pCurFrame)) { + // + // a TD is linked to the framelist entry + // + TempTD = (TD_STRUCT *) GetCurFrameListPointer (pCurFrame); + + while (GetTDLinkPtrValidorInvalid (TempTD)) { + + if (IsTDLinkPtrQHOrTD (TempTD)) { + // + // QH linked next to the TD + // + break; + } + + TempTD = (TD_STRUCT *) GetTDLinkPtr (TempTD); + } + + // + // either no ptr linked next to the TD or QH is linked next to the TD + // + if (!GetTDLinkPtrValidorInvalid (TempTD)) { + + // + // no ptr linked next to the TD + // + TempTD->ptrNextQH = PtrQH; + SetTDLinkPtrQHorTDSelect (TempTD, TRUE); + SetTDLinkPtr (TempTD, PtrQH); + SetTDLinkPtrValidorInvalid (TempTD, TRUE); + return ; + + } else { + // + // QH is linked next to the TD + // + TempQH = (QH_STRUCT *) GetTDLinkPtr (TempTD); + } + } else { + // + // a QH is linked to the framelist entry + // + TempQH = (QH_STRUCT *) GetCurFrameListPointer (pCurFrame); + } + + // + // Set up Flag + // + LINK = TRUE; + + // + // Avoid the same qh repeated linking in one frame entry + // + if (TempQH == PtrQH) { + LINK = FALSE; + return ; + } + // + // if current QH has next QH connected + // + while (GetQHHorizontalValidorInvalid (TempQH)) { + // + // Get next QH pointer + // + NextTempQH = (QH_STRUCT *) GetQHHorizontalLinkPtr (TempQH); + + // + // Bulk transfer qh may be self-linked, + // so, the code below is to aVOID dead-loop when meeting self-linked qh + // + if (NextTempQH == TempQH) { + LINK = FALSE; + break; + } + + TempQH = NextTempQH; + + // + // Avoid the same qh repeated linking in one frame entry + // + if (TempQH == PtrQH) { + LINK = FALSE; + } + } + + if (LINK) { + TempQH->ptrNext = PtrQH; + SetQHHorizontalQHorTDSelect (TempQH, TRUE); + SetQHHorizontalLinkPtr (TempQH, PtrQH); + SetQHHorizontalValidorInvalid (TempQH, TRUE); + } + + return ; + } +} + +EFI_STATUS +ExecuteControlTransfer ( + IN USB_HC_DEV *HcDev, + IN TD_STRUCT *PtrTD, + IN UINT32 wIndex, + OUT UINTN *ActualLen, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +/*++ + +Routine Description: + + Execute Control Transfer + +Arguments: + + HcDev - USB_HC_DEV + PtrTD - TD_STRUCT + wIndex - No use + ActualLen - Actual transfered Len + TimeOut - TimeOut value in milliseconds + TransferResult - Transfer result +Returns: + + EFI_SUCCESS - Sucess + EFI_DEVICE_ERROR - Error + + +--*/ +{ + UINTN ErrTDPos; + UINTN Delay; + UINTN RequiredLen; + BOOLEAN TransferFinished; + + ErrTDPos = 0; + *TransferResult = EFI_USB_NOERROR; + RequiredLen = *ActualLen; + *ActualLen = 0; + + Delay = (TimeOut * STALL_1_MILLI_SECOND / 50) + 1; + + do { + TransferFinished = CheckTDsResults ( + PtrTD, + RequiredLen, + TransferResult, + &ErrTDPos, + ActualLen + ); + + if (TransferFinished) { + break; + } + + // + // TD is inactive, which means the control transfer is end. + // + if ((*TransferResult & EFI_USB_ERR_NOTEXECUTE) != EFI_USB_ERR_NOTEXECUTE) { + break; + } + + gBS->Stall (50); + + } while (Delay--); + + if (*TransferResult != EFI_USB_NOERROR) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +ExecBulkorSyncInterruptTransfer ( + IN USB_HC_DEV *HcDev, + IN TD_STRUCT *PtrTD, + IN UINT32 wIndex, + OUT UINTN *ActualLen, + OUT UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +/*++ + +Routine Description: + + Execute Bulk or SyncInterrupt Transfer + +Arguments: + + HcDev - USB_HC_DEV + PtrTD - TD_STRUCT + wIndex - No use + ActualLen - Actual transfered Len + DataToggle - Data Toggle + TimeOut - TimeOut value in milliseconds + TransferResult - Transfer result +Returns: + + EFI_SUCCESS - Sucess + EFI_DEVICE_ERROR - Error +--*/ +{ + UINTN ErrTDPos; + UINTN ScrollNum; + UINTN Delay; + UINTN RequiredLen; + BOOLEAN TransferFinished; + + ErrTDPos = 0; + *TransferResult = EFI_USB_NOERROR; + RequiredLen = *ActualLen; + *ActualLen = 0; + + Delay = (TimeOut * STALL_1_MILLI_SECOND / 50) + 1; + + do { + + TransferFinished = CheckTDsResults ( + PtrTD, + RequiredLen, + TransferResult, + &ErrTDPos, + ActualLen + ); + + if (TransferFinished) { + break; + } + + // + // TD is inactive, which means bulk or interrupt transfer's end. + // + if ((*TransferResult & EFI_USB_ERR_NOTEXECUTE) != EFI_USB_ERR_NOTEXECUTE) { + break; + } + + gBS->Stall (50); + + } while (Delay--); + + // + // has error + // + if (*TransferResult != EFI_USB_NOERROR) { + + // + // scroll the Data Toggle back to the last success TD + // + ScrollNum = CountTDsNumber (PtrTD) - ErrTDPos; + if (ScrollNum & 0x1) { + *DataToggle ^= 1; + } + + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +VOID +DelLinkSingleQH ( + IN USB_HC_DEV *HcDev, + IN QH_STRUCT *PtrQH, + IN UINT16 FrameListIndex, + IN BOOLEAN SearchOther, + IN BOOLEAN Delete + ) +/*++ + +Routine Description: + + Unlink from frame list and delete single QH +Arguments: + + HcDev - USB_HC_DEV + PtrQH - QH_STRUCT + FrameListIndex - Frame List Index + SearchOther - Search Other QH + Delete - TRUE is to delete the QH +Returns: + VOID +--*/ +{ + FRAMELIST_ENTRY *pCurFrame; + UINTN Index; + UINTN BeginFrame; + UINTN EndFrame; + QH_STRUCT *CurrentQH; + QH_STRUCT *NextQH; + TD_STRUCT *CurrentTD; + VOID *PtrPreQH; + BOOLEAN Found; + + NextQH = NULL; + PtrPreQH = NULL; + Found = FALSE; + + if (PtrQH == NULL) { + return ; + } + + if (SearchOther) { + BeginFrame = 0; + EndFrame = 1024; + } else { + BeginFrame = FrameListIndex; + EndFrame = FrameListIndex + 1; + } + + for (Index = BeginFrame; Index < EndFrame; Index++) { + + pCurFrame = HcDev->FrameListEntry + (Index & 0x3FF); + + if (GetCurFrameListTerminate (pCurFrame)) { + // + // current frame list is empty,search next frame list entry + // + continue; + } + + if (!IsCurFrameListQHorTD (pCurFrame)) { + // + // TD linked to current framelist + // + CurrentTD = (TD_STRUCT *) GetCurFrameListPointer (pCurFrame); + + while (GetTDLinkPtrValidorInvalid (CurrentTD)) { + + if (IsTDLinkPtrQHOrTD (CurrentTD)) { + // + // QH linked next to the TD,break while () + // + break; + } + + CurrentTD = (TD_STRUCT *) GetTDLinkPtr (CurrentTD); + } + + if (!GetTDLinkPtrValidorInvalid (CurrentTD)) { + // + // no QH linked next to the last TD, + // search next frame list + // + continue; + } + + // + // a QH linked next to the last TD + // + CurrentQH = (QH_STRUCT *) GetTDLinkPtr (CurrentTD); + + PtrPreQH = CurrentTD; + + } else { + // + // a QH linked to current framelist + // + CurrentQH = (QH_STRUCT *) GetCurFrameListPointer (pCurFrame); + + PtrPreQH = NULL; + } + + if (CurrentQH == PtrQH) { + + if (GetQHHorizontalValidorInvalid (PtrQH)) { + // + // there is QH connected after the QH found + // + // + // retrieve nex qh pointer of the qh found. + // + NextQH = GetQHHorizontalLinkPtr (PtrQH); + } else { + NextQH = NULL; + } + + if (PtrPreQH) { + // + // QH linked to a TD struct + // + CurrentTD = (TD_STRUCT *) PtrPreQH; + + SetTDLinkPtrValidorInvalid (CurrentTD, (BOOLEAN) ((NextQH == NULL) ? FALSE : TRUE)); + SetTDLinkPtr (CurrentTD, NextQH); + CurrentTD->ptrNextQH = NextQH; + + } else { + // + // QH linked directly to current framelist entry + // + SetorClearCurFrameListTerminate (pCurFrame, (BOOLEAN) ((NextQH == NULL) ? TRUE : FALSE)); + SetCurFrameListPointer (pCurFrame, (UINT8 *) NextQH); + } + + Found = TRUE; + // + // search next framelist entry + // + continue; + } + + while (GetQHHorizontalValidorInvalid (CurrentQH)) { + + PtrPreQH = CurrentQH; + // + // Get next horizontal linked QH + // + CurrentQH = (QH_STRUCT *) GetQHHorizontalLinkPtr (CurrentQH); + // + // the qh is found + // + if (CurrentQH == PtrQH) { + break; + } + } + + // + // search next frame list entry + // + if (CurrentQH != PtrQH) { + // + // Not find the QH + // + continue; + } + // + // find the specified qh, then delink it from + // the horizontal QH list in the frame entry. + // + + if (GetQHHorizontalValidorInvalid (PtrQH)) { + // + // there is QH connected after the QH found + // + // + // retrieve nex qh pointer of the qh found. + // + NextQH = GetQHHorizontalLinkPtr (PtrQH); + + } else { + // + // NO QH connected after the QH found + // + NextQH = NULL; + // + // NULL the previous QH's link ptr and set Terminate field. + // + SetQHHorizontalValidorInvalid ((QH_STRUCT *) PtrPreQH, FALSE); + } + + SetQHHorizontalLinkPtr ((QH_STRUCT *) PtrPreQH, NextQH); + ((QH_STRUCT *) PtrPreQH)->ptrNext = NextQH; + + Found = TRUE; + } + + if (Found && Delete) { + // + // free memory once used by the specific QH + // + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + } + + return ; +} + + +VOID +DeleteQueuedTDs ( + IN USB_HC_DEV *HcDev, + IN TD_STRUCT *PtrFirstTD + ) +/*++ +Routine Description: + + Delete Queued TDs +Arguments: + + HcDev - USB_HC_DEV + PtrFirstTD - TD link list head + +Returns: + VOID + +--*/ +{ + TD_STRUCT *Tptr1; + TD_STRUCT *Tptr2; + + Tptr1 = PtrFirstTD; + // + // Delete all the TDs in a queue. + // + while (Tptr1) { + + Tptr2 = Tptr1; + + if (!GetTDLinkPtrValidorInvalid (Tptr2)) { + Tptr1 = NULL; + } else { + + Tptr1 = GetTDLinkPtr (Tptr2); + + // + // TD link to itself + // + if (Tptr1 == Tptr2) { + Tptr1 = NULL; + } + } + + UhciFreePool (HcDev, (UINT8 *) Tptr2, sizeof (TD_STRUCT)); + } + + return ; +} + +VOID +InsertQHTDToINTList ( + IN USB_HC_DEV *HcDev, + IN QH_STRUCT *PtrQH, + IN TD_STRUCT *PtrFirstTD, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DataToggle, + IN UINTN DataLength, + IN UINTN PollingInterval, + IN VOID *Mapping, + IN UINT8 *DataBuffer, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, + IN VOID *Context + ) +/*++ +Routine Description: + Insert QH and TD To Interrupt List +Arguments: + + HcDev - USB_HC_DEV + PtrQH - QH_STRUCT + PtrFirstTD - First TD_STRUCT + DeviceAddress - Device Address + EndPointAddress - EndPoint Address + DataToggle - Data Toggle + DataLength - Data length + PollingInterval - Polling Interval when inserted to frame list + Mapping - Mapping alue + DataBuffer - Data buffer + CallBackFunction- CallBackFunction after interrupt transfeer + Context - CallBackFunction Context passed as function parameter +Returns: + EFI_SUCCESS - Sucess + EFI_INVALID_PARAMETER - Paremeter is error + +--*/ +{ + INTERRUPT_LIST *Node; + + Node = AllocatePool (sizeof (INTERRUPT_LIST)); + if (Node == NULL) { + return ; + } + + // + // Fill Node field + // + Node->Signature = INTERRUPT_LIST_SIGNATURE; + Node->DevAddr = DeviceAddress; + Node->EndPoint = EndPointAddress; + Node->PtrQH = PtrQH; + Node->PtrFirstTD = PtrFirstTD; + Node->DataToggle = DataToggle; + Node->DataLen = DataLength; + Node->PollInterval = PollingInterval; + Node->Mapping = Mapping; + // + // DataBuffer is allocated host memory, not mapped memory + // + Node->DataBuffer = DataBuffer; + Node->InterruptCallBack = CallBackFunction; + Node->InterruptContext = Context; + + // + // insert the new interrupt transfer to the head of the list. + // The interrupt transfer's monitor function scans the whole list from head + // to tail. The new interrupt transfer MUST be added to the head of the list + // for the sake of error recovery. + // + InsertHeadList (&(HcDev->InterruptListHead), &(Node->Link)); + + return ; +} + + +EFI_STATUS +DeleteAsyncINTQHTDs ( + IN USB_HC_DEV *HcDev, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + OUT UINT8 *DataToggle + ) +/*++ +Routine Description: + + Delete Async INT QH and TDs +Arguments: + + HcDev - USB_HC_DEV + DeviceAddress - Device Address + EndPointAddress - EndPoint Address + DataToggle - Data Toggle + +Returns: + EFI_SUCCESS - Sucess + EFI_INVALID_PARAMETER - Paremeter is error + +--*/ +{ + QH_STRUCT *MatchQH; + QH_STRUCT *ptrNextQH; + TD_STRUCT *MatchTD; + LIST_ENTRY *Link; + INTERRUPT_LIST *MatchList; + INTERRUPT_LIST *PtrList; + BOOLEAN Found; + + UINT32 Result; + UINTN ErrTDPos; + UINTN ActualLen; + + MatchQH = NULL; + MatchTD = NULL; + MatchList = NULL; + + // + // no interrupt transaction exists + // + if (IsListEmpty (&(HcDev->InterruptListHead))) { + return EFI_INVALID_PARAMETER; + } + // + // find the correct QH-TD that need to delete + // (by matching Device address and EndPoint number to match QH-TD ) + // + Found = FALSE; + Link = &(HcDev->InterruptListHead); + do { + + Link = Link->ForwardLink; + PtrList = INTERRUPT_LIST_FROM_LINK (Link); + + if ((PtrList->DevAddr == DeviceAddress) && ((PtrList->EndPoint & 0x0f) == (EndPointAddress & 0x0f))) { + MatchList = PtrList; + + Found = TRUE; + break; + } + + } while (Link->ForwardLink != &(HcDev->InterruptListHead)); + + if (!Found) { + return EFI_INVALID_PARAMETER; + } + // + // get current endpoint's data toggle bit and save. + // + ExecuteAsyncINTTDs (HcDev, MatchList, &Result, &ErrTDPos, &ActualLen); + UpdateAsyncINTQHTDs (MatchList, Result, (UINT32) ErrTDPos); + *DataToggle = MatchList->DataToggle; + + MatchTD = MatchList->PtrFirstTD; + MatchQH = MatchList->PtrQH; + // + // find the first matching QH position in the FrameList + // + while (MatchQH) { + + ptrNextQH = MatchQH->ptrNextIntQH; + + // + // Search all the entries + // + DelLinkSingleQH (HcDev, MatchQH, 0, TRUE, TRUE); + + MatchQH = ptrNextQH; + } + + // + // Call PciIo->Unmap() to unmap the busmaster read/write + // + HcDev->PciIo->Unmap (HcDev->PciIo, MatchList->Mapping); + + // + // free host data buffer allocated, + // mapped data buffer is freed by Unmap + // + if (MatchList->DataBuffer != NULL) { + gBS->FreePool (MatchList->DataBuffer); + } + + // + // at last delete the TDs, to aVOID problems + // + DeleteQueuedTDs (HcDev, MatchTD); + + // + // remove Match node from interrupt list + // + RemoveEntryList (&(MatchList->Link)); + gBS->FreePool (MatchList); + return EFI_SUCCESS; +} + +BOOLEAN +CheckTDsResults ( + IN TD_STRUCT *PtrTD, + IN UINTN RequiredLen, + OUT UINT32 *Result, + OUT UINTN *ErrTDPos, + OUT UINTN *ActualTransferSize + ) +/*++ + +Routine Description: + + Check TDs Results + +Arguments: + + PtrTD - TD_STRUCT to check + RequiredLen - Required Len + Result - Transfer result + ErrTDPos - Error TD Position + ActualTransferSize - Actual Transfer Size + +Returns: + + TRUE - Sucess + FALSE - Fail + +--*/ +{ + UINTN Len; + + *Result = EFI_USB_NOERROR; + *ErrTDPos = 0; + + // + // Init to zero. + // + *ActualTransferSize = 0; + + while (PtrTD) { + + if (IsTDStatusActive (PtrTD)) { + *Result |= EFI_USB_ERR_NOTEXECUTE; + } + + if (IsTDStatusStalled (PtrTD)) { + *Result |= EFI_USB_ERR_STALL; + } + + if (IsTDStatusBufferError (PtrTD)) { + *Result |= EFI_USB_ERR_BUFFER; + } + + if (IsTDStatusBabbleError (PtrTD)) { + *Result |= EFI_USB_ERR_BABBLE; + } + + if (IsTDStatusNAKReceived (PtrTD)) { + *Result |= EFI_USB_ERR_NAK; + } + + if (IsTDStatusCRCTimeOutError (PtrTD)) { + *Result |= EFI_USB_ERR_TIMEOUT; + } + + if (IsTDStatusBitStuffError (PtrTD)) { + *Result |= EFI_USB_ERR_BITSTUFF; + } + + // + // if any error encountered, stop processing the left TDs. + // + if (*Result) { + return FALSE; + } + + Len = GetTDStatusActualLength (PtrTD) & 0x7FF; + *ActualTransferSize += Len; + + if (*ActualTransferSize <= RequiredLen && Len < PtrTD->TDData.TDTokenMaxLen) { + // + // transter finished and actural length less than required length + // + goto Done; + } + // + // Accumulate actual transferred data length in each TD. + // + PtrTD = (TD_STRUCT *) (PtrTD->ptrNextTD); + // + // Record the first Error TD's position in the queue, + // this value is zero-based. + // + (*ErrTDPos)++; + } + +Done: + return TRUE; +} + + +VOID +ExecuteAsyncINTTDs ( + IN USB_HC_DEV *HcDev, + IN INTERRUPT_LIST *PtrList, + OUT UINT32 *Result, + OUT UINTN *ErrTDPos, + OUT UINTN *ActualLen + ) +/*++ + +Routine Description: + + Execute Async Interrupt TDs + +Arguments: + + HcDev - USB_HC_DEV + PtrList - INTERRUPT_LIST + Result - Transfer result + ErrTDPos - Error TD Position + ActualTransferSize - Actual Transfer Size + +Returns: + + VOID + +--*/ +{ + // + // *ErrTDPos is zero-based value, indicating the first error TD's position + // in the TDs' sequence. + // *ErrTDPos value is only valid when *Result is not equal NOERROR. + // + UINTN RequiredLen; + + RequiredLen = *ActualLen; + CheckTDsResults (PtrList->PtrFirstTD, RequiredLen, Result, ErrTDPos, ActualLen); + + return ; +} + + +VOID +UpdateAsyncINTQHTDs ( + IN INTERRUPT_LIST *PtrList, + IN UINT32 Result, + IN UINT32 ErrTDPos + ) +/*++ + +Routine Description: + + Update Async Interrupt QH and TDs + +Arguments: + + PtrList - INTERRUPT_LIST + Result - Transfer reslut + ErrTDPos - Error TD Position + +Returns: + + VOID + +--*/ +{ + QH_STRUCT *PtrFirstQH; + QH_STRUCT *PtrQH; + TD_STRUCT *PtrFirstTD; + TD_STRUCT *PtrTD; + UINT8 DataToggle; + UINT32 Index; + + PtrFirstQH = PtrList->PtrQH; + PtrFirstTD = PtrList->PtrFirstTD; + + DataToggle = 0; + + if (Result == EFI_USB_NOERROR) { + + PtrTD = PtrFirstTD; + while (PtrTD) { + DataToggle = GetTDTokenDataToggle (PtrTD); + PtrTD = PtrTD->ptrNextTD; + } + + // + // save current DataToggle value to interrupt list. + // this value is used for tracing the interrupt endpoint DataToggle. + // when this interrupt transfer is deleted, the last DataToggle is saved + // + PtrList->DataToggle = DataToggle; + + PtrTD = PtrFirstTD; + + // + // Since DataToggle bit should toggle after each success transaction, + // the First TD's DataToggle bit will be updated to XOR of Last TD's + // DataToggle bit. If the First TD's DataToggle bit is not equal Last + // TD's DataToggle bit, that means it already be the XOR of Last TD's, + // so no update is needed. + // + if (DataToggle == GetTDTokenDataToggle (PtrFirstTD)) { + PtrTD = PtrFirstTD; + while (PtrTD) { + + DataToggle ^= 1; + if (DataToggle) { + SetTDTokenDataToggle1 (PtrTD); + } else { + SetTDTokenDataToggle0 (PtrTD); + } + + PtrTD = PtrTD->ptrNextTD; + } + } + // + // restore Link Pointer of QH to First TD + // (because QH's Link Pointer will change during TD execution) + // + PtrQH = PtrFirstQH; + while (PtrQH) { + + LinkTDToQH (PtrQH, PtrFirstTD); + PtrQH = PtrQH->ptrNextIntQH; + } + + // + // set all the TDs active + // + PtrTD = PtrFirstTD; + while (PtrTD) { + SetTDStatusActiveorInactive (PtrTD, TRUE); + PtrTD = PtrTD->ptrNextTD; + } + + } else if (((Result & EFI_USB_ERR_NOTEXECUTE) == EFI_USB_ERR_NOTEXECUTE) || + ((Result & EFI_USB_ERR_NAK) == EFI_USB_ERR_NAK) + ) { + // + // no update + // + } else { + // + // Have Errors + // + PtrTD = PtrFirstTD; + // + // not first TD error + // + if (ErrTDPos != 0) { + // + // get the last success TD + // + for (Index = 1; Index < ErrTDPos; Index++) { + PtrTD = PtrTD->ptrNextTD; + } + // + // update Data Toggle in the interrupt list node + // + PtrList->DataToggle = GetTDTokenDataToggle (PtrTD); + + // + // get the error TD + // + PtrTD = PtrTD->ptrNextTD; + + } else { + PtrList->DataToggle = GetTDTokenDataToggle (PtrTD); + } + // + // do not restore the QH's vertical link pointer, + // let the callback function do the rest of error handling. + // + } + + return ; +} + +VOID +ReleaseInterruptList ( + IN USB_HC_DEV *HcDev, + IN LIST_ENTRY *ListHead + ) +/*++ + +Routine Description: + + Release Interrupt List +Arguments: + + HcDev - USB_HC_DEV + ListHead - List head + +Returns: + + VOID + +--*/ +{ + LIST_ENTRY *Link; + LIST_ENTRY *SavedLink; + INTERRUPT_LIST *pNode; + TD_STRUCT *PtrTD; + TD_STRUCT *ptrNextTD; + QH_STRUCT *PtrQH; + QH_STRUCT *SavedQH; + + if (ListHead == NULL) { + return ; + } + + Link = ListHead; + + // + // Free all the resources in the interrupt list + // + SavedLink = Link->ForwardLink; + while (!IsListEmpty (ListHead)) { + + Link = SavedLink; + + SavedLink = Link->ForwardLink; + + pNode = INTERRUPT_LIST_FROM_LINK (Link); + + RemoveEntryList (&pNode->Link); + + SavedQH = pNode->PtrQH; + for (PtrQH = SavedQH; PtrQH != NULL; PtrQH = SavedQH) { + SavedQH = PtrQH->ptrNextIntQH; + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + } + + PtrTD = pNode->PtrFirstTD; + while (PtrTD != NULL) { + + ptrNextTD = PtrTD->ptrNextTD; + UhciFreePool (HcDev, (UINT8 *) PtrTD, sizeof (TD_STRUCT)); + PtrTD = ptrNextTD; + } + + gBS->FreePool (pNode); + } +} + + +EFI_STATUS +InitializeMemoryManagement ( + IN USB_HC_DEV *HcDev + ) +/*++ + +Routine Description: + + Initialize Memory Management + +Arguments: + + HcDev - USB_HC_DEV + +Returns: + + EFI_SUCCESS - Success +--*/ +{ + MEMORY_MANAGE_HEADER *MemoryHeader; + EFI_STATUS Status; + UINTN MemPages; + + MemPages = NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES; + Status = CreateMemoryBlock (HcDev, &MemoryHeader, MemPages); + if (EFI_ERROR (Status)) { + return Status; + } + + HcDev->MemoryHeader = MemoryHeader; + + return EFI_SUCCESS; +} + +EFI_STATUS +CreateMemoryBlock ( + IN USB_HC_DEV *HcDev, + OUT MEMORY_MANAGE_HEADER **MemoryHeader, + IN UINTN MemoryBlockSizeInPages + ) +/*++ + +Routine Description: + + Use PciIo->AllocateBuffer to allocate common buffer for the memory block, + and use PciIo->Map to map the common buffer for Bus Master Read/Write. + + +Arguments: + + HcDev - USB_HC_DEV + MemoryHeader - MEMORY_MANAGE_HEADER to output + MemoryBlockSizeInPages - MemoryBlockSizeInPages +Returns: + + EFI_SUCCESS - Success +--*/ +{ + EFI_STATUS Status; + VOID *CommonBuffer; + EFI_PHYSICAL_ADDRESS MappedAddress; + UINTN MemoryBlockSizeInBytes; + VOID *Mapping; + + // + // Allocate memory for MemoryHeader + // + *MemoryHeader = AllocateZeroPool (sizeof (MEMORY_MANAGE_HEADER)); + if (*MemoryHeader == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + (*MemoryHeader)->Next = NULL; + + // + // set Memory block size + // + (*MemoryHeader)->MemoryBlockSizeInBytes = EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages); + + // + // each bit in Bit Array will manage 32 bytes memory in memory block + // + (*MemoryHeader)->BitArraySizeInBytes = ((*MemoryHeader)->MemoryBlockSizeInBytes / 32) / 8; + + // + // Allocate memory for BitArray + // + (*MemoryHeader)->BitArrayPtr = AllocateZeroPool ((*MemoryHeader)->BitArraySizeInBytes); + if ((*MemoryHeader)->BitArrayPtr == NULL) { + gBS->FreePool (*MemoryHeader); + return EFI_OUT_OF_RESOURCES; + } + + // + // Memory Block uses MemoryBlockSizeInPages pages, + // and it is allocated as common buffer use. + // + Status = HcDev->PciIo->AllocateBuffer ( + HcDev->PciIo, + AllocateAnyPages, + EfiBootServicesData, + MemoryBlockSizeInPages, + &CommonBuffer, + 0 + ); + if (EFI_ERROR (Status)) { + gBS->FreePool ((*MemoryHeader)->BitArrayPtr); + gBS->FreePool (*MemoryHeader); + return Status; + } + + MemoryBlockSizeInBytes = EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages); + Status = HcDev->PciIo->Map ( + HcDev->PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + CommonBuffer, + &MemoryBlockSizeInBytes, + &MappedAddress, + &Mapping + ); + // + // if returned Mapped size is less than the size we request,do not support. + // + if (EFI_ERROR (Status) || (MemoryBlockSizeInBytes != EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages))) { + HcDev->PciIo->FreeBuffer (HcDev->PciIo, MemoryBlockSizeInPages, CommonBuffer); + gBS->FreePool ((*MemoryHeader)->BitArrayPtr); + gBS->FreePool (*MemoryHeader); + return EFI_UNSUPPORTED; + } + // + // Set Memory block initial address + // + (*MemoryHeader)->MemoryBlockPtr = (UINT8 *) ((UINTN) MappedAddress); + (*MemoryHeader)->Mapping = Mapping; + + ZeroMem ( + (*MemoryHeader)->MemoryBlockPtr, + EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages) + ); + + return EFI_SUCCESS; +} + +EFI_STATUS +FreeMemoryHeader ( + IN USB_HC_DEV *HcDev, + IN MEMORY_MANAGE_HEADER *MemoryHeader + ) +/*++ + +Routine Description: + + Free Memory Header + +Arguments: + + HcDev - USB_HC_DEV + MemoryHeader - MemoryHeader to be freed + +Returns: + + EFI_INVALID_PARAMETER - Parameter is error + EFI_SUCCESS - Success + +--*/ +{ + if ((MemoryHeader == NULL) || (HcDev == NULL)) { + return EFI_INVALID_PARAMETER; + } + // + // unmap the common buffer used by the memory block + // + HcDev->PciIo->Unmap (HcDev->PciIo, MemoryHeader->Mapping); + + // + // free common buffer + // + HcDev->PciIo->FreeBuffer ( + HcDev->PciIo, + EFI_SIZE_TO_PAGES (MemoryHeader->MemoryBlockSizeInBytes), + MemoryHeader->MemoryBlockPtr + ); + // + // free bit array + // + gBS->FreePool (MemoryHeader->BitArrayPtr); + // + // free memory header + // + gBS->FreePool (MemoryHeader); + + return EFI_SUCCESS; +} + +EFI_STATUS +UhciAllocatePool ( + IN USB_HC_DEV *HcDev, + OUT UINT8 **Pool, + IN UINTN AllocSize + ) +/*++ + +Routine Description: + + Uhci Allocate Pool + +Arguments: + + HcDev - USB_HC_DEV + Pool - Place to store pointer to the memory buffer + AllocSize - Alloc Size + +Returns: + + EFI_SUCCESS - Success + +--*/ +{ + MEMORY_MANAGE_HEADER *MemoryHeader; + MEMORY_MANAGE_HEADER *TempHeaderPtr; + MEMORY_MANAGE_HEADER *NewMemoryHeader; + UINTN RealAllocSize; + UINTN MemoryBlockSizeInPages; + EFI_STATUS Status; + + *Pool = NULL; + + MemoryHeader = HcDev->MemoryHeader; + ASSERT (MemoryHeader != NULL); + + // + // allocate unit is 32 bytes (align on 32 byte) + // + if (AllocSize & 0x1F) { + RealAllocSize = (AllocSize / 32 + 1) * 32; + } else { + RealAllocSize = AllocSize; + } + + // + // There may be linked MemoryHeaders. + // To allocate a free pool in Memory blocks, + // must search in the MemoryHeader link list + // until enough free pool is found. + // + Status = EFI_NOT_FOUND; + for (TempHeaderPtr = MemoryHeader; TempHeaderPtr != NULL; + TempHeaderPtr = TempHeaderPtr->Next) { + + Status = AllocMemInMemoryBlock ( + TempHeaderPtr, + (VOID **) Pool, + RealAllocSize / 32 + ); + if (!EFI_ERROR (Status)) { + ZeroMem (*Pool, AllocSize); + return EFI_SUCCESS; + } + } + + // + // There is no enough memory, + // Create a new Memory Block + // + + // + // if pool size is larger than NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES, + // just allocate a large enough memory block. + // + if (RealAllocSize > EFI_PAGES_TO_SIZE (NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES)) { + MemoryBlockSizeInPages = EFI_SIZE_TO_PAGES (RealAllocSize) + 1; + } else { + MemoryBlockSizeInPages = NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES; + } + + Status = CreateMemoryBlock (HcDev, &NewMemoryHeader, MemoryBlockSizeInPages); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Link the new Memory Block to the Memory Header list + // + InsertMemoryHeaderToList (MemoryHeader, NewMemoryHeader); + + Status = AllocMemInMemoryBlock ( + NewMemoryHeader, + (VOID **) Pool, + RealAllocSize / 32 + ); + + if (!EFI_ERROR (Status)) { + ZeroMem (*Pool, AllocSize); + } + + return Status; +} + +VOID +UhciFreePool ( + IN USB_HC_DEV *HcDev, + IN UINT8 *Pool, + IN UINTN AllocSize + ) +/*++ + +Routine Description: + + Uhci Free Pool + +Arguments: + + HcDev - USB_HC_DEV + Pool - Pool to free + AllocSize - Pool size + +Returns: + + VOID + +--*/ +{ + MEMORY_MANAGE_HEADER *MemoryHeader; + MEMORY_MANAGE_HEADER *TempHeaderPtr; + UINTN StartBytePos; + UINTN Index; + UINT8 StartBitPos; + UINT8 Index2; + UINTN Count; + UINTN RealAllocSize; + + MemoryHeader = HcDev->MemoryHeader; + + // + // allocate unit is 32 byte (align on 32 byte) + // + if (AllocSize & 0x1F) { + RealAllocSize = (AllocSize / 32 + 1) * 32; + } else { + RealAllocSize = AllocSize; + } + // + // scan the memory header linked list for + // the asigned memory to free. + // + for (TempHeaderPtr = MemoryHeader;TempHeaderPtr != NULL; + TempHeaderPtr = TempHeaderPtr->Next) { + + if ((Pool >= TempHeaderPtr->MemoryBlockPtr) && + ((Pool + RealAllocSize) <= (TempHeaderPtr->MemoryBlockPtr + TempHeaderPtr->MemoryBlockSizeInBytes)) + ) { + // + // Pool is in the Memory Block area, + // find the start byte and bit in the bit array + // + StartBytePos = ((Pool - TempHeaderPtr->MemoryBlockPtr) / 32) / 8; + StartBitPos = (UINT8) (((Pool - TempHeaderPtr->MemoryBlockPtr) / 32) & 0x7); + + // + // reset associated bits in bit arry + // + for (Index = StartBytePos, Index2 = StartBitPos, Count = 0; Count < (RealAllocSize / 32); Count++) { + + TempHeaderPtr->BitArrayPtr[Index] ^= (UINT8) (bit (Index2)); + Index2++; + if (Index2 == 8) { + Index += 1; + Index2 = 0; + } + } + // + // break the loop + // + break; + } + } + + // + // Release emptied memory blocks (only if the memory block is not + // the first one in the memory header list + // + for (TempHeaderPtr = MemoryHeader->Next; TempHeaderPtr != NULL;) { + // + // Debug + // + ASSERT (MemoryHeader->Next != NULL); + + if (IsMemoryBlockEmptied (TempHeaderPtr)) { + + DelinkMemoryBlock (MemoryHeader, TempHeaderPtr); + // + // when the TempHeaderPtr is freed in FreeMemoryHeader(), + // the TempHeaderPtr is pointing to nonsense content. + // + FreeMemoryHeader (HcDev, TempHeaderPtr); + // + // reset the TempHeaderPtr, continue search for + // another empty memory block. + // + TempHeaderPtr = MemoryHeader->Next; + continue; + } + + TempHeaderPtr = TempHeaderPtr->Next; + } +} + +VOID +InsertMemoryHeaderToList ( + IN MEMORY_MANAGE_HEADER *MemoryHeader, + IN MEMORY_MANAGE_HEADER *NewMemoryHeader + ) +/*++ + +Routine Description: + + Insert Memory Header To List + +Arguments: + + MemoryHeader - MEMORY_MANAGE_HEADER + NewMemoryHeader - MEMORY_MANAGE_HEADER + +Returns: + + VOID + +--*/ +{ + MEMORY_MANAGE_HEADER *TempHeaderPtr; + + for (TempHeaderPtr = MemoryHeader; TempHeaderPtr != NULL; + TempHeaderPtr = TempHeaderPtr->Next) { + if (TempHeaderPtr->Next == NULL) { + TempHeaderPtr->Next = NewMemoryHeader; + break; + } + } +} + +EFI_STATUS +AllocMemInMemoryBlock ( + IN MEMORY_MANAGE_HEADER *MemoryHeader, + OUT VOID **Pool, + IN UINTN NumberOfMemoryUnit + ) +/*++ + +Routine Description: + + Alloc Memory In MemoryBlock + +Arguments: + + MemoryHeader - MEMORY_MANAGE_HEADER + Pool - Place to store pointer to memory + NumberOfMemoryUnit - Number Of Memory Unit + +Returns: + + EFI_NOT_FOUND - Can't find the free memory + EFI_SUCCESS - Success + +--*/ +{ + UINTN TempBytePos; + UINTN FoundBytePos; + UINT8 Index; + UINT8 FoundBitPos; + UINT8 ByteValue; + UINT8 BitValue; + UINTN NumberOfZeros; + UINTN Count; + + FoundBytePos = 0; + FoundBitPos = 0; + ByteValue = MemoryHeader->BitArrayPtr[0]; + NumberOfZeros = 0; + Index = 0; + + for (TempBytePos = 0; TempBytePos < MemoryHeader->BitArraySizeInBytes;) { + + // + // Pop out BitValue from a byte in TempBytePos. + // + BitValue = (UINT8) (ByteValue & 0x1); + // + // right shift the byte + // + ByteValue /= 2; + + if (BitValue == 0) { + // + // Found a free bit, the NumberOfZeros only record the number + // of those consecutive zeros + // + NumberOfZeros++; + // + // Found enough consecutive free space, break the loop + // + if (NumberOfZeros >= NumberOfMemoryUnit) { + break; + } + } else { + // + // Encountering a '1', meant the bit is ocupied. + // + if (NumberOfZeros >= NumberOfMemoryUnit) { + // + // Found enough consecutive free space,break the loop + // + break; + } else { + // + // the NumberOfZeros only record the number of those consecutive zeros, + // so reset the NumberOfZeros to 0 when encountering '1' before finding + // enough consecutive '0's + // + NumberOfZeros = 0; + // + // reset the (FoundBytePos,FoundBitPos) to the position of '1' + // + FoundBytePos = TempBytePos; + FoundBitPos = Index; + } + } + // + // step forward a bit + // + Index++; + if (Index == 8) { + // + // step forward a byte, getting the byte value, + // and reset the bit pos. + // + TempBytePos += 1; + ByteValue = MemoryHeader->BitArrayPtr[TempBytePos]; + Index = 0; + } + } + + if (NumberOfZeros < NumberOfMemoryUnit) { + return EFI_NOT_FOUND; + } + + // + // Found enough free space. + // + + // + // The values recorded in (FoundBytePos,FoundBitPos) have two conditions: + // 1)(FoundBytePos,FoundBitPos) record the position + // of the last '1' before the consecutive '0's, it must + // be adjusted to the start position of the consecutive '0's. + // 2)the start address of the consecutive '0's is just the start of + // the bitarray. so no need to adjust the values of + // (FoundBytePos,FoundBitPos). + // + if ((MemoryHeader->BitArrayPtr[0] & bit (0)) != 0) { + FoundBitPos += 1; + } + + // + // Have the (FoundBytePos,FoundBitPos) make sense. + // + if (FoundBitPos > 7) { + FoundBytePos += 1; + FoundBitPos -= 8; + } + + // + // Set the memory as allocated + // + for (TempBytePos = FoundBytePos, Index = FoundBitPos,Count = 0; + Count < NumberOfMemoryUnit; Count ++) { + + MemoryHeader->BitArrayPtr[TempBytePos] |= bit (Index); + Index++; + if (Index == 8) { + TempBytePos += 1; + Index = 0; + } + } + + *Pool = MemoryHeader->MemoryBlockPtr + (FoundBytePos * 8 + FoundBitPos) * 32; + + return EFI_SUCCESS; +} + +BOOLEAN +IsMemoryBlockEmptied ( + IN MEMORY_MANAGE_HEADER *MemoryHeaderPtr + ) +/*++ + +Routine Description: + + Is Memory Block Emptied + +Arguments: + + MemoryHeaderPtr - MEMORY_MANAGE_HEADER + +Returns: + + TRUE - Empty + FALSE - Not Empty + +--*/ +{ + UINTN Index; + + for (Index = 0; Index < MemoryHeaderPtr->BitArraySizeInBytes; Index++) { + if (MemoryHeaderPtr->BitArrayPtr[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +VOID +DelinkMemoryBlock ( + IN MEMORY_MANAGE_HEADER *FirstMemoryHeader, + IN MEMORY_MANAGE_HEADER *NeedFreeMemoryHeader + ) +/*++ + +Routine Description: + + Delink Memory Block + +Arguments: + + FirstMemoryHeader - MEMORY_MANAGE_HEADER + NeedFreeMemoryHeader - MEMORY_MANAGE_HEADER + +Returns: + + VOID + +--*/ +{ + MEMORY_MANAGE_HEADER *TempHeaderPtr; + + if ((FirstMemoryHeader == NULL) || (NeedFreeMemoryHeader == NULL)) { + return ; + } + for (TempHeaderPtr = FirstMemoryHeader; TempHeaderPtr != NULL; + TempHeaderPtr = TempHeaderPtr->Next) { + + if (TempHeaderPtr->Next == NeedFreeMemoryHeader) { + // + // Link the before and after + // + TempHeaderPtr->Next = NeedFreeMemoryHeader->Next; + break; + } + } +} + +EFI_STATUS +DelMemoryManagement ( + IN USB_HC_DEV *HcDev + ) +/*++ + +Routine Description: + + Delete Memory Management + +Arguments: + + HcDev - USB_HC_DEV + +Returns: + + EFI_SUCCESS - Success + +--*/ +{ + MEMORY_MANAGE_HEADER *TempHeaderPtr; + + for (TempHeaderPtr = HcDev->MemoryHeader->Next; TempHeaderPtr != NULL;) { + + DelinkMemoryBlock (HcDev->MemoryHeader, TempHeaderPtr); + // + // when the TempHeaderPtr is freed in FreeMemoryHeader(), + // the TempHeaderPtr is pointing to nonsense content. + // + FreeMemoryHeader (HcDev, TempHeaderPtr); + // + // reset the TempHeaderPtr,continue free another memory block. + // + TempHeaderPtr = HcDev->MemoryHeader->Next; + } + + FreeMemoryHeader (HcDev, HcDev->MemoryHeader); + + return EFI_SUCCESS; +} + + +VOID +CleanUsbTransactions ( + IN USB_HC_DEV *HcDev + ) +{ + // + // only asynchronous interrupt transfers are always alive on the bus + // + ReleaseInterruptList (HcDev, &(HcDev->InterruptListHead)); +} + +VOID +TurnOffUSBEmulation ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ) +/*++ + + Routine Description: + Disable USB Emulation + Arguments: + PciIo - EFI_PCI_IO_PROTOCOL + Returns: + VOID +--*/ +{ + UINT16 Command; + + // + // Disable USB Emulation + // + Command = 0; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + USB_EMULATION, + 1, + &Command + ); + + return ; +} diff --git a/EdkModulePkg/Bus/Pci/Uhci/Dxe/uhci.c b/EdkModulePkg/Bus/Pci/Uhci/Dxe/uhci.c new file mode 100644 index 0000000..1eba8be --- /dev/null +++ b/EdkModulePkg/Bus/Pci/Uhci/Dxe/uhci.c @@ -0,0 +1,3498 @@ +/*++ + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Uhci.c + +Abstract: + + +Revision History +--*/ + +#include "uhci.h" + +// +// Prototypes +// Driver model protocol interface +// + +EFI_STATUS +EFIAPI +UHCIDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +EFI_STATUS +EFIAPI +UHCIDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +UHCIDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +UHCIDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// UHCI interface functions +// + +EFI_STATUS +EFIAPI +UHCIReset ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT16 Attributes + ); + +EFI_STATUS +EFIAPI +UHCIGetState ( + IN EFI_USB_HC_PROTOCOL *This, + OUT EFI_USB_HC_STATE *State + ); + +EFI_STATUS +EFIAPI +UHCISetState ( + IN EFI_USB_HC_PROTOCOL *This, + IN EFI_USB_HC_STATE State + ); + +EFI_STATUS +EFIAPI +UHCIControlTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN BOOLEAN IsSlowDevice, + IN UINT8 MaximumPacketLength, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data, OPTIONAL + IN OUT UINTN *DataLength, OPTIONAL + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ); + +EFI_STATUS +EFIAPI +UHCIBulkTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ); + +EFI_STATUS +EFIAPI +UHCIAsyncInterruptTransfer ( + IN EFI_USB_HC_PROTOCOL * This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN BOOLEAN IsSlowDevice, + IN UINT8 MaxiumPacketLength, + IN BOOLEAN IsNewTransfer, + IN OUT UINT8 *DataToggle, + IN UINTN PollingInterval, OPTIONAL + IN UINTN DataLength, OPTIONAL + IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, OPTIONAL + IN VOID *Context OPTIONAL + ); + +EFI_STATUS +EFIAPI +UHCISyncInterruptTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN BOOLEAN IsSlowDevice, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ); + +EFI_STATUS +EFIAPI +UHCIIsochronousTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN UINTN DataLength, + OUT UINT32 *TransferResult + ); + +EFI_STATUS +EFIAPI +UHCIAsyncIsochronousTransfer ( + IN EFI_USB_HC_PROTOCOL * This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN UINTN DataLength, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack, + IN VOID *Context OPTIONAL + ); + +EFI_STATUS +EFIAPI +UHCIGetRootHubPortNumber ( + IN EFI_USB_HC_PROTOCOL *This, + OUT UINT8 *PortNumber + ); + +EFI_STATUS +EFIAPI +UHCIGetRootHubPortStatus ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus + ); + +EFI_STATUS +EFIAPI +UHCISetRootHubPortFeature ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ); + +EFI_STATUS +EFIAPI +UHCIClearRootHubPortFeature ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ); + +// +// Asynchronous interrupt transfer monitor function +// +VOID +EFIAPI +MonitorInterruptTrans ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +// +// UHCI Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL gUhciDriverBinding = { + UHCIDriverBindingSupported, + UHCIDriverBindingStart, + UHCIDriverBindingStop, + 0x10, + NULL, + NULL +}; + +EFI_STATUS +EFIAPI +UHCIDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +/*++ + + Routine Description: + Test to see if this driver supports ControllerHandle. Any ControllerHandle + that has UsbHcProtocol installed will be supported. + + Arguments: + This - Protocol instance pointer. + Controller, - Handle of device to test + RemainingDevicePath - Not used + + Returns: + EFI_SUCCESS - This driver supports this device. + EFI_UNSUPPORTED - This driver does not support this device. + +--*/ +{ + EFI_STATUS OpenStatus; + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + USB_CLASSC UsbClassCReg; + + // + // Test whether there is PCI IO Protocol attached on the controller handle. + // + OpenStatus = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (OpenStatus)) { + return OpenStatus; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + CLASSC, + sizeof (USB_CLASSC) / sizeof (UINT8), + &UsbClassCReg + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_UNSUPPORTED; + } + // + // Test whether the controller belongs to UHCI type + // + if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || + (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) || + (UsbClassCReg.PI != PCI_CLASSC_PI_UHCI)) { + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_UNSUPPORTED; + } + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_SUCCESS; + +} + +EFI_STATUS +EFIAPI +UHCIDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +/*++ + + Routine Description: + Starting the Usb UHCI Driver + + Arguments: + This - Protocol instance pointer. + Controller - Handle of device to test + RemainingDevicePath - Not used + + Returns: + EFI_SUCCESS - This driver supports this device. + EFI_UNSUPPORTED - This driver does not support this device. + EFI_DEVICE_ERROR - This driver cannot be started due to device + Error + EFI_OUT_OF_RESOURCES + +--*/ +{ + EFI_STATUS Status; + UINTN FlBaseAddrReg; + EFI_PCI_IO_PROTOCOL *PciIo; + USB_HC_DEV *HcDev; + + HcDev = NULL; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Turn off USB emulation + // + TurnOffUSBEmulation (PciIo); + + // + // Enable the USB Host Controller + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_DEVICE_ENABLE, + NULL + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_UNSUPPORTED; + } + + // + // allocate memory for UHC private data structure + // + HcDev = AllocateZeroPool (sizeof (USB_HC_DEV)); + if (HcDev == NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_OUT_OF_RESOURCES; + } + + // + // init EFI_USB_HC_PROTOCOL protocol interface and install the protocol + // + HcDev->UsbHc.Reset = UHCIReset; + HcDev->UsbHc.GetState = UHCIGetState; + HcDev->UsbHc.SetState = UHCISetState; + HcDev->UsbHc.ControlTransfer = UHCIControlTransfer; + HcDev->UsbHc.BulkTransfer = UHCIBulkTransfer; + HcDev->UsbHc.AsyncInterruptTransfer = UHCIAsyncInterruptTransfer; + HcDev->UsbHc.SyncInterruptTransfer = UHCISyncInterruptTransfer; + HcDev->UsbHc.IsochronousTransfer = UHCIIsochronousTransfer; + HcDev->UsbHc.AsyncIsochronousTransfer = UHCIAsyncIsochronousTransfer; + HcDev->UsbHc.GetRootHubPortNumber = UHCIGetRootHubPortNumber; + HcDev->UsbHc.GetRootHubPortStatus = UHCIGetRootHubPortStatus; + HcDev->UsbHc.SetRootHubPortFeature = UHCISetRootHubPortFeature; + HcDev->UsbHc.ClearRootHubPortFeature = UHCIClearRootHubPortFeature; + + HcDev->UsbHc.MajorRevision = 0x1; + HcDev->UsbHc.MinorRevision = 0x1; + + // + // Init UHCI private data structures + // + HcDev->Signature = USB_HC_DEV_SIGNATURE; + HcDev->PciIo = PciIo; + + FlBaseAddrReg = USBFLBASEADD; + + // + // Allocate and Init Host Controller's Frame List Entry + // + Status = CreateFrameList (HcDev, (UINT32) FlBaseAddrReg); + if (EFI_ERROR (Status)) { + + if (HcDev != NULL) { + gBS->FreePool (HcDev); + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_OUT_OF_RESOURCES; + } + + // + // Init interrupt list head in the HcDev structure. + // + InitializeListHead (&(HcDev->InterruptListHead)); + + // + // Create timer for interrupt transfer result polling + // + Status = gBS->CreateEvent ( + EFI_EVENT_TIMER | EFI_EVENT_NOTIFY_SIGNAL, + EFI_TPL_NOTIFY, + MonitorInterruptTrans, + HcDev, + &HcDev->InterruptTransTimer + ); + if (EFI_ERROR (Status)) { + + FreeFrameListEntry (HcDev); + + if (HcDev != NULL) { + gBS->FreePool (HcDev); + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_UNSUPPORTED; + } + + // + // Here set interrupt transfer polling timer in 50ms unit. + // + Status = gBS->SetTimer ( + HcDev->InterruptTransTimer, + TimerPeriodic, + INTERRUPT_POLLING_TIME + ); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (HcDev->InterruptTransTimer); + + FreeFrameListEntry (HcDev); + + if (HcDev != NULL) { + gBS->FreePool (HcDev); + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_UNSUPPORTED; + } + + // + // QH,TD structures must in common buffer that will be + // accessed by both cpu and usb bus master at the same time. + // so, there must has memory management for QH,TD structures. + // + Status = InitializeMemoryManagement (HcDev); + if (EFI_ERROR (Status)) { + + gBS->CloseEvent (HcDev->InterruptTransTimer); + + FreeFrameListEntry (HcDev); + + if (HcDev != NULL) { + gBS->FreePool (HcDev); + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; + } + + // + // Install Host Controller Protocol + // + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEfiUsbHcProtocolGuid, + EFI_NATIVE_INTERFACE, + &HcDev->UsbHc + ); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (HcDev->InterruptTransTimer); + FreeFrameListEntry (HcDev); + DelMemoryManagement (HcDev); + + if (HcDev != NULL) { + gBS->FreePool (HcDev); + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; + } + + // + // component name protocol. + // + HcDev->ControllerNameTable = NULL; + AddUnicodeString ( + "eng", + gUhciComponentName.SupportedLanguages, + &HcDev->ControllerNameTable, + (CHAR16 *) L"Usb Universal Host Controller" + ); + + return EFI_SUCCESS; +} + + +EFI_STATUS +UnInstallUHCInterface ( + IN EFI_HANDLE Controller, + IN EFI_USB_HC_PROTOCOL *This + ) +/*++ + Routine Description: + UnInstall UHCInterface + Arguments: + Controller - Controller handle + This - Protocol instance pointer. + Returns: + EFI_SUCCESS + others +--*/ +{ + USB_HC_DEV *HcDev; + + HcDev = USB_HC_DEV_FROM_THIS (This); + + gBS->UninstallProtocolInterface ( + Controller, + &gEfiUsbHcProtocolGuid, + &HcDev->UsbHc + ); + + // + // first stop USB Host Controller + // + This->SetState (This, EfiUsbHcStateHalt); + + // + // Delete interrupt transfer polling timer + // + gBS->CloseEvent (HcDev->InterruptTransTimer); + + // + // Delete all the asynchronous interrupt transfers in the interrupt list + // and free associated memory + // + ReleaseInterruptList (HcDev, &(HcDev->InterruptListHead)); + + // + // free Frame List Entry. + // + FreeFrameListEntry (HcDev); + + // + // Free common buffer allocated for QH,TD structures + // + DelMemoryManagement (HcDev); + + if (HcDev->ControllerNameTable) { + FreeUnicodeStringTable (HcDev->ControllerNameTable); + } + // + // Disable the USB Host Controller + // + HcDev->PciIo->Attributes ( + HcDev->PciIo, + EfiPciIoAttributeOperationDisable, + EFI_PCI_DEVICE_ENABLE, + NULL + ); + + gBS->FreePool (HcDev); + + return EFI_SUCCESS; +} + + +EFI_STATUS +EFIAPI +UHCIDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +/*++ + + Routine Description: + Stop this driver on ControllerHandle. Support stoping any child handles + created by this driver. + + Arguments: + This - Protocol instance pointer. + Controller - Handle of device to stop driver on + NumberOfChildren - Number of Children in the ChildHandleBuffer + ChildHandleBuffer - List of handles for the children we need to stop. + + Returns: + EFI_SUCCESS + others + +--*/ +{ + EFI_USB_HC_PROTOCOL *UsbHc; + EFI_STATUS OpenStatus; + + OpenStatus = gBS->OpenProtocol ( + Controller, + &gEfiUsbHcProtocolGuid, + (VOID **) &UsbHc, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + // + // Test whether the Controller handler passed in is a valid + // Usb controller handle that should be supported, if not, + // return the error status directly + // + if (EFI_ERROR (OpenStatus)) { + return OpenStatus; + } + // + // free all the controller related memory and uninstall UHCI Protocol. + // + UnInstallUHCInterface (Controller, UsbHc); + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; + +} + + +EFI_STATUS +EFIAPI +UHCIReset ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT16 Attributes + ) +/*++ + + Routine Description: + Provides software reset for the USB host controller. + + Arguments: + + This A pointer to the EFI_USB_HC_PROTOCOL instance. + + Attributes A bit mask of the reset operation to perform. + See below for a list of the supported bit mask values. + + #define EFI_USB_HC_RESET_GLOBAL 0x0001 + #define EFI_USB_HC_RESET_HOST_CONTROLLER 0x0002 + + EFI_USB_HC_RESET_GLOBAL + If this bit is set, a global reset signal will be sent to the USB bus. + This resets all of the USB bus logic, including the USB host + controller hardware and all the devices attached on the USB bus. + EFI_USB_HC_RESET_HOST_CONTROLLER + If this bit is set, the USB host controller hardware will be reset. + No reset signal will be sent to the USB bus. + + Returns: + EFI_SUCCESS + The reset operation succeeded. + EFI_INVALID_PARAMETER + Attributes is not valid. + EFI_DEVICE_ERROR + An error was encountered while attempting to perform + the reset operation. +--*/ +{ + BOOLEAN Match; + USB_HC_DEV *HcDev; + UINT32 CommandRegAddr; + UINT32 FlBaseAddrReg; + UINT16 Command; + EFI_STATUS Status; + + Match = FALSE; + HcDev = USB_HC_DEV_FROM_THIS (This); + + CommandRegAddr = (UINT32) (USBCMD); + FlBaseAddrReg = (UINT32) (USBFLBASEADD); + + if ((Attributes & EFI_USB_HC_RESET_GLOBAL) != 0) { + Match = TRUE; + // + // set the Global Reset bit in the command register + // + Status = ReadUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + &Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Command |= USBCMD_GRESET; + Status = WriteUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + // + // Wait 50ms for root port to let reset complete + // See UHCI spec page122 Reset signaling + // + gBS->Stall (ROOT_PORT_REST_TIME); + + // + // Clear the Global Reset bit to zero. + // + Command &= ~USBCMD_GRESET; + Status = WriteUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // UHCI spec page120 reset recovery time + // + gBS->Stall (PORT_RESET_RECOVERY_TIME); + } + + if ((Attributes & EFI_USB_HC_RESET_HOST_CONTROLLER) != 0) { + Match = TRUE; + // + // set Host Controller Reset bit to 1 + // + Status = ReadUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + &Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Command |= USBCMD_HCRESET; + Status = WriteUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // this bit will be reset by Host Controller when reset is completed. + // wait 10ms to let reset complete + // + gBS->Stall (PORT_RESET_RECOVERY_TIME); + } + + if (!Match) { + return EFI_INVALID_PARAMETER; + } + + // + // Delete all old transactions on the USB bus + // + CleanUsbTransactions (HcDev); + + // + // Initialize Universal Host Controller's Frame List Data Structure + // + InitFrameList (HcDev); + + // + // Reset may cause Frame List Base Address Register reset to zero, + // so set the original value back again. + // + SetFrameListBaseAddress ( + HcDev->PciIo, + FlBaseAddrReg, + (UINT32) ((UINTN) HcDev->FrameListEntry) + ); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +UHCIGetState ( + IN EFI_USB_HC_PROTOCOL *This, + OUT EFI_USB_HC_STATE *State + ) +/*++ + + Routine Description: + Retrieves current state of the USB host controller. + + Arguments: + + This A pointer to the EFI_USB_HC_PROTOCOL instance. + + State A pointer to the EFI_USB_HC_STATE data structure that + indicates current state of the USB host controller. + Type EFI_USB_HC_STATE is defined below. + + typedef enum { + EfiUsbHcStateHalt, + EfiUsbHcStateOperational, + EfiUsbHcStateSuspend, + EfiUsbHcStateMaximum + } EFI_USB_HC_STATE; + + Returns: + EFI_SUCCESS + The state information of the host controller was returned in State. + EFI_INVALID_PARAMETER + State is NULL. + EFI_DEVICE_ERROR + An error was encountered while attempting to retrieve the + host controller's current state. +--*/ +{ + USB_HC_DEV *HcDev; + UINT32 CommandRegAddr; + UINT32 StatusRegAddr; + UINT16 UhcCommand; + UINT16 UhcStatus; + EFI_STATUS Status; + + if (State == NULL) { + return EFI_INVALID_PARAMETER; + } + + HcDev = USB_HC_DEV_FROM_THIS (This); + + CommandRegAddr = (UINT32) (USBCMD); + StatusRegAddr = (UINT32) (USBSTS); + + Status = ReadUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + &UhcCommand + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Status = ReadUHCCommandReg ( + HcDev->PciIo, + StatusRegAddr, + &UhcStatus + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + if (UhcCommand & USBCMD_EGSM) { + *State = EfiUsbHcStateSuspend; + return EFI_SUCCESS; + } + + if ((UhcStatus & USBSTS_HCH) == 0) { + *State = EfiUsbHcStateOperational; + } else { + *State = EfiUsbHcStateHalt; + } + + return EFI_SUCCESS; +} + + +EFI_STATUS +EFIAPI +UHCISetState ( + IN EFI_USB_HC_PROTOCOL *This, + IN EFI_USB_HC_STATE State + ) +/*++ + + Routine Description: + Sets the USB host controller to a specific state. + + Arguments: + + This A pointer to the EFI_USB_HC_PROTOCOL instance. + + State Indicates the state of the host controller that will be set. + + Returns: + EFI_SUCCESS + The USB host controller was successfully placed in the state + specified by State. + EFI_INVALID_PARAMETER + State is invalid. + EFI_DEVICE_ERROR + Failed to set the state specified by State due to device error. +--*/ +{ + USB_HC_DEV *HcDev; + UINT32 CommandRegAddr; + UINT32 StatusRegAddr; + UINT16 Command; + EFI_USB_HC_STATE CurrentState; + EFI_STATUS Status; + + HcDev = USB_HC_DEV_FROM_THIS (This); + + CommandRegAddr = (UINT32) (USBCMD); + StatusRegAddr = (UINT32) (USBSTS); + + Status = UHCIGetState (This, &CurrentState); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + switch (State) { + + case EfiUsbHcStateHalt: + if (CurrentState == EfiUsbHcStateHalt) { + return EFI_SUCCESS; + } + + Status = ReadUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + &Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Command &= ~USBCMD_RS; + + Status = WriteUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + StatusRegAddr = (UINT32) (USBSTS); + // + // ensure the HC is in halt status after send the stop command + // + if (WaitForUHCHalt (HcDev->PciIo, StatusRegAddr, STALL_1_SECOND) == EFI_TIMEOUT) { + return EFI_DEVICE_ERROR; + } + break; + + case EfiUsbHcStateOperational: + if (IsHostSysOrProcessErr (HcDev->PciIo, StatusRegAddr)) { + return EFI_DEVICE_ERROR; + } + + switch (CurrentState) { + + case EfiUsbHcStateOperational: + return EFI_SUCCESS; + + case EfiUsbHcStateHalt: + // + // Set Run/Stop bit to 1. + // + Status = ReadUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + &Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Command |= USBCMD_RS | USBCMD_MAXP; + Status = WriteUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + break; + + case EfiUsbHcStateSuspend: + Status = ReadUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + &Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + // + // FGR(Force Global Resume) bit is 0 + // + if ((Command | (~USBCMD_FGR)) != 0xFF) { + // + // Write FGR bit to 1 + // + Command |= USBCMD_FGR; + WriteUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + Command + ); + } + + // + // wait 20ms to let resume complete + // (20ms is specified by UHCI spec) + // + gBS->Stall (FORCE_GLOBAL_RESUME_TIME); + + // + // Write FGR bit to 0 and EGSM(Enter Global Suspend Mode) bit to 0 + // + Command &= ~USBCMD_FGR; + Command &= ~USBCMD_EGSM; + Command |= USBCMD_RS; + WriteUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + Command + ); + break; + + default: + break; + } + break; + + case EfiUsbHcStateSuspend: + if (CurrentState == EfiUsbHcStateSuspend) { + return EFI_SUCCESS; + } + + Status = UHCISetState (This, EfiUsbHcStateHalt); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // Set Enter Global Suspend Mode bit to 1. + // + Status = ReadUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + &Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Command |= USBCMD_EGSM; + Status = WriteUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + break; + + default: + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +UHCIGetRootHubPortNumber ( + IN EFI_USB_HC_PROTOCOL *This, + OUT UINT8 *PortNumber + ) +/*++ + + Routine Description: + Retrieves the number of root hub ports. + + Arguments: + + This A pointer to the EFI_USB_HC_PROTOCOL instance. + + PortNumber A pointer to the number of the root hub ports. + + Returns: + EFI_SUCCESS + The port number was retrieved successfully. + EFI_INVALID_PARAMETER + PortNumber is NULL. + EFI_DEVICE_ERROR + An error was encountered while attempting to + retrieve the port number. +--*/ +{ + USB_HC_DEV *HcDev; + UINT32 PSAddr; + UINT16 RHPortControl; + UINT32 Index; + EFI_STATUS Status; + + HcDev = USB_HC_DEV_FROM_THIS (This); + + if (PortNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + + *PortNumber = 0; + + for (Index = 0; Index < 2; Index++) { + PSAddr = (UINT32) (USBPORTSC1 + Index * 2); + Status = ReadRootPortReg ( + HcDev->PciIo, + PSAddr, + &RHPortControl + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // Port Register content is valid + // + if (RHPortControl != 0xff) { + (*PortNumber)++; + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +UHCIGetRootHubPortStatus ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus + ) +/*++ + + Routine Description: + Retrieves the current status of a USB root hub port. + + Arguments: + + This A pointer to the EFI_USB_HC_PROTOCOL. + + PortNumber Specifies the root hub port from which the status + is to be retrieved. This value is zero-based. For example, + if a root hub has two ports, then the first port is numbered 0, + and the second port is numbered 1. + + PortStatus A pointer to the current port status bits and + port status change bits. + + Returns: + EFI_SUCCESS + The status of the USB root hub port specified by PortNumber + was returned in PortStatus. + EFI_INVALID_PARAMETER + PortNumber is invalid. + EFI_DEVICE_ERROR - Can't read register +--*/ +{ + USB_HC_DEV *HcDev; + UINT32 PSAddr; + UINT16 RHPortStatus; + UINT8 TotalPortNumber; + EFI_STATUS Status; + + if (PortStatus == NULL) { + return EFI_INVALID_PARAMETER; + } + + UHCIGetRootHubPortNumber (This, &TotalPortNumber); + if (PortNumber >= TotalPortNumber) { + return EFI_INVALID_PARAMETER; + } + + HcDev = USB_HC_DEV_FROM_THIS (This); + PSAddr = (UINT32) (USBPORTSC1 + PortNumber * 2); + + // + // Clear port status + // + PortStatus->PortStatus = 0; + PortStatus->PortChangeStatus = 0; + + Status = ReadRootPortReg ( + HcDev->PciIo, + PSAddr, + &RHPortStatus + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // Fill Port Status bits + // + + // + // Current Connect Status + // + if (RHPortStatus & USBPORTSC_CCS) { + PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION; + } + // + // Port Enabled/Disabled + // + if (RHPortStatus & USBPORTSC_PED) { + PortStatus->PortStatus |= USB_PORT_STAT_ENABLE; + } + + // + // Port Suspend + // + if (RHPortStatus & USBPORTSC_SUSP) { + PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND; + } + + // + // Port Reset + // + if (RHPortStatus & USBPORTSC_PR) { + PortStatus->PortStatus |= USB_PORT_STAT_RESET; + } + + // + // Low Speed Device Attached + // + if (RHPortStatus & USBPORTSC_LSDA) { + PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED; + } + // + // Fill Port Status Change bits + // + + // + // Connect Status Change + // + if (RHPortStatus & USBPORTSC_CSC) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION; + } + + // + // Port Enabled/Disabled Change + // + if (RHPortStatus & USBPORTSC_PEDC) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +UHCISetRootHubPortFeature ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +/*++ + + Routine Description: + Sets a feature for the specified root hub port. + + Arguments: + + This A pointer to the EFI_USB_HC_PROTOCOL. + + PortNumber Specifies the root hub port whose feature + is requested to be set. + + PortFeature Indicates the feature selector associated + with the feature set request. + + Returns: + EFI_SUCCESS + The feature specified by PortFeature was set for the + USB root hub port specified by PortNumber. + EFI_INVALID_PARAMETER + PortNumber is invalid or PortFeature is invalid. + EFI_DEVICE_ERROR + Can't read register +--*/ +{ + USB_HC_DEV *HcDev; + UINT32 PSAddr; + UINT32 CommandRegAddr; + // + // root hub port status + // + UINT16 RHPortControl; + UINT16 Command; + UINT8 TotalPortNumber; + EFI_STATUS Status; + + UHCIGetRootHubPortNumber (This, &TotalPortNumber); + if (PortNumber >= TotalPortNumber) { + return EFI_INVALID_PARAMETER; + } + + HcDev = USB_HC_DEV_FROM_THIS (This); + + PSAddr = (UINT32) (USBPORTSC1 + PortNumber * 2); + CommandRegAddr = (UINT32) (USBCMD); + + Status = ReadRootPortReg ( + HcDev->PciIo, + PSAddr, + &RHPortControl + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + switch (PortFeature) { + + case EfiUsbPortSuspend: + Status = ReadUHCCommandReg ( + HcDev->PciIo, + CommandRegAddr, + &Command + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + if (!(Command & USBCMD_EGSM)) { + // + // if global suspend is not active, can set port suspend + // + RHPortControl &= 0xfff5; + RHPortControl |= USBPORTSC_SUSP; + } + break; + + case EfiUsbPortReset: + RHPortControl &= 0xfff5; + // + // Set the reset bit + // + RHPortControl |= USBPORTSC_PR; + break; + + case EfiUsbPortPower: + break; + + case EfiUsbPortEnable: + RHPortControl &= 0xfff5; + RHPortControl |= USBPORTSC_PED; + break; + + default: + return EFI_INVALID_PARAMETER; + } + + WriteRootPortReg ( + HcDev->PciIo, + PSAddr, + RHPortControl + ); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +UHCIClearRootHubPortFeature ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +/*++ + + Routine Description: + Clears a feature for the specified root hub port. + + Arguments: + + This A pointer to the EFI_USB_HC_PROTOCOL instance. + + PortNumber Specifies the root hub port whose feature + is requested to be cleared. + + PortFeature Indicates the feature selector associated with the + feature clear request. + + Returns: + EFI_SUCCESS + The feature specified by PortFeature was cleared for the + USB root hub port specified by PortNumber. + EFI_INVALID_PARAMETER + PortNumber is invalid or PortFeature is invalid. + EFI_DEVICE_ERROR + Can't read register +--*/ +{ + USB_HC_DEV *HcDev; + UINT32 PSAddr; + UINT16 RHPortControl; + UINT8 TotalPortNumber; + EFI_STATUS Status; + + UHCIGetRootHubPortNumber (This, &TotalPortNumber); + + if (PortNumber >= TotalPortNumber) { + return EFI_INVALID_PARAMETER; + } + + HcDev = USB_HC_DEV_FROM_THIS (This); + PSAddr = (UINT32) (USBPORTSC1 + PortNumber * 2); + + Status = ReadRootPortReg ( + HcDev->PciIo, + PSAddr, + &RHPortControl + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + switch (PortFeature) { + // + // clear PORT_ENABLE feature means disable port. + // + case EfiUsbPortEnable: + RHPortControl &= 0xfff5; + RHPortControl &= ~USBPORTSC_PED; + break; + + // + // clear PORT_SUSPEND feature means resume the port. + // (cause a resume on the specified port if in suspend mode) + // + case EfiUsbPortSuspend: + RHPortControl &= 0xfff5; + RHPortControl &= ~USBPORTSC_SUSP; + break; + + // + // no operation + // + case EfiUsbPortPower: + break; + + // + // clear PORT_RESET means clear the reset signal. + // + case EfiUsbPortReset: + RHPortControl &= 0xfff5; + RHPortControl &= ~USBPORTSC_PR; + break; + + // + // clear connect status change + // + case EfiUsbPortConnectChange: + RHPortControl &= 0xfff5; + RHPortControl |= USBPORTSC_CSC; + break; + + // + // clear enable/disable status change + // + case EfiUsbPortEnableChange: + RHPortControl &= 0xfff5; + RHPortControl |= USBPORTSC_PEDC; + break; + + // + // root hub does not support this request + // + case EfiUsbPortSuspendChange: + break; + + // + // root hub does not support this request + // + case EfiUsbPortOverCurrentChange: + break; + + // + // root hub does not support this request + // + case EfiUsbPortResetChange: + break; + + default: + return EFI_INVALID_PARAMETER; + } + + WriteRootPortReg ( + HcDev->PciIo, + PSAddr, + RHPortControl + ); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +UHCIControlTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN BOOLEAN IsSlowDevice, + IN UINT8 MaximumPacketLength, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data, OPTIONAL + IN OUT UINTN *DataLength, OPTIONAL + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +/*++ + + Routine Description: + Submits control transfer to a target USB device. + + Arguments: + + This A pointer to the EFI_USB_HC_PROTOCOL instance. + + DeviceAddress Represents the address of the target device on the USB, + which is assigned during USB enumeration. + + IsSlowDevice Indicates whether the target device is slow device + or full-speed device. + + MaximumPacketLength Indicates the maximum packet size that the + default control transfer endpoint is capable of + sending or receiving. + + Request A pointer to the USB device request that will be sent + to the USB device. + + TransferDirection Specifies the data direction for the transfer. + There are three values available, DataIn, DataOut + and NoData. + + Data A pointer to the buffer of data that will be transmitted + to USB device or received from USB device. + + DataLength Indicates the size, in bytes, of the data buffer + specified by Data. + + TimeOut Indicates the maximum time, in microseconds, + which the transfer is allowed to complete. + + TransferResult A pointer to the detailed result information generated + by this control transfer. + + Returns: + EFI_SUCCESS + The control transfer was completed successfully. + EFI_OUT_OF_RESOURCES + The control transfer could not be completed due to a lack of resources. + EFI_INVALID_PARAMETER + Some parameters are invalid. + EFI_TIMEOUT + The control transfer failed due to timeout. + EFI_DEVICE_ERROR + The control transfer failed due to host controller or device error. + Caller should check TranferResult for detailed error information. + +--*/ +{ + USB_HC_DEV *HcDev; + UINT32 StatusReg; + UINT32 FrameNumReg; + UINT8 PktID; + QH_STRUCT *PtrQH; + TD_STRUCT *PtrTD; + TD_STRUCT *PtrPreTD; + TD_STRUCT *PtrSetupTD; + TD_STRUCT *PtrStatusTD; + EFI_STATUS Status; + UINTN Index; + UINTN DataLen; + UINT8 *PtrDataSource; + UINT8 *Ptr; + UINT8 DataToggle; + UINT16 LoadFrameListIndex; + UINT8 PktSize; + + UINT8 *RequestMappedAddress; + VOID *RequestMapping; + UINTN RequestLen; + + EFI_PHYSICAL_ADDRESS TempPtr; + VOID *Mapping; + + TD_STRUCT *PtrFirstDataTD; + TD_STRUCT *ptrLastDataTD; + BOOLEAN FirstTD; + + FirstTD = FALSE; + RequestMappedAddress = NULL; + RequestMapping = NULL; + Mapping = NULL; + PtrFirstDataTD = NULL; + ptrLastDataTD = NULL; + PktID = INPUT_PACKET_ID; + Mapping = NULL; + HcDev = USB_HC_DEV_FROM_THIS (This); + StatusReg = (UINT32) (USBSTS); + FrameNumReg = (UINT32) (USBFRNUM); + PtrPreTD = NULL; + PtrTD = NULL; + + // + // Parameters Checking + // + if (Request == NULL || TransferResult == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // if errors exist that cause host controller halt, + // then return EFI_DEVICE_ERROR. + // + if (!IsStatusOK (HcDev->PciIo, StatusReg)) { + + ClearStatusReg (HcDev->PciIo, StatusReg); + *TransferResult = EFI_USB_ERR_SYSTEM; + return EFI_DEVICE_ERROR; + } + + // + // low speed usb devices are limited to only an eight-byte + // maximum data payload size + // + if (IsSlowDevice && (MaximumPacketLength != 8)) { + return EFI_INVALID_PARAMETER; + } + + if (MaximumPacketLength != 8 && + MaximumPacketLength != 16 && + MaximumPacketLength != 32 && + MaximumPacketLength != 64) { + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection != EfiUsbNoData) && (DataLength == NULL)) { + return EFI_INVALID_PARAMETER; + } + + switch (TransferDirection) { + + case EfiUsbDataIn: + PktID = INPUT_PACKET_ID; + PtrDataSource = Data; + DataLen = *DataLength; + + // + // map the source data buffer for bus master access. + // BusMasterWrite means cpu read + // + Status = HcDev->PciIo->Map ( + HcDev->PciIo, + EfiPciIoOperationBusMasterWrite, + PtrDataSource, + &DataLen, + &TempPtr, + &Mapping + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Ptr = (UINT8 *) ((UINTN) TempPtr); + break; + + case EfiUsbDataOut: + PktID = OUTPUT_PACKET_ID; + PtrDataSource = Data; + DataLen = *DataLength; + + // + // map the source data buffer for bus master access. + // BusMasterRead means cpu write + // + Status = HcDev->PciIo->Map ( + HcDev->PciIo, + EfiPciIoOperationBusMasterRead, + PtrDataSource, + &DataLen, + &TempPtr, + &Mapping + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Ptr = (UINT8 *) ((UINTN) TempPtr); + break; + + // + // no data stage + // + case EfiUsbNoData: + if ((DataLength != NULL) && (*DataLength != 0)) { + return EFI_INVALID_PARAMETER; + } + + PktID = OUTPUT_PACKET_ID; + PtrDataSource = NULL; + DataLen = 0; + Ptr = NULL; + break; + + default: + return EFI_INVALID_PARAMETER; + } + + Status = ClearStatusReg (HcDev->PciIo, StatusReg); + if (EFI_ERROR (Status)) { + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + return EFI_DEVICE_ERROR; + } + // + // create QH structure and init + // + Status = CreateQH (HcDev, &PtrQH); + if (EFI_ERROR (Status)) { + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + return Status; + } + + // + // map the Request for bus master access. + // BusMasterRead means cpu write + // + RequestLen = sizeof (EFI_USB_DEVICE_REQUEST); + Status = HcDev->PciIo->Map ( + HcDev->PciIo, + EfiPciIoOperationBusMasterRead, + (UINT8 *) Request, + &RequestLen, + &TempPtr, + &RequestMapping + ); + + if (EFI_ERROR (Status)) { + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + return Status; + } + + RequestMappedAddress = (UINT8 *) ((UINTN) TempPtr); + + // + // generate Setup Stage TD + // + Status = GenSetupStageTD ( + HcDev, + DeviceAddress, + 0, + IsSlowDevice, + (UINT8 *) RequestMappedAddress, + sizeof (EFI_USB_DEVICE_REQUEST), + &PtrSetupTD + ); + + if (EFI_ERROR (Status)) { + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + HcDev->PciIo->Unmap (HcDev->PciIo, RequestMapping); + return Status; + } + + // + // Data Stage of Control Transfer + // + DataToggle = 1; + FirstTD = TRUE; + while (DataLen > 0) { + // + // create TD structures and link together + // + + // + // PktSize is the data load size that each TD carries. + // + PktSize = (UINT8) DataLen; + if (DataLen > MaximumPacketLength) { + PktSize = MaximumPacketLength; + } + + Status = GenDataTD ( + HcDev, + DeviceAddress, + 0, + Ptr, + PktSize, + PktID, + DataToggle, + IsSlowDevice, + &PtrTD + ); + + if (EFI_ERROR (Status)) { + // + // free all resources occupied + // + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + HcDev->PciIo->Unmap (HcDev->PciIo, RequestMapping); + DeleteQueuedTDs (HcDev, PtrSetupTD); + DeleteQueuedTDs (HcDev, PtrFirstDataTD); + return Status; + } + + // + // Link two TDs in vertical depth + // + if (FirstTD) { + PtrFirstDataTD = PtrTD; + PtrFirstDataTD->ptrNextTD = NULL; + FirstTD = FALSE; + } else { + LinkTDToTD (PtrPreTD, PtrTD); + } + + PtrPreTD = PtrTD; + + DataToggle ^= 1; + Ptr += PktSize; + DataLen -= PktSize; + } + + ptrLastDataTD = PtrTD; + + // + // Status Stage of Control Transfer + // + if (PktID == OUTPUT_PACKET_ID) { + PktID = INPUT_PACKET_ID; + } else { + PktID = OUTPUT_PACKET_ID; + } + + // + // create Status Stage TD structure + // + Status = CreateStatusTD ( + HcDev, + DeviceAddress, + 0, + PktID, + IsSlowDevice, + &PtrStatusTD + ); + + if (EFI_ERROR (Status)) { + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + HcDev->PciIo->Unmap (HcDev->PciIo, RequestMapping); + DeleteQueuedTDs (HcDev, PtrSetupTD); + DeleteQueuedTDs (HcDev, PtrFirstDataTD); + return Status; + } + + if (IsSlowDevice) { + // + // link setup TD structures to QH structure + // + LinkTDToQH (PtrQH, PtrSetupTD); + + LoadFrameListIndex = (UINT16) ((GetCurrentFrameNumber (HcDev->PciIo, FrameNumReg)) & 0x3FF); + + // + // link QH-TDs to total 100 frame list entry to speed up the execution. + // + for (Index = 0; Index < 100; Index++) { + LinkQHToFrameList ( + HcDev->FrameListEntry, + (UINT16) ((LoadFrameListIndex + Index) & 0x3FF), + PtrQH + ); + } + // + // Poll QH-TDs execution and get result. + // detail status is returned + // + Status = ExecuteControlTransfer ( + HcDev, + PtrSetupTD, + LoadFrameListIndex, + DataLength, + TimeOut, + TransferResult + ); + // + // Remove Control Transfer QH-TDs structure from the frame list + // and update the pointers in the Frame List + // and other pointers in other related QH structures. + // + for (Index = 0; Index < 100; Index++) { + DelLinkSingleQH ( + HcDev, + PtrQH, + (UINT16) ((LoadFrameListIndex + Index) & 0x3FF), + FALSE, + FALSE + ); + } + // + // delete setup stage TD; the QH is reserved for the next stages. + // + DeleteQueuedTDs (HcDev, PtrSetupTD); + + // + // if setup stage error, return error + // + if (EFI_ERROR (Status)) { + goto Done; + } + // + // some control transfers do not have Data Stage + // + if (PtrFirstDataTD != NULL) { + + LinkTDToQH (PtrQH, PtrFirstDataTD); + LoadFrameListIndex = (UINT16) ((GetCurrentFrameNumber (HcDev->PciIo, FrameNumReg)) & 0x3FF); + + for (Index = 0; Index < 500; Index++) { + LinkQHToFrameList ( + HcDev->FrameListEntry, + (UINT16) ((LoadFrameListIndex + Index) & 0x3FF), + PtrQH + ); + } + + Status = ExecuteControlTransfer ( + HcDev, + PtrFirstDataTD, + LoadFrameListIndex, + DataLength, + TimeOut, + TransferResult + ); + + for (Index = 0; Index < 500; Index++) { + DelLinkSingleQH ( + HcDev, + PtrQH, + (UINT16) ((LoadFrameListIndex + Index) & 0x3FF), + FALSE, + FALSE + ); + } + // + // delete data stage TD; the QH is reserved for the next stage. + // + DeleteQueuedTDs (HcDev, PtrFirstDataTD); + } + // + // if data stage error, goto done and return error + // + if (EFI_ERROR (Status)) { + goto Done; + } + + LinkTDToQH (PtrQH, PtrStatusTD); + // + // get the frame list index that the QH-TDs will be linked to. + // + LoadFrameListIndex = (UINT16) ((GetCurrentFrameNumber (HcDev->PciIo, FrameNumReg)) & 0x3FF); + + for (Index = 0; Index < 100; Index++) { + // + // put the QH-TDs directly or indirectly into the proper place + // in the Frame List + // + LinkQHToFrameList ( + HcDev->FrameListEntry, + (UINT16) ((LoadFrameListIndex + Index) & 0x3FF), + PtrQH + ); + } + // + // Poll QH-TDs execution and get result. + // detail status is returned + // + Status = ExecuteControlTransfer ( + HcDev, + PtrStatusTD, + LoadFrameListIndex, + DataLength, + TimeOut, + TransferResult + ); + + // + // Delete Control Transfer QH-TDs structure + // and update the pointers in the Frame List + // and other pointers in other related QH structures. + // + // TRUE means must search other framelistindex + // + for (Index = 0; Index < 100; Index++) { + DelLinkSingleQH ( + HcDev, + PtrQH, + (UINT16) ((LoadFrameListIndex + Index) & 0x3FF), + FALSE, + FALSE + ); + } + + DeleteQueuedTDs (HcDev, PtrStatusTD); + + } else { + // + // link setup stage TD with data stage TD + // + PtrPreTD = PtrSetupTD; + if (PtrFirstDataTD != NULL) { + LinkTDToTD (PtrSetupTD, PtrFirstDataTD); + PtrPreTD = ptrLastDataTD; + } + // + // link status TD with previous TD + // + LinkTDToTD (PtrPreTD, PtrStatusTD); + + // + // link QH with TD + // + LinkTDToQH (PtrQH, PtrSetupTD); + + LoadFrameListIndex = (UINT16) ((GetCurrentFrameNumber (HcDev->PciIo, FrameNumReg)) & 0x3FF); + for (Index = 0; Index < 500; Index++) { + // + // put the QH-TDs directly or indirectly into the proper place + // in the Frame List + // + LinkQHToFrameList ( + HcDev->FrameListEntry, + (UINT16) ((LoadFrameListIndex + Index) & 0x3FF), + PtrQH + ); + } + // + // Poll QH-TDs execution and get result. + // detail status is returned + // + Status = ExecuteControlTransfer ( + HcDev, + PtrSetupTD, + LoadFrameListIndex, + DataLength, + TimeOut, + TransferResult + ); + // + // Remove Control Transfer QH-TDs structure from the frame list + // and update the pointers in the Frame List + // and other pointers in other related QH structures. + // + for (Index = 0; Index < 500; Index++) { + DelLinkSingleQH ( + HcDev, + PtrQH, + (UINT16) ((LoadFrameListIndex + Index) & 0x3FF), + FALSE, + FALSE + ); + } + + DeleteQueuedTDs (HcDev, PtrSetupTD); + } + +Done: + + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + + if (Mapping != NULL) { + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + } + + if (RequestMapping != NULL) { + HcDev->PciIo->Unmap (HcDev->PciIo, RequestMapping); + } + // + // if has errors that cause host controller halt, + // then return EFI_DEVICE_ERROR directly. + // + if (!IsStatusOK (HcDev->PciIo, StatusReg)) { + + ClearStatusReg (HcDev->PciIo, StatusReg); + *TransferResult |= EFI_USB_ERR_SYSTEM; + return EFI_DEVICE_ERROR; + } + + ClearStatusReg (HcDev->PciIo, StatusReg); + HcDev->PciIo->Flush (HcDev->PciIo); + return Status; +} + +EFI_STATUS +EFIAPI +UHCIBulkTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +/*++ + + Routine Description: + Submits bulk transfer to a bulk endpoint of a USB device. + + Arguments: + + This A pointer to the EFI_USB_HC_PROTOCOL instance. + + DeviceAddress Represents the address of the target device on the USB, + which is assigned during USB enumeration. + EndPointAddress The combination of an endpoint number and an + endpoint direction of the target USB device. + Each endpoint address supports data transfer in + one direction except the control endpoint + (whose default endpoint address is 0). + It is the caller's responsibility to make sure that + the EndPointAddress represents a bulk endpoint. + + MaximumPacketLength Indicates the maximum packet size the target endpoint + is capable of sending or receiving. + + Data A pointer to the buffer of data that will be transmitted + to USB device or received from USB device. + DataLength When input, indicates the size, in bytes, of the data buffer + specified by Data. When output, indicates the actually + transferred data size. + + DataToggle A pointer to the data toggle value. On input, it indicates + the initial data toggle value the bulk transfer should adopt; + on output, it is updated to indicate the data toggle value + of the subsequent bulk transfer. + + TimeOut Indicates the maximum time, in microseconds, which the + transfer is allowed to complete. + + TransferResult A pointer to the detailed result information of the + bulk transfer. + + Returns: + EFI_SUCCESS + The bulk transfer was completed successfully. + EFI_OUT_OF_RESOURCES + The bulk transfer could not be submitted due to lack of resource. + EFI_INVALID_PARAMETER + Some parameters are invalid. + EFI_TIMEOUT + The bulk transfer failed due to timeout. + EFI_DEVICE_ERROR + The bulk transfer failed due to host controller or device error. + Caller should check TranferResult for detailed error information. + +--*/ +{ + USB_HC_DEV *HcDev; + UINT32 StatusReg; + UINT32 FrameNumReg; + UINTN DataLen; + QH_STRUCT *PtrQH; + TD_STRUCT *PtrFirstTD; + TD_STRUCT *PtrTD; + TD_STRUCT *PtrPreTD; + UINT16 LoadFrameListIndex; + UINT16 SavedFrameListIndex; + UINT8 PktID; + UINT8 *PtrDataSource; + UINT8 *Ptr; + BOOLEAN IsFirstTD; + EFI_STATUS Status; + UINT32 Index; + UINT8 PktSize; + + EFI_USB_DATA_DIRECTION TransferDirection; + // + // Used to calculate how many entries are linked to the + // specified bulk transfer QH-TDs + // + UINT32 LinkTimes; + + BOOLEAN ShortPacketEnable; + EFI_PHYSICAL_ADDRESS TempPtr; + VOID *Mapping; + + HcDev = USB_HC_DEV_FROM_THIS (This); + StatusReg = (UINT32) (USBSTS); + FrameNumReg = (UINT32) (USBFRNUM); + PktID = INPUT_PACKET_ID; + PtrTD = NULL; + PtrFirstTD = NULL; + PtrPreTD = NULL; + LinkTimes = 1; + DataLen = 0; + Ptr = NULL; + ShortPacketEnable = FALSE; + Mapping = NULL; + + // + // Parameters Checking + // + + if ((DataLength == NULL) || + (Data == NULL) || + (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // if has errors that cause host controller halt, + // then return EFI_DEVICE_ERROR directly. + // + if (!IsStatusOK (HcDev->PciIo, StatusReg)) { + + ClearStatusReg (HcDev->PciIo, StatusReg); + *TransferResult = EFI_USB_ERR_SYSTEM; + return EFI_DEVICE_ERROR; + } + + if (*DataLength == 0) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + if (MaximumPacketLength != 8 && + MaximumPacketLength != 16 && + MaximumPacketLength != 32 && + MaximumPacketLength != 64) { + return EFI_INVALID_PARAMETER; + } + + // + // Enable the maximum packet size (64bytes) + // that can be used for full speed bandwidth reclamation + // at the end of a frame. + // + EnableMaxPacketSize (HcDev); + + Status = ClearStatusReg (HcDev->PciIo, StatusReg); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + // + // construct QH and TD data structures, + // and link them together + // + if (EndPointAddress & 0x80) { + TransferDirection = EfiUsbDataIn; + } else { + TransferDirection = EfiUsbDataOut; + } + + switch (TransferDirection) { + + case EfiUsbDataIn: + ShortPacketEnable = TRUE; + PktID = INPUT_PACKET_ID; + PtrDataSource = Data; + DataLen = *DataLength; + + // + // BusMasterWrite means cpu read + // + Status = HcDev->PciIo->Map ( + HcDev->PciIo, + EfiPciIoOperationBusMasterWrite, + PtrDataSource, + &DataLen, + &TempPtr, + &Mapping + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Ptr = (UINT8 *) ((UINTN) TempPtr); + break; + + case EfiUsbDataOut: + PktID = OUTPUT_PACKET_ID; + PtrDataSource = Data; + DataLen = *DataLength; + + // + // BusMasterRead means cpu write + // + Status = HcDev->PciIo->Map ( + HcDev->PciIo, + EfiPciIoOperationBusMasterRead, + PtrDataSource, + &DataLen, + &TempPtr, + &Mapping + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Ptr = (UINT8 *) ((UINTN) TempPtr); + break; + + default: + return EFI_INVALID_PARAMETER; + } + + // + // create QH structure and init + // + Status = CreateQH (HcDev, &PtrQH); + if (EFI_ERROR (Status)) { + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + return Status; + } + + // + // i is used to calculate the total number of TDs. + // + Index = 0; + + IsFirstTD = TRUE; + while (DataLen > 0) { + + // + // create TD structures and link together + // + + PktSize = (UINT8) DataLen; + if (DataLen > MaximumPacketLength) { + PktSize = MaximumPacketLength; + } + + Status = GenDataTD ( + HcDev, + DeviceAddress, + EndPointAddress, + Ptr, + PktSize, + PktID, + *DataToggle, + FALSE, + &PtrTD + ); + + if (EFI_ERROR (Status)) { + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + DeleteQueuedTDs (HcDev, PtrFirstTD); + return Status; + } + + // + // Enable short packet detection. + // (default action is disabling short packet detection) + // + if (ShortPacketEnable) { + EnableorDisableTDShortPacket (PtrTD, TRUE); + } + + if (IsFirstTD) { + PtrFirstTD = PtrTD; + PtrFirstTD->ptrNextTD = NULL; + IsFirstTD = FALSE; + } else { + // + // Link two TDs in vertical depth + // + LinkTDToTD (PtrPreTD, PtrTD); + } + + Index++; + + PtrPreTD = PtrTD; + + *DataToggle ^= 1; + Ptr += PktSize; + DataLen -= PktSize; + } + + // + // link TD structures to QH structure + // + LinkTDToQH (PtrQH, PtrFirstTD); + + // + // calculate how many entries are linked to the specified bulk transfer QH-TDs + // the below values are referred to the USB spec revision1.1. + // + switch (MaximumPacketLength) { + case 8: + LinkTimes = Index / 71 + 1; + break; + + case 16: + LinkTimes = Index / 51 + 1; + break; + + case 32: + LinkTimes = Index / 33 + 1; + break; + + case 64: + LinkTimes = Index / 19 + 1; + break; + } + + LinkTimes += 500; + + // + // put QH-TDs into Frame list + // + LoadFrameListIndex = (UINT16) ((GetCurrentFrameNumber (HcDev->PciIo, FrameNumReg)) & 0x3FF); + SavedFrameListIndex = LoadFrameListIndex; + + for (Index = 0; Index <= LinkTimes; Index++) { + + // + // put the QH-TD directly or indirectly into the proper place + // in the Frame List + // + LinkQHToFrameList (HcDev->FrameListEntry, LoadFrameListIndex, PtrQH); + + LoadFrameListIndex += 1; + LoadFrameListIndex &= 0x3FF; + } + + LoadFrameListIndex = SavedFrameListIndex; + + // + // Execute QH-TD and get result + // + // + // detail status is put into the Result field in the pIRP + // the Data Toggle value is also re-updated to the value + // of the last successful TD + // + Status = ExecBulkorSyncInterruptTransfer ( + HcDev, + PtrFirstTD, + LoadFrameListIndex, + DataLength, + DataToggle, + TimeOut, + TransferResult + ); + + // + // Delete Bulk transfer QH-TD structure + // and maitain the pointers in the Frame List + // and other pointers in related QH structure + // + // TRUE means must search other framelistindex + // + for (Index = 0; Index <= LinkTimes; Index++) { + DelLinkSingleQH ( + HcDev, + PtrQH, + LoadFrameListIndex, + FALSE, + FALSE + ); + LoadFrameListIndex += 1; + LoadFrameListIndex &= 0x3FF; + } + + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + + DeleteQueuedTDs (HcDev, PtrFirstTD); + + if (Mapping != NULL) { + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + } + + // + // if has errors that cause host controller halt, + // then return EFI_DEVICE_ERROR directly. + // + if (!IsStatusOK (HcDev->PciIo, StatusReg)) { + + ClearStatusReg (HcDev->PciIo, StatusReg); + *TransferResult |= EFI_USB_ERR_SYSTEM; + return EFI_DEVICE_ERROR; + } + + ClearStatusReg (HcDev->PciIo, StatusReg); + + HcDev->PciIo->Flush (HcDev->PciIo); + + return Status; +} + +EFI_STATUS +EFIAPI +UHCIAsyncInterruptTransfer ( + IN EFI_USB_HC_PROTOCOL * This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN BOOLEAN IsSlowDevice, + IN UINT8 MaxiumPacketLength, + IN BOOLEAN IsNewTransfer, + IN OUT UINT8 *DataToggle, + IN UINTN PollingInterval, OPTIONAL + IN UINTN DataLength, OPTIONAL + IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, OPTIONAL + IN VOID *Context OPTIONAL + ) +/*++ + + Routine Description: + Submits an asynchronous interrupt transfer to an + interrupt endpoint of a USB device. + + Arguments: + + This A pointer to the EFI_USB_HC_PROTOCOL instance. + + DeviceAddress Represents the address of the target device on the USB, + which is assigned during USB enumeration. + + EndPointAddress The combination of an endpoint number and an endpoint + direction of the target USB device. Each endpoint address + supports data transfer in one direction except the + control endpoint (whose default endpoint address is 0). + It is the caller's responsibility to make sure that + the EndPointAddress represents an interrupt endpoint. + + IsSlowDevice Indicates whether the target device is slow device + or full-speed device. + + MaxiumPacketLength Indicates the maximum packet size the target endpoint + is capable of sending or receiving. + + IsNewTransfer If TRUE, an asynchronous interrupt pipe is built between + the host and the target interrupt endpoint. + If FALSE, the specified asynchronous interrupt pipe + is canceled. + + DataToggle A pointer to the data toggle value. On input, it is valid + when IsNewTransfer is TRUE, and it indicates the initial + data toggle value the asynchronous interrupt transfer + should adopt. + On output, it is valid when IsNewTransfer is FALSE, + and it is updated to indicate the data toggle value of + the subsequent asynchronous interrupt transfer. + + PollingInterval Indicates the interval, in milliseconds, that the + asynchronous interrupt transfer is polled. + This parameter is required when IsNewTransfer is TRUE. + + DataLength Indicates the length of data to be received at the + rate specified by PollingInterval from the target + asynchronous interrupt endpoint. This parameter + is only required when IsNewTransfer is TRUE. + + CallBackFunction The Callback function.This function is called at the + rate specified by PollingInterval.This parameter is + only required when IsNewTransfer is TRUE. + + Context The context that is passed to the CallBackFunction. + This is an optional parameter and may be NULL. + + Returns: + EFI_SUCCESS + The asynchronous interrupt transfer request has been successfully + submitted or canceled. + EFI_INVALID_PARAMETER + Some parameters are invalid. + EFI_OUT_OF_RESOURCES + The request could not be completed due to a lack of resources. + EFI_DEVICE_ERROR + Can't read register +--*/ +{ + USB_HC_DEV *HcDev; + UINT32 StatusReg; + UINT32 FrameNumReg; + UINTN DataLen; + QH_STRUCT *ptrFirstQH; + QH_STRUCT *PtrQH; + QH_STRUCT *ptrPreQH; + TD_STRUCT *PtrFirstTD; + TD_STRUCT *PtrTD; + TD_STRUCT *PtrPreTD; + UINT16 LoadFrameListIndex; + UINT16 Index; + UINT8 PktID; + UINT8 *Ptr; + UINT8 *MappedPtr; + BOOLEAN IsFirstTD; + BOOLEAN IsFirstQH; + EFI_STATUS Status; + BOOLEAN ShortPacketEnable; + UINT8 CurrentDataToggle; + EFI_PHYSICAL_ADDRESS TempPtr; + VOID *Mapping; + UINT8 PktSize; + QH_STRUCT *TempQH; + EFI_TPL OldTpl; + + HcDev = USB_HC_DEV_FROM_THIS (This); + StatusReg = (UINT32) (USBSTS); + FrameNumReg = (UINT32) (USBFRNUM); + Mapping = NULL; + ShortPacketEnable = FALSE; + + PktID = INPUT_PACKET_ID; + PtrTD = NULL; + PtrFirstTD = NULL; + PtrPreTD = NULL; + Ptr = NULL; + PtrQH = NULL; + ptrPreQH = NULL; + ptrFirstQH = NULL; + + if ((EndPointAddress & 0x80) == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // delete Async interrupt transfer request + // + if (!IsNewTransfer) { + + OldTpl = gBS->RaiseTPL (EFI_TPL_NOTIFY); + + Status = DeleteAsyncINTQHTDs ( + HcDev, + DeviceAddress, + EndPointAddress, + DataToggle + ); + + gBS->RestoreTPL (OldTpl); + + return Status; + } + // + // if has errors that cause host controller halt, + // then return EFI_DEVICE_ERROR directly. + // + if (!IsStatusOK (HcDev->PciIo, StatusReg)) { + + ClearStatusReg (HcDev->PciIo, StatusReg); + return EFI_DEVICE_ERROR; + } + + ClearStatusReg (HcDev->PciIo, StatusReg); + + // + // submit Async interrupt transfer request + // + if (PollingInterval < 1 || PollingInterval > 255) { + return EFI_INVALID_PARAMETER; + } + + if (DataLength == 0) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + ShortPacketEnable = TRUE; + PktID = INPUT_PACKET_ID; + DataLen = DataLength; + Ptr = AllocatePool (DataLen); + if (Ptr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // BusMasterWrite means cpu read + // + Status = HcDev->PciIo->Map ( + HcDev->PciIo, + EfiPciIoOperationBusMasterWrite, + Ptr, + &DataLen, + &TempPtr, + &Mapping + ); + if (EFI_ERROR (Status)) { + gBS->FreePool (Ptr); + return Status; + } + + MappedPtr = (UINT8 *) ((UINTN) TempPtr); + + CurrentDataToggle = *DataToggle; + + IsFirstTD = TRUE; + + while (DataLen > 0) { + // + // create TD structures and link together + // + + PktSize = (UINT8) DataLen; + if (DataLen > MaxiumPacketLength) { + PktSize = MaxiumPacketLength; + } + + Status = GenDataTD ( + HcDev, + DeviceAddress, + EndPointAddress, + MappedPtr, + PktSize, + PktID, + CurrentDataToggle, + IsSlowDevice, + &PtrTD + ); + if (EFI_ERROR (Status)) { + gBS->FreePool (Ptr); + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + DeleteQueuedTDs (HcDev, PtrFirstTD); + return Status; + } + // + // Enable short packet detection. + // + if (ShortPacketEnable) { + EnableorDisableTDShortPacket (PtrTD, TRUE); + } + + if (IsFirstTD) { + PtrFirstTD = PtrTD; + PtrFirstTD->ptrNextTD = NULL; + IsFirstTD = FALSE; + } else { + // + // Link two TDs in vertical depth + // + LinkTDToTD (PtrPreTD, PtrTD); + } + + PtrPreTD = PtrTD; + + CurrentDataToggle ^= 1; + MappedPtr += PktSize; + DataLen -= PktSize; + } + + // + // roll one value back + // + CurrentDataToggle ^= 1; + + // + // create a list of QH structures and init, + // link TDs to all the QHs, and link all the QHs together using internal + // defined pointer of the QH_STRUCT. + // + IsFirstQH = TRUE; + ptrPreQH = NULL; + for (Index = 0; Index < 1024;) { + + Status = CreateQH (HcDev, &PtrQH); + if (EFI_ERROR (Status)) { + gBS->FreePool (Ptr); + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + DeleteQueuedTDs (HcDev, PtrFirstTD); + PtrQH = ptrFirstQH; + while (PtrQH) { + TempQH = PtrQH; + PtrQH = TempQH->ptrNextIntQH; + UhciFreePool (HcDev, (UINT8 *) TempQH, sizeof (QH_STRUCT)); + } + + return Status; + } + + // + // link TD structures to QH structure + // + LinkTDToQH (PtrQH, PtrFirstTD); + + if (IsFirstQH) { + ptrFirstQH = PtrQH; + ptrFirstQH->ptrNextIntQH = NULL; + IsFirstQH = FALSE; + } else { + // + // link neighbor QH structures together + // + ptrPreQH->ptrNextIntQH = PtrQH; + } + + ptrPreQH = PtrQH; + + Index = (UINT16) (PollingInterval + Index); + } + // + // last QH in QH list should set its next QH pointer to NULL. + // + PtrQH->ptrNextIntQH = NULL; + + // + // Save QH-TD structures in Interrupt transfer list, + // for monitor interrupt transfer execution routine use. + // + InsertQHTDToINTList ( + HcDev, + ptrFirstQH, + PtrFirstTD, + DeviceAddress, + EndPointAddress, + CurrentDataToggle, + DataLength, + PollingInterval, + Mapping, + Ptr, + CallBackFunction, + Context + ); + + // + // put QHs-TDs into Frame list + // + LoadFrameListIndex = (UINT16) ((GetCurrentFrameNumber (HcDev->PciIo, FrameNumReg)) & 0x3FF); + + PtrQH = ptrFirstQH; + + for (Index = LoadFrameListIndex; Index < (1024 + LoadFrameListIndex);) { + + // + // put the QH-TD directly or indirectly into the proper place + // in the Frame List + // + LinkQHToFrameList (HcDev->FrameListEntry, (UINT16) (Index & 0x3FF), PtrQH); + + Index = (UINT16) (PollingInterval + Index); + + PtrQH = PtrQH->ptrNextIntQH; + } + + HcDev->PciIo->Flush (HcDev->PciIo); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +UHCISyncInterruptTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN BOOLEAN IsSlowDevice, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +/*++ + + Routine Description: + Submits synchronous interrupt transfer to an interrupt endpoint + of a USB device. + + Arguments: + + This A pointer to the EFI_USB_HC_PROTOCOL instance. + + DeviceAddress Represents the address of the target device on the USB, + which is assigned during USB enumeration. + + EndPointAddress The combination of an endpoint number and an endpoint + direction of the target USB device. Each endpoint + address supports data transfer in one direction + except the control endpoint (whose default + endpoint address is 0). It is the caller's responsibility + to make sure that the EndPointAddress represents + an interrupt endpoint. + + IsSlowDevice Indicates whether the target device is slow device + or full-speed device. + + MaximumPacketLength Indicates the maximum packet size the target endpoint + is capable of sending or receiving. + + Data A pointer to the buffer of data that will be transmitted + to USB device or received from USB device. + + DataLength On input, the size, in bytes, of the data buffer specified + by Data. On output, the number of bytes transferred. + + DataToggle A pointer to the data toggle value. On input, it indicates + the initial data toggle value the synchronous interrupt + transfer should adopt; + on output, it is updated to indicate the data toggle value + of the subsequent synchronous interrupt transfer. + + TimeOut Indicates the maximum time, in microseconds, which the + transfer is allowed to complete. + + TransferResult A pointer to the detailed result information from + the synchronous interrupt transfer. + + Returns: + EFI_SUCCESS + The synchronous interrupt transfer was completed successfully. + EFI_OUT_OF_RESOURCES + The synchronous interrupt transfer could not be submitted due + to lack of resource. + EFI_INVALID_PARAMETER + Some parameters are invalid. + EFI_TIMEOUT + The synchronous interrupt transfer failed due to timeout. + EFI_DEVICE_ERROR + The synchronous interrupt transfer failed due to host controller + or device error. Caller should check TranferResult for detailed + error information. +--*/ +{ + USB_HC_DEV *HcDev; + UINT32 StatusReg; + UINT32 FrameNumReg; + UINTN DataLen; + QH_STRUCT *PtrQH; + TD_STRUCT *PtrFirstTD; + TD_STRUCT *PtrTD; + TD_STRUCT *PtrPreTD; + UINT16 LoadFrameListIndex; + UINT16 SavedFrameListIndex; + UINT32 Index; + UINT32 LinkTimes; + UINT8 PktID; + UINT8 *PtrDataSource; + UINT8 *Ptr; + BOOLEAN IsFirstTD; + EFI_STATUS Status; + BOOLEAN ShortPacketEnable; + EFI_PHYSICAL_ADDRESS TempPtr; + VOID *Mapping; + UINT8 PktSize; + + HcDev = USB_HC_DEV_FROM_THIS (This); + StatusReg = (UINT32) (USBSTS); + FrameNumReg = (UINT32) (USBFRNUM); + ShortPacketEnable = FALSE; + Mapping = NULL; + PktID = INPUT_PACKET_ID; + PtrTD = NULL; + PtrFirstTD = NULL; + PtrPreTD = NULL; + DataLen = 0; + Ptr = NULL; + Index = 0; + LinkTimes = 0; + + // + // Parameters Checking + // + + if ((DataLength == NULL) || + (Data == NULL) || + (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // if has errors that cause host controller halt, + // then return EFI_DEVICE_ERROR directly. + // + if (!IsStatusOK (HcDev->PciIo, StatusReg)) { + + ClearStatusReg (HcDev->PciIo, StatusReg); + *TransferResult = EFI_USB_ERR_SYSTEM; + return EFI_DEVICE_ERROR; + } + + if ((EndPointAddress & 0x80) == 0) { + return EFI_INVALID_PARAMETER; + } + + if (*DataLength == 0) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + if (MaximumPacketLength > 64) { + return EFI_INVALID_PARAMETER; + } + + if (IsSlowDevice && (MaximumPacketLength > 8)) { + return EFI_INVALID_PARAMETER; + } + + ClearStatusReg (HcDev->PciIo, StatusReg); + + // + // submit Sync interrupt transfer request + // + ShortPacketEnable = TRUE; + PktID = INPUT_PACKET_ID; + DataLen = *DataLength; + PtrDataSource = Data; + + // + // create QH structure and init + // + Status = CreateQH (HcDev, &PtrQH); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // BusMasterWrite means cpu read + // + Status = HcDev->PciIo->Map ( + HcDev->PciIo, + EfiPciIoOperationBusMasterWrite, + PtrDataSource, + &DataLen, + &TempPtr, + &Mapping + ); + if (EFI_ERROR (Status)) { + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + return Status; + } + + Ptr = (UINT8 *) ((UINTN) TempPtr); + + IsFirstTD = TRUE; + while (DataLen > 0) { + // + // create TD structures and link together + // + PktSize = (UINT8) DataLen; + if (DataLen > MaximumPacketLength) { + PktSize = MaximumPacketLength; + } + + Status = GenDataTD ( + HcDev, + DeviceAddress, + EndPointAddress, + Ptr, + PktSize, + PktID, + *DataToggle, + IsSlowDevice, + &PtrTD + ); + if (EFI_ERROR (Status)) { + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + DeleteQueuedTDs (HcDev, PtrFirstTD); + return Status; + } + // + // Enable short packet detection. + // + if (ShortPacketEnable) { + EnableorDisableTDShortPacket (PtrTD, TRUE); + } + + if (IsFirstTD) { + PtrFirstTD = PtrTD; + PtrFirstTD->ptrNextTD = NULL; + IsFirstTD = FALSE; + } else { + // + // Link two TDs in vertical depth + // + LinkTDToTD (PtrPreTD, PtrTD); + } + + Index++; + + PtrPreTD = PtrTD; + + *DataToggle ^= 1; + Ptr += PktSize; + DataLen -= PktSize; + } + + // + // link TD structures to QH structure + // + LinkTDToQH (PtrQH, PtrFirstTD); + + switch (MaximumPacketLength) { + case 8: + LinkTimes = Index / 71 + 1; + break; + + case 16: + LinkTimes = Index / 51 + 1; + break; + + case 32: + LinkTimes = Index / 33 + 1; + break; + + case 64: + LinkTimes = Index / 19 + 1; + break; + } + + LinkTimes += 100; + + LoadFrameListIndex = (UINT16) ((GetCurrentFrameNumber (HcDev->PciIo, FrameNumReg)) & 0x3FF); + SavedFrameListIndex = LoadFrameListIndex; + + for (Index = 0; Index < LinkTimes; Index++) { + + // + // put the QH-TD directly or indirectly into the proper place + // in the Frame List + // + LinkQHToFrameList (HcDev->FrameListEntry, LoadFrameListIndex, PtrQH); + + LoadFrameListIndex += 1; + LoadFrameListIndex &= 0x3FF; + } + + LoadFrameListIndex = SavedFrameListIndex; + // + // detail status is put into the Result field in the pIRP + // the Data Toggle value is also re-updated to the value + // of the last successful TD + // + Status = ExecBulkorSyncInterruptTransfer ( + HcDev, + PtrFirstTD, + LoadFrameListIndex, + DataLength, + DataToggle, + TimeOut, + TransferResult + ); + // + // Delete Sync Interrupt transfer QH-TD structure + // and maintain the pointers in the Frame List + // and other pointers in related QH structure + // + // TRUE means must search other framelistindex + // + for (Index = 0; Index <= LinkTimes; Index++) { + DelLinkSingleQH ( + HcDev, + PtrQH, + LoadFrameListIndex, + FALSE, + FALSE + ); + LoadFrameListIndex += 1; + LoadFrameListIndex &= 0x3FF; + } + + UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT)); + + DeleteQueuedTDs (HcDev, PtrFirstTD); + + HcDev->PciIo->Unmap (HcDev->PciIo, Mapping); + + // + // if has errors that cause host controller halt, + // then return EFI_DEVICE_ERROR directly. + // + if (!IsStatusOK (HcDev->PciIo, StatusReg)) { + + ClearStatusReg (HcDev->PciIo, StatusReg); + *TransferResult |= EFI_USB_ERR_SYSTEM; + return EFI_DEVICE_ERROR; + } + + ClearStatusReg (HcDev->PciIo, StatusReg); + + HcDev->PciIo->Flush (HcDev->PciIo); + + return Status; +} + +EFI_STATUS +EFIAPI +UHCIIsochronousTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN UINTN DataLength, + OUT UINT32 *TransferResult + ) +/*++ + + Routine Description: + Submits isochronous transfer to a target USB device. + + Arguments: + + This - A pointer to the EFI_USB_HC_PROTOCOL instance. + DeviceAddress - Represents the address of the target device on the USB, + which is assigned during USB enumeration. + EndPointAddress - End point address + MaximumPacketLength - Indicates the maximum packet size that the + default control transfer endpoint is capable of + sending or receiving. + Data - A pointer to the buffer of data that will be transmitted + to USB device or received from USB device. + DataLength - Indicates the size, in bytes, of the data buffer + specified by Data. + TransferResult - A pointer to the detailed result information generated + by this control transfer. + Returns: + EFI_UNSUPPORTED + +--*/ +{ + return EFI_UNSUPPORTED; +} + + +EFI_STATUS +EFIAPI +UHCIAsyncIsochronousTransfer ( + IN EFI_USB_HC_PROTOCOL * This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN UINTN DataLength, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack, + IN VOID *Context OPTIONAL + ) +/*++ + + Routine Description: + Submits Async isochronous transfer to a target USB device. + + Arguments: + + This - A pointer to the EFI_USB_HC_PROTOCOL instance. + + DeviceAddress - Represents the address of the target device on the USB, + which is assigned during USB enumeration. + + EndPointAddress - End point address + + MaximumPacketLength - Indicates the maximum packet size that the + default control transfer endpoint is capable of + sending or receiving. + + Data - A pointer to the buffer of data that will be transmitted + to USB device or received from USB device. + + IsochronousCallBack - When the transfer complete, the call back function will be called + + Context - Pass to the call back function as parameter + + Returns: + EFI_UNSUPPORTED + +--*/ +{ + return EFI_UNSUPPORTED; +} + +VOID +EFIAPI +MonitorInterruptTrans ( + IN EFI_EVENT Event, + IN VOID *Context + ) +/*++ + Routine Description: + Interrupt transfer periodic check handler + Arguments: + Event - Interrupt event + Contex - Pointer to USB_HC_DEV + Returns: + None +--*/ +{ + + USB_HC_DEV *HcDev; + INTERRUPT_LIST *PtrList; + LIST_ENTRY *Link; + UINT32 Result; + VOID *DataBuffer; + UINTN DataLen; + UINTN ActualLen; + UINTN ErrTDPos; + UINT32 StatusAddr; + LIST_ENTRY *NextLink; + + HcDev = (USB_HC_DEV *) Context; + StatusAddr = (UINT32) (USBSTS); + + // + // interrupt transfer list is empty, means that no interrupt transfer + // is submitted by far. + // + if (IsListEmpty (&(HcDev->InterruptListHead))) { + return ; + } + + NextLink = HcDev->InterruptListHead.ForwardLink; + do { + + Link = NextLink; + NextLink = Link->ForwardLink; + + PtrList = INTERRUPT_LIST_FROM_LINK (Link); + + // + // get TD execution results. + // ErrTDPos is zero-based value indicating the first error TD's position + // in the TDs' list. + // This value is only valid when Result not equal NOERROR. + // + ExecuteAsyncINTTDs ( + HcDev, + PtrList, + &Result, + &ErrTDPos, + &ActualLen + ); + + // + // interrupt transfer has not been executed yet. + // + if (((Result & EFI_USB_ERR_NAK) == EFI_USB_ERR_NAK) || + ((Result & EFI_USB_ERR_NOTEXECUTE) == EFI_USB_ERR_NOTEXECUTE)) { + continue; + } + // + // get actual data length transferred data and its data length. + // + DataLen = ActualLen; + DataBuffer = AllocatePool (DataLen); + if (DataBuffer == NULL) { + return ; + } + + CopyMem ( + DataBuffer, + PtrList->PtrFirstTD->pTDBuffer, + DataLen + ); + + // + // only if interrupt endpoint responds + // and the interrupt transfer stops because of completion + // or error, then we will call callback function. + // + if (Result == EFI_USB_NOERROR) { + // + // add for real platform debug + // + if (PtrList->InterruptCallBack != NULL) { + (PtrList->InterruptCallBack) ( + DataBuffer, + DataLen, + PtrList->InterruptContext, + Result + ); + } + + if (DataBuffer) { + gBS->FreePool (DataBuffer); + } + + // + // update should done after data buffer got. + // + UpdateAsyncINTQHTDs (PtrList, Result, (UINT32) ErrTDPos); + + } else { + + DEBUG ((EFI_D_ERROR, "interrupt transfer error code is %x\n", Result)); + + if (DataBuffer) { + gBS->FreePool (DataBuffer); + } + // + // leave error recovery to its related device driver. + // A common case of the error recovery is to re-submit the interrupt + // transfer. + // When an interrupt transfer is re-submitted, its position in the linked + // list is changed. It is inserted to the head of the linked list, while + // this function scans the whole list from head to tail. Thus, the + // re-submitted interrupt transfer's callback function will not be called + // again in this round. + // + if (PtrList->InterruptCallBack != NULL) { + (PtrList->InterruptCallBack) ( + NULL, + 0, + PtrList->InterruptContext, + Result + ); + } + } + } while (NextLink != &(HcDev->InterruptListHead)); + +} diff --git a/EdkModulePkg/Bus/Pci/Uhci/Dxe/uhci.h b/EdkModulePkg/Bus/Pci/Uhci/Dxe/uhci.h new file mode 100644 index 0000000..93da46c --- /dev/null +++ b/EdkModulePkg/Bus/Pci/Uhci/Dxe/uhci.h @@ -0,0 +1,1187 @@ +/*++ + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Uhci.h + +Abstract: + + +Revision History +--*/ + +#ifndef _UHCI_H +#define _UHCI_H + +/* + * Universal Host Controller Interface data structures and defines + */ + +#include + +#define EFI_D_UHCI EFI_D_INFO + +// +// stall time +// +#define STALL_1_MILLI_SECOND 1000 +#define STALL_1_SECOND 1000 * STALL_1_MILLI_SECOND + +#define FORCE_GLOBAL_RESUME_TIME 20 * STALL_1_MILLI_SECOND + +#define ROOT_PORT_REST_TIME 50 * STALL_1_MILLI_SECOND + +#define PORT_RESET_RECOVERY_TIME 10 * STALL_1_MILLI_SECOND + +// +// 50 ms +// +#define INTERRUPT_POLLING_TIME 50 * 1000 * 10 + +// +// UHCI IO Space Address Register Register locates at +// offset 20 ~ 23h of PCI Configuration Space (UHCI spec, Revision 1.1), +// so, its BAR Index is 4. +// +#define USB_BAR_INDEX 4 + +// +// One memory block uses 1 page (common buffer for QH,TD use.) +// +#define NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES 1 + + +#define bit(a) 1 << (a) + +// +// //////////////////////////////////////////////////////////////////////// +// +// Universal Host Controller Registers Definitions +// +////////////////////////////////////////////////////////////////////////// +extern UINT16 USBBaseAddr; + +/* Command register */ +#define USBCMD 0 /* Command Register Offset 00-01h */ +#define USBCMD_RS bit (0) /* Run/Stop */ +#define USBCMD_HCRESET bit (1) /* Host reset */ +#define USBCMD_GRESET bit (2) /* Global reset */ +#define USBCMD_EGSM bit (3) /* Global Suspend Mode */ +#define USBCMD_FGR bit (4) /* Force Global Resume */ +#define USBCMD_SWDBG bit (5) /* SW Debug mode */ +#define USBCMD_CF bit (6) /* Config Flag (sw only) */ +#define USBCMD_MAXP bit (7) /* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS 2 /* Status Register Offset 02-03h */ +#define USBSTS_USBINT bit (0) /* Interrupt due to IOC */ +#define USBSTS_ERROR bit (1) /* Interrupt due to error */ +#define USBSTS_RD bit (2) /* Resume Detect */ +#define USBSTS_HSE bit (3) /* Host System Error*/ +#define USBSTS_HCPE bit (4) /* Host Controller Process Error*/ +#define USBSTS_HCH bit (5) /* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR 4 /* Interrupt Enable Register 04-05h */ +#define USBINTR_TIMEOUT bit (0) /* Timeout/CRC error enable */ +#define USBINTR_RESUME bit (1) /* Resume interrupt enable */ +#define USBINTR_IOC bit (2) /* Interrupt On Complete enable */ +#define USBINTR_SP bit (3) /* Short packet interrupt enable */ + +/* Frame Number Register Offset 06-08h */ +#define USBFRNUM 6 + +/* Frame List Base Address Register Offset 08-0Bh */ +#define USBFLBASEADD 8 + +/* Start of Frame Modify Register Offset 0Ch */ +#define USBSOF 0x0c + +/* USB port status and control registers */ +#define USBPORTSC1 0x10 /*Port 1 offset 10-11h */ +#define USBPORTSC2 0x12 /*Port 2 offset 12-13h */ + +#define USBPORTSC_CCS bit (0) /* Current Connect Status*/ +#define USBPORTSC_CSC bit (1) /* Connect Status Change */ +#define USBPORTSC_PED bit (2) /* Port Enable / Disable */ +#define USBPORTSC_PEDC bit (3) /* Port Enable / Disable Change */ +#define USBPORTSC_LSL bit (4) /* Line Status Low bit*/ +#define USBPORTSC_LSH bit (5) /* Line Status High bit*/ +#define USBPORTSC_RD bit (6) /* Resume Detect */ +#define USBPORTSC_LSDA bit (8) /* Low Speed Device Attached */ +#define USBPORTSC_PR bit (9) /* Port Reset */ +#define USBPORTSC_SUSP bit (12) /* Suspend */ + +/* PCI Configuration Registers for USB */ + +// +// Class Code Register offset +// +#define CLASSC 0x09 +// +// USB IO Space Base Address Register offset +// +#define USBBASE 0x20 + +// +// USB legacy Support +// +#define USB_EMULATION 0xc0 + +// +// USB Base Class Code,Sub-Class Code and Programming Interface. +// +#define PCI_CLASSC_PI_UHCI 0x00 + +#define SETUP_PACKET_ID 0x2D +#define INPUT_PACKET_ID 0x69 +#define OUTPUT_PACKET_ID 0xE1 +#define ERROR_PACKET_ID 0x55 + +// +// //////////////////////////////////////////////////////////////////////// +// +// USB Transfer Mechanism Data Structures +// +////////////////////////////////////////////////////////////////////////// +#pragma pack(1) +// +// USB Class Code structure +// +typedef struct { + UINT8 PI; + UINT8 SubClassCode; + UINT8 BaseCode; +} USB_CLASSC; + +typedef struct { + UINT32 QHHorizontalTerminate : 1; + UINT32 QHHorizontalQSelect : 1; + UINT32 QHHorizontalRsvd : 2; + UINT32 QHHorizontalPtr : 28; + UINT32 QHVerticalTerminate : 1; + UINT32 QHVerticalQSelect : 1; + UINT32 QHVerticalRsvd : 2; + UINT32 QHVerticalPtr : 28; +} QUEUE_HEAD; + +typedef struct { + UINT32 TDLinkPtrTerminate : 1; + UINT32 TDLinkPtrQSelect : 1; + UINT32 TDLinkPtrDepthSelect : 1; + UINT32 TDLinkPtrRsvd : 1; + UINT32 TDLinkPtr : 28; + UINT32 TDStatusActualLength : 11; + UINT32 TDStatusRsvd : 5; + UINT32 TDStatus : 8; + UINT32 TDStatusIOC : 1; + UINT32 TDStatusIOS : 1; + UINT32 TDStatusLS : 1; + UINT32 TDStatusErr : 2; + UINT32 TDStatusSPD : 1; + UINT32 TDStatusRsvd2 : 2; + UINT32 TDTokenPID : 8; + UINT32 TDTokenDevAddr : 7; + UINT32 TDTokenEndPt : 4; + UINT32 TDTokenDataToggle : 1; + UINT32 TDTokenRsvd : 1; + UINT32 TDTokenMaxLen : 11; + UINT32 TDBufferPtr; +} TD; + +#pragma pack() + +typedef struct { + QUEUE_HEAD QH; + VOID *ptrNext; + VOID *ptrDown; + VOID *ptrNextIntQH; // for interrupt transfer's special use + VOID *LoopPtr; +} QH_STRUCT; + +typedef struct { + TD TDData; + UINT8 *pTDBuffer; + VOID *ptrNextTD; + VOID *ptrNextQH; + UINT16 TDBufferLength; + UINT16 reserved; +} TD_STRUCT; + +// +// //////////////////////////////////////////////////////////////////////// +// +// Universal Host Controller Device Data Structure +// +////////////////////////////////////////////////////////////////////////// +#define USB_HC_DEV_FROM_THIS(a) CR (a, USB_HC_DEV, UsbHc, USB_HC_DEV_SIGNATURE) + +#define USB_HC_DEV_SIGNATURE EFI_SIGNATURE_32 ('u', 'h', 'c', 'i') +#define INTERRUPT_LIST_SIGNATURE EFI_SIGNATURE_32 ('i', 'n', 't', 's') +typedef struct { + UINTN Signature; + + LIST_ENTRY Link; + UINT8 DevAddr; + UINT8 EndPoint; + UINT8 DataToggle; + UINT8 Reserved[5]; + TD_STRUCT *PtrFirstTD; + QH_STRUCT *PtrQH; + UINTN DataLen; + UINTN PollInterval; + VOID *Mapping; + UINT8 *DataBuffer; // allocated host memory, not mapped memory + EFI_ASYNC_USB_TRANSFER_CALLBACK InterruptCallBack; + VOID *InterruptContext; +} INTERRUPT_LIST; + +#define INTERRUPT_LIST_FROM_LINK(a) CR (a, INTERRUPT_LIST, Link, INTERRUPT_LIST_SIGNATURE) + +typedef struct { + UINT32 FrameListPtrTerminate : 1; + UINT32 FrameListPtrQSelect : 1; + UINT32 FrameListRsvd : 2; + UINT32 FrameListPtr : 28; + +} FRAMELIST_ENTRY; + +typedef struct _MEMORY_MANAGE_HEADER { + UINT8 *BitArrayPtr; + UINTN BitArraySizeInBytes; + UINT8 *MemoryBlockPtr; + UINTN MemoryBlockSizeInBytes; + VOID *Mapping; + struct _MEMORY_MANAGE_HEADER *Next; +} MEMORY_MANAGE_HEADER; + +typedef struct { + UINTN Signature; + EFI_USB_HC_PROTOCOL UsbHc; + EFI_PCI_IO_PROTOCOL *PciIo; + + // + // local data + // + LIST_ENTRY InterruptListHead; + FRAMELIST_ENTRY *FrameListEntry; + VOID *FrameListMapping; + MEMORY_MANAGE_HEADER *MemoryHeader; + EFI_EVENT InterruptTransTimer; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + +} USB_HC_DEV; + +extern EFI_DRIVER_BINDING_PROTOCOL gUhciDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gUhciComponentName; + +EFI_STATUS +WriteUHCCommandReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 CmdAddrOffset, + IN UINT16 UsbCmd + ); + +EFI_STATUS +ReadUHCCommandReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 CmdAddrOffset, + IN OUT UINT16 *Data + ); + +EFI_STATUS +WriteUHCStatusReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusAddrOffset, + IN UINT16 UsbSts + ); + +EFI_STATUS +ReadUHCStatusReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusAddrOffset, + IN OUT UINT16 *Data + ); + +EFI_STATUS +ClearStatusReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusAddrOffset + ); + +EFI_STATUS +ReadUHCFrameNumberReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 FrameNumAddrOffset, + IN OUT UINT16 *Data + ); + +EFI_STATUS +WriteUHCFrameListBaseReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 FlBaseAddrOffset, + IN UINT32 UsbFrameListBaseAddr + ); + +EFI_STATUS +ReadRootPortReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 PortAddrOffset, + IN OUT UINT16 *Data + ); + +EFI_STATUS +WriteRootPortReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 PortAddrOffset, + IN UINT16 ControlBits + ); + +EFI_STATUS +WaitForUHCHalt ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusRegAddr, + IN UINTN Timeout + ); + +BOOLEAN +IsStatusOK ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusRegAddr + ); + +BOOLEAN +IsHostSysOrProcessErr ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 StatusRegAddr + ); + +// +// This routine programs the USB frame number register. We assume that the +// HC schedule execution is stopped. +// +EFI_STATUS +SetFrameNumberReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 FRNUMAddr, + IN UINT16 Index + ); + +UINT16 +GetCurrentFrameNumber ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 FRNUMAddr + ); + +EFI_STATUS +SetFrameListBaseAddress ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 FLBASEADDRReg, + IN UINT32 Addr + ); + +UINT32 +GetFrameListBaseAddress ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 FLBAddr + ); + +EFI_STATUS +CreateFrameList ( + IN USB_HC_DEV *HcDev, + IN UINT32 FLBASEADDRReg + ); + +EFI_STATUS +FreeFrameListEntry ( + IN USB_HC_DEV *UhcDev + ); + +VOID +InitFrameList ( + IN USB_HC_DEV *HcDev + ); + + +EFI_STATUS +CreateQH ( + IN USB_HC_DEV *HcDev, + OUT QH_STRUCT **pptrQH + ); + +VOID +SetQHHorizontalLinkPtr ( + IN QH_STRUCT *ptrQH, + IN VOID *ptrNext + ); + +VOID * +GetQHHorizontalLinkPtr ( + IN QH_STRUCT *ptrQH + ); + +VOID +SetQHHorizontalQHorTDSelect ( + IN QH_STRUCT *ptrQH, + IN BOOLEAN bQH + ); + +VOID +SetQHHorizontalValidorInvalid ( + IN QH_STRUCT *ptrQH, + IN BOOLEAN bValid + ); + +VOID +SetQHVerticalLinkPtr ( + IN QH_STRUCT *ptrQH, + IN VOID *ptrNext + ); + +VOID * +GetQHVerticalLinkPtr ( + IN QH_STRUCT *ptrQH + ); + +VOID +SetQHVerticalQHorTDSelect ( + IN QH_STRUCT *ptrQH, + IN BOOLEAN bQH + ); + +BOOLEAN +IsQHHorizontalQHSelect ( + IN QH_STRUCT *ptrQH + ); + +VOID +SetQHVerticalValidorInvalid ( + IN QH_STRUCT *ptrQH, + IN BOOLEAN bValid + ); + +BOOLEAN +GetQHVerticalValidorInvalid ( + IN QH_STRUCT *ptrQH + ); + +EFI_STATUS +AllocateTDStruct ( + IN USB_HC_DEV *HcDev, + OUT TD_STRUCT **ppTDStruct + ); +/*++ + +Routine Description: + + Allocate TD Struct + +Arguments: + + HcDev - USB_HC_DEV + ppTDStruct - place to store TD_STRUCT pointer +Returns: + + EFI_SUCCESS + +--*/ + +EFI_STATUS +CreateTD ( + IN USB_HC_DEV *HcDev, + OUT TD_STRUCT **pptrTD + ); +/*++ + +Routine Description: + + Create TD + +Arguments: + + HcDev - USB_HC_DEV + pptrTD - TD_STRUCT pointer to store + +Returns: + + EFI_OUT_OF_RESOURCES - Can't allocate resources + EFI_SUCCESS - Success + +--*/ + + +EFI_STATUS +GenSetupStageTD ( + IN USB_HC_DEV *HcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN BOOLEAN bSlow, + IN UINT8 *pDevReq, + IN UINT8 RequestLen, + OUT TD_STRUCT **ppTD + ); +/*++ + +Routine Description: + + Generate Setup Stage TD + +Arguments: + + HcDev - USB_HC_DEV + DevAddr - Device address + Endpoint - Endpoint number + bSlow - Full speed or low speed + pDevReq - Device request + RequestLen - Request length + ppTD - TD_STRUCT to return +Returns: + + EFI_OUT_OF_RESOURCES - Can't allocate memory + EFI_SUCCESS - Success + +--*/ + +EFI_STATUS +GenDataTD ( + IN USB_HC_DEV *HcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 *pData, + IN UINT8 Len, + IN UINT8 PktID, + IN UINT8 Toggle, + IN BOOLEAN bSlow, + OUT TD_STRUCT **ppTD + ); +/*++ + +Routine Description: + + Generate Data Stage TD + +Arguments: + + HcDev - USB_HC_DEV + DevAddr - Device address + Endpoint - Endpoint number + pData - Data buffer + Len - Data length + PktID - Packet ID + Toggle - Data toggle value + bSlow - Full speed or low speed + ppTD - TD_STRUCT to return +Returns: + + EFI_OUT_OF_RESOURCES - Can't allocate memory + EFI_SUCCESS - Success + +--*/ + +EFI_STATUS +CreateStatusTD ( + IN USB_HC_DEV *HcDev, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 PktID, + IN BOOLEAN bSlow, + OUT TD_STRUCT **ppTD + ); +/*++ + +Routine Description: + + Generate Setup Stage TD + +Arguments: + + HcDev - USB_HC_DEV + DevAddr - Device address + Endpoint - Endpoint number + bSlow - Full speed or low speed + pDevReq - Device request + RequestLen - Request length + ppTD - TD_STRUCT to return +Returns: + + EFI_OUT_OF_RESOURCES - Can't allocate memory + EFI_SUCCESS - Success + +--*/ + +VOID +SetTDLinkPtrValidorInvalid ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bValid + ); + +VOID +SetTDLinkPtrQHorTDSelect ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bQH + ); + +VOID +SetTDLinkPtrDepthorBreadth ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bDepth + ); + +VOID +SetTDLinkPtr ( + IN TD_STRUCT *ptrTDStruct, + IN VOID *ptrNext + ); + +VOID * +GetTDLinkPtr ( + IN TD_STRUCT *ptrTDStruct + ); + +VOID +EnableorDisableTDShortPacket ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bEnable + ); + +VOID +SetTDControlErrorCounter ( + IN TD_STRUCT *ptrTDStruct, + IN UINT8 nMaxErrors + ); + +VOID +SetTDLoworFullSpeedDevice ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bLowSpeedDevice + ); + +VOID +SetTDControlIsochronousorNot ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bIsochronous + ); + +VOID +SetorClearTDControlIOC ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bSet + ); + +VOID +SetTDStatusActiveorInactive ( + IN TD_STRUCT *ptrTDStruct, + IN BOOLEAN bActive + ); + +UINT16 +SetTDTokenMaxLength ( + IN TD_STRUCT *ptrTDStruct, + IN UINT16 nMaxLen + ); + +VOID +SetTDTokenDataToggle1 ( + IN TD_STRUCT *ptrTDStruct + ); + +VOID +SetTDTokenDataToggle0 ( + IN TD_STRUCT *ptrTDStruct + ); + +UINT8 +GetTDTokenDataToggle ( + IN TD_STRUCT *ptrTDStruct + ); + +VOID +SetTDTokenEndPoint ( + IN TD_STRUCT *ptrTDStruct, + IN UINTN nEndPoint + ); + +VOID +SetTDTokenDeviceAddress ( + IN TD_STRUCT *ptrTDStruct, + IN UINTN nDevAddr + ); + +VOID +SetTDTokenPacketID ( + IN TD_STRUCT *ptrTDStruct, + IN UINT8 nPID + ); + +VOID +SetTDDataBuffer ( + IN TD_STRUCT *ptrTDStruct + ); + +BOOLEAN +IsTDStatusActive ( + IN TD_STRUCT *ptrTDStruct + ); + +BOOLEAN +IsTDStatusStalled ( + IN TD_STRUCT *ptrTDStruct + ); + +BOOLEAN +IsTDStatusBufferError ( + IN TD_STRUCT *ptrTDStruct + ); + +BOOLEAN +IsTDStatusBabbleError ( + IN TD_STRUCT *ptrTDStruct + ); + +BOOLEAN +IsTDStatusNAKReceived ( + IN TD_STRUCT *ptrTDStruct + ); + +BOOLEAN +IsTDStatusCRCTimeOutError ( + IN TD_STRUCT *ptrTDStruct + ); + +BOOLEAN +IsTDStatusBitStuffError ( + IN TD_STRUCT *ptrTDStruct + ); + +UINT16 +GetTDStatusActualLength ( + IN TD_STRUCT *ptrTDStruct + ); + +UINT16 +GetTDTokenMaxLength ( + IN TD_STRUCT *ptrTDStruct + ); + +UINT8 +GetTDTokenEndPoint ( + IN TD_STRUCT *ptrTDStruct + ); + +UINT8 +GetTDTokenDeviceAddress ( + IN TD_STRUCT *ptrTDStruct + ); + +UINT8 +GetTDTokenPacketID ( + IN TD_STRUCT *ptrTDStruct + ); + +UINT8 * +GetTDDataBuffer ( + IN TD_STRUCT *ptrTDStruct + ); + +BOOLEAN +GetTDLinkPtrValidorInvalid ( + IN TD_STRUCT *ptrTDStruct + ); + +UINTN +CountTDsNumber ( + IN TD_STRUCT *ptrFirstTD + ); + +VOID +LinkTDToQH ( + IN QH_STRUCT *ptrQH, + IN TD_STRUCT *ptrTD + ); + +VOID +LinkTDToTD ( + IN TD_STRUCT *ptrPreTD, + IN TD_STRUCT *ptrTD + ); + +VOID +SetorClearCurFrameListTerminate ( + IN FRAMELIST_ENTRY *pCurEntry, + IN BOOLEAN bSet + ); + +VOID +SetCurFrameListQHorTD ( + IN FRAMELIST_ENTRY *pCurEntry, + IN BOOLEAN bQH + ); + +BOOLEAN +GetCurFrameListTerminate ( + IN FRAMELIST_ENTRY *pCurEntry + ); + +VOID +SetCurFrameListPointer ( + IN FRAMELIST_ENTRY *pCurEntry, + IN UINT8 *ptr + ); + +VOID * +GetCurFrameListPointer ( + IN FRAMELIST_ENTRY *pCurEntry + ); + +VOID +LinkQHToFrameList ( + IN FRAMELIST_ENTRY *pEntry, + IN UINT16 FrameListIndex, + IN QH_STRUCT *ptrQH + ); +/*++ + +Routine Description: + + Link QH To Frame List + +Arguments: + + pEntry - FRAMELIST_ENTRY + FrameListIndex - Frame List Index + PtrQH - QH to link +Returns: + + VOID + +--*/ +VOID +DeleteQHTDs ( + IN FRAMELIST_ENTRY *pEntry, + IN QH_STRUCT *ptrQH, + IN TD_STRUCT *ptrFirstTD, + IN UINT16 FrameListIndex, + IN BOOLEAN SearchOther + ); + +VOID +DelLinkSingleQH ( + IN USB_HC_DEV *HcDev, + IN QH_STRUCT *ptrQH, + IN UINT16 FrameListIndex, + IN BOOLEAN SearchOther, + IN BOOLEAN Delete + ); + +VOID +DeleteQueuedTDs ( + IN USB_HC_DEV *HcDev, + IN TD_STRUCT *ptrFirstTD + ); + +VOID +InsertQHTDToINTList ( + IN USB_HC_DEV *HcDev, + IN QH_STRUCT *ptrQH, + IN TD_STRUCT *ptrFirstTD, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DataToggle, + IN UINTN DataLength, + IN UINTN PollingInterval, + IN VOID *Mapping, + IN UINT8 *DataBuffer, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, + IN VOID *Context + ); +/*++ +Routine Description: + Insert QH and TD To Interrupt List +Arguments: + + HcDev - USB_HC_DEV + PtrQH - QH_STRUCT + PtrFirstTD - First TD_STRUCT + DeviceAddress - Device Address + EndPointAddress - EndPoint Address + DataToggle - Data Toggle + DataLength - Data length + PollingInterval - Polling Interval when inserted to frame list + Mapping - Mapping alue + DataBuffer - Data buffer + CallBackFunction- CallBackFunction after interrupt transfeer + Context - CallBackFunction Context passed as function parameter +Returns: + EFI_SUCCESS - Sucess + EFI_INVALID_PARAMETER - Paremeter is error + +--*/ + +EFI_STATUS +DeleteAsyncINTQHTDs ( + IN USB_HC_DEV *HcDev, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + OUT UINT8 *DataToggle + ); +/*++ +Routine Description: + + Delete Async INT QH and TDs +Arguments: + + HcDev - USB_HC_DEV + DeviceAddress - Device Address + EndPointAddress - EndPoint Address + DataToggle - Data Toggle + +Returns: + EFI_SUCCESS - Sucess + EFI_INVALID_PARAMETER - Paremeter is error + +--*/ +BOOLEAN +CheckTDsResults ( + IN TD_STRUCT *ptrTD, + IN UINTN RequiredLen, + OUT UINT32 *Result, + OUT UINTN *ErrTDPos, + OUT UINTN *ActualTransferSize + ); +/*++ + +Routine Description: + + Check TDs Results + +Arguments: + + PtrTD - TD_STRUCT to check + RequiredLen - Required Len + Result - Transfer result + ErrTDPos - Error TD Position + ActualTransferSize - Actual Transfer Size + +Returns: + + TRUE - Sucess + FALSE - Fail + +--*/ +VOID +ExecuteAsyncINTTDs ( + IN USB_HC_DEV *HcDev, + IN INTERRUPT_LIST *ptrList, + OUT UINT32 *Result, + OUT UINTN *ErrTDPos, + OUT UINTN *ActualLen + ) ; +/*++ + +Routine Description: + + Execute Async Interrupt TDs + +Arguments: + + HcDev - USB_HC_DEV + PtrList - INTERRUPT_LIST + Result - Transfer result + ErrTDPos - Error TD Position + ActualTransferSize - Actual Transfer Size + +Returns: + + VOID + +--*/ +VOID +UpdateAsyncINTQHTDs ( + IN INTERRUPT_LIST *ptrList, + IN UINT32 Result, + IN UINT32 ErrTDPos + ); +/*++ + +Routine Description: + + Update Async Interrupt QH and TDs + +Arguments: + + PtrList - INTERRUPT_LIST + Result - Transfer reslut + ErrTDPos - Error TD Position + +Returns: + + VOID + +--*/ +VOID +ReleaseInterruptList ( + IN USB_HC_DEV *HcDev, + IN LIST_ENTRY *ListHead + ); +/*++ + +Routine Description: + + Release Interrupt List +Arguments: + + HcDev - USB_HC_DEV + ListHead - List head + +Returns: + + VOID + +--*/ +EFI_STATUS +ExecuteControlTransfer ( + IN USB_HC_DEV *HcDev, + IN TD_STRUCT *ptrTD, + IN UINT32 wIndex, + OUT UINTN *ActualLen, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ); +/*++ + +Routine Description: + + Execute Control Transfer + +Arguments: + + HcDev - USB_HC_DEV + PtrTD - TD_STRUCT + wIndex - No use + ActualLen - Actual transfered Len + TimeOut - TimeOut value in milliseconds + TransferResult - Transfer result +Returns: + + EFI_SUCCESS - Sucess + EFI_DEVICE_ERROR - Error + + +--*/ +EFI_STATUS +ExecBulkorSyncInterruptTransfer ( + IN USB_HC_DEV *HcDev, + IN TD_STRUCT *ptrTD, + IN UINT32 wIndex, + OUT UINTN *ActualLen, + OUT UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ); +/*++ + +Routine Description: + + Execute Bulk or SyncInterrupt Transfer + +Arguments: + + HcDev - USB_HC_DEV + PtrTD - TD_STRUCT + wIndex - No use + ActualLen - Actual transfered Len + DataToggle - Data Toggle + TimeOut - TimeOut value in milliseconds + TransferResult - Transfer result +Returns: + + EFI_SUCCESS - Sucess + EFI_DEVICE_ERROR - Error +--*/ + +EFI_STATUS +InitializeMemoryManagement ( + IN USB_HC_DEV *HcDev + ); + +EFI_STATUS +CreateMemoryBlock ( + IN USB_HC_DEV *HcDev, + IN MEMORY_MANAGE_HEADER **MemoryHeader, + IN UINTN MemoryBlockSizeInPages + ); + +EFI_STATUS +FreeMemoryHeader ( + IN USB_HC_DEV *HcDev, + IN MEMORY_MANAGE_HEADER *MemoryHeader + ); + +EFI_STATUS +UhciAllocatePool ( + IN USB_HC_DEV *UhcDev, + IN UINT8 **Pool, + IN UINTN AllocSize + ); + +VOID +UhciFreePool ( + IN USB_HC_DEV *HcDev, + IN UINT8 *Pool, + IN UINTN AllocSize + ); + +VOID +InsertMemoryHeaderToList ( + IN MEMORY_MANAGE_HEADER *MemoryHeader, + IN MEMORY_MANAGE_HEADER *NewMemoryHeader + ); + +EFI_STATUS +AllocMemInMemoryBlock ( + IN MEMORY_MANAGE_HEADER *MemoryHeader, + IN VOID **Pool, + IN UINTN NumberOfMemoryUnit + ); + +BOOLEAN +IsMemoryBlockEmptied ( + IN MEMORY_MANAGE_HEADER *MemoryHeaderPtr + ); + +VOID +DelinkMemoryBlock ( + IN MEMORY_MANAGE_HEADER *FirstMemoryHeader, + IN MEMORY_MANAGE_HEADER *FreeMemoryHeader + ); + +EFI_STATUS +DelMemoryManagement ( + IN USB_HC_DEV *HcDev + ); + +VOID +EnableMaxPacketSize ( + IN USB_HC_DEV *HcDev + ); + +VOID +CleanUsbTransactions ( + IN USB_HC_DEV *HcDev + ); + +VOID +TurnOffUSBEmulation ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ); + +#endif -- cgit v1.1