From 913cb9dc645d6db47d8c2a0be0369083b8bed25d Mon Sep 17 00:00:00 2001 From: vanjeff Date: Wed, 11 Jul 2007 06:46:38 +0000 Subject: Import EhciDxe and UhciDxe into MdeModulePkg. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3191 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c | 2331 +++++++++++++++++++++++++++++++++++ 1 file changed, 2331 insertions(+) create mode 100644 MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c (limited to 'MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c') diff --git a/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c b/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c new file mode 100644 index 0000000000..1c3e37a7bc --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c @@ -0,0 +1,2331 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Uhci.c + +Abstract: + + The UHCI driver model and HC protocol routines. + +Revision History + + +**/ + +#include "Uhci.h" + + +/** + Provides software reset for the USB host controller. + + This : A pointer to the EFI_USB_HC_PROTOCOL instance. + Attributes: A bit mask of the reset operation to perform. + + @return EFI_SUCCESS : The reset operation succeeded. + @return EFI_INVALID_PARAMETER : Attributes is not valid. + @return EFI_DEVICE_ERROR : An error was encountered while attempting + @return to perform the reset operation. + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciReset ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT16 Attributes + ) +{ + USB_HC_DEV *Uhc; + EFI_TPL OldTpl; + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + Uhc = UHC_FROM_USB_HC_PROTO (This); + + switch (Attributes) { + case EFI_USB_HC_RESET_GLOBAL: + // + // Stop schedule and set the Global Reset bit in the command register + // + UhciStopHc (Uhc, STALL_1_SECOND); + UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET); + + // + // Wait 50ms for root port to let reset complete + // See UHCI spec page122 Reset signaling + // + gBS->Stall (ROOT_PORT_REST_TIME); + + // + // Clear the Global Reset bit to zero. + // + UhciClearRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET); + + // + // UHCI spec page120 reset recovery time + // + gBS->Stall (PORT_RESET_RECOVERY_TIME); + break; + + case EFI_USB_HC_RESET_HOST_CONTROLLER: + // + // Stop schedule and set Host Controller Reset bit to 1 + // + UhciStopHc (Uhc, STALL_1_SECOND); + UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_HCRESET); + + // + // this bit will be reset by Host Controller when reset is completed. + // wait 10ms to let reset complete + // + gBS->Stall (PORT_RESET_RECOVERY_TIME); + break; + + default: + goto ON_INVAILD_PARAMETER; + } + + // + // Delete all old transactions on the USB bus, then + // reinitialize the frame list + // + UhciFreeAllAsyncReq (Uhc); + UhciDestoryFrameList (Uhc); + UhciInitFrameList (Uhc); + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +ON_INVAILD_PARAMETER: + + gBS->RestoreTPL (OldTpl); + + return EFI_INVALID_PARAMETER; +} + + +/** + Retrieves current state of the USB host controller. + + This : A pointer to the EFI_USB_HC_PROTOCOL instance. + State : A pointer to the EFI_USB_HC_STATE data structure that + indicates current state of the USB host controller. + + @return EFI_SUCCESS : State was returned + @return EFI_INVALID_PARAMETER : State is NULL. + @return EFI_DEVICE_ERROR : An error was encountered + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciGetState ( + IN EFI_USB_HC_PROTOCOL *This, + OUT EFI_USB_HC_STATE *State + ) +{ + USB_HC_DEV *Uhc; + UINT16 UsbSts; + UINT16 UsbCmd; + + if (State == NULL) { + return EFI_INVALID_PARAMETER; + } + + Uhc = UHC_FROM_USB_HC_PROTO (This); + + UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); + UsbSts = UhciReadReg (Uhc->PciIo, USBSTS_OFFSET); + + if (UsbCmd & USBCMD_EGSM) { + *State = EfiUsbHcStateSuspend; + + } else if ((UsbSts & USBSTS_HCH) != 0) { + *State = EfiUsbHcStateHalt; + + } else { + *State = EfiUsbHcStateOperational; + } + + return EFI_SUCCESS; +} + + +/** + Sets the USB host controller to a specific state. + + This : A pointer to the EFI_USB_HC_PROTOCOL instance. + State : Indicates the state of the host controller that will be set. + + @return EFI_SUCCESS : The USB host controller was successfully set + @return EFI_INVALID_PARAMETER : State is invalid. + @return EFI_DEVICE_ERROR : Failed to set the state specified + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciSetState ( + IN EFI_USB_HC_PROTOCOL *This, + IN EFI_USB_HC_STATE State + ) +{ + EFI_USB_HC_STATE CurState; + USB_HC_DEV *Uhc; + EFI_TPL OldTpl; + EFI_STATUS Status; + UINT16 UsbCmd; + + Uhc = UHC_FROM_USB_HC_PROTO (This); + Status = UhciGetState (This, &CurState); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + if (CurState == State) { + return EFI_SUCCESS; + } + + Status = EFI_SUCCESS; + OldTpl = gBS->RaiseTPL (UHCI_TPL); + + switch (State) { + case EfiUsbHcStateHalt: + Status = UhciStopHc (Uhc, STALL_1_SECOND); + break; + + case EfiUsbHcStateOperational: + UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); + + if (CurState == EfiUsbHcStateHalt) { + // + // Set Run/Stop bit to 1, also set the bandwidht reclamation + // point to 64 bytes + // + UsbCmd |= USBCMD_RS | USBCMD_MAXP; + UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); + + } else if (CurState == EfiUsbHcStateSuspend) { + // + // If FGR(Force Global Resume) bit is 0, set it + // + if ((UsbCmd & USBCMD_FGR) == 0) { + UsbCmd |= USBCMD_FGR; + UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); + } + + // + // wait 20ms to let resume complete (20ms is specified by UHCI spec) + // + gBS->Stall (FORCE_GLOBAL_RESUME_TIME); + + // + // Write FGR bit to 0 and EGSM(Enter Global Suspend Mode) bit to 0 + // + UsbCmd &= ~USBCMD_FGR; + UsbCmd &= ~USBCMD_EGSM; + UsbCmd |= USBCMD_RS; + UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); + } + + break; + + case EfiUsbHcStateSuspend: + Status = UhciSetState (This, EfiUsbHcStateHalt); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + // + // Set Enter Global Suspend Mode bit to 1. + // + UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); + UsbCmd |= USBCMD_EGSM; + UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Retrieves the number of root hub ports. + + This : A pointer to the EFI_USB_HC_PROTOCOL instance. + PortNumber : A pointer to the number of the root hub ports. + + @return EFI_SUCCESS : The port number was retrieved successfully. + @return EFI_INVALID_PARAMETER : PortNumber is NULL. + @return EFI_DEVICE_ERROR : An error was encountered + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciGetRootHubPortNumber ( + IN EFI_USB_HC_PROTOCOL *This, + OUT UINT8 *PortNumber + ) +{ + USB_HC_DEV *Uhc; + UINT32 Offset; + UINT16 PortSC; + UINT32 Index; + + Uhc = UHC_FROM_USB_HC_PROTO (This); + + if (PortNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + + *PortNumber = 0; + + for (Index = 0; Index < USB_MAX_ROOTHUB_PORT; Index++) { + Offset = USBPORTSC_OFFSET + Index * 2; + PortSC = UhciReadReg (Uhc->PciIo, Offset); + + // + // Port status's bit 7 is reserved and always returns 1 if + // the port number is valid. Intel's UHCI (in EHCI controller) + // returns 0 in this bit if port number is invalid. Also, if + // PciIo IoRead returns error, 0xFFFF is returned to caller. + // + if (((PortSC & 0x80) != 0) && (PortSC != 0xFFFF)) { + (*PortNumber)++; + } + } + + Uhc->RootPorts = *PortNumber; + + UHCI_DEBUG (("UhciGetRootHubPortNumber: %d ports\n", Uhc->RootPorts)); + return EFI_SUCCESS; +} + + +/** + Retrieves the current status of a USB root hub port. + + This : A pointer to the EFI_USB_HC_PROTOCOL. + PortNumber : Specifies the root hub port. This value is zero-based. + PortStatus : A pointer to the current port status bits and port status change bits. + + @return EFI_SUCCESS : The port status was returned in PortStatus. + @return EFI_INVALID_PARAMETER : PortNumber is invalid. + @return EFI_DEVICE_ERROR : Can't read register + +**/ +EFI_STATUS +EFIAPI +UhciGetRootHubPortStatus ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus + ) +{ + USB_HC_DEV *Uhc; + UINT32 Offset; + UINT16 PortSC; + + Uhc = UHC_FROM_USB_HC_PROTO (This); + + if (PortStatus == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (PortNumber >= Uhc->RootPorts) { + return EFI_INVALID_PARAMETER; + } + + Offset = USBPORTSC_OFFSET + PortNumber * 2; + PortStatus->PortStatus = 0; + PortStatus->PortChangeStatus = 0; + + PortSC = UhciReadReg (Uhc->PciIo, Offset); + + if (PortSC & USBPORTSC_CCS) { + PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION; + } + + if (PortSC & USBPORTSC_PED) { + PortStatus->PortStatus |= USB_PORT_STAT_ENABLE; + } + + if (PortSC & USBPORTSC_SUSP) { + UHCI_DEBUG (("UhciGetRootHubPortStatus: port %d is suspended\n", PortNumber)); + PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND; + } + + if (PortSC & USBPORTSC_PR) { + PortStatus->PortStatus |= USB_PORT_STAT_RESET; + } + + if (PortSC & USBPORTSC_LSDA) { + PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED; + } + + // + // CHC will always return one in port owner bit + // + PortStatus->PortStatus |= USB_PORT_STAT_OWNER; + + if (PortSC & USBPORTSC_CSC) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION; + } + + if (PortSC & USBPORTSC_PEDC) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE; + } + + return EFI_SUCCESS; +} + + +/** + Sets a feature for the specified root hub port. + + This : A pointer to the EFI_USB_HC_PROTOCOL. + PortNumber : Specifies the root hub port whose feature + is requested to be set. + PortFeature : Indicates the feature selector associated + with the feature set request. + + @return EFI_SUCCESS : The feature was set for the port. + @return EFI_INVALID_PARAMETER : PortNumber is invalid or PortFeature is invalid. + @return EFI_DEVICE_ERROR : Can't read register + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciSetRootHubPortFeature ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + USB_HC_DEV *Uhc; + EFI_TPL OldTpl; + UINT32 Offset; + UINT16 PortSC; + UINT16 Command; + + Uhc = UHC_FROM_USB_HC_PROTO (This); + + if (PortNumber >= Uhc->RootPorts) { + return EFI_INVALID_PARAMETER; + } + + Offset = USBPORTSC_OFFSET + PortNumber * 2; + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + PortSC = UhciReadReg (Uhc->PciIo, Offset); + + switch (PortFeature) { + case EfiUsbPortSuspend: + Command = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); + if (!(Command & USBCMD_EGSM)) { + // + // if global suspend is not active, can set port suspend + // + PortSC &= 0xfff5; + PortSC |= USBPORTSC_SUSP; + } + break; + + case EfiUsbPortReset: + PortSC &= 0xfff5; + PortSC |= USBPORTSC_PR; + break; + + case EfiUsbPortPower: + // + // No action + // + break; + + case EfiUsbPortEnable: + PortSC &= 0xfff5; + PortSC |= USBPORTSC_PED; + break; + + default: + gBS->RestoreTPL (OldTpl); + return EFI_INVALID_PARAMETER; + } + + UhciWriteReg (Uhc->PciIo, Offset, PortSC); + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Clears a feature for the specified root hub port. + + This : A pointer to the EFI_USB_HC_PROTOCOL instance. + PortNumber : Specifies the root hub port whose feature + is requested to be cleared. + PortFeature : Indicates the feature selector associated with the + feature clear request. + + @return EFI_SUCCESS : The feature was cleared for the port. + @return EFI_INVALID_PARAMETER : PortNumber is invalid or PortFeature is invalid. + @return EFI_DEVICE_ERROR : Can't read register + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciClearRootHubPortFeature ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + USB_HC_DEV *Uhc; + EFI_TPL OldTpl; + UINT32 Offset; + UINT16 PortSC; + + Uhc = UHC_FROM_USB_HC_PROTO (This); + + if (PortNumber >= Uhc->RootPorts) { + return EFI_INVALID_PARAMETER; + } + + Offset = USBPORTSC_OFFSET + PortNumber * 2; + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + PortSC = UhciReadReg (Uhc->PciIo, Offset); + + switch (PortFeature) { + case EfiUsbPortEnable: + PortSC &= 0xfff5; + PortSC &= ~USBPORTSC_PED; + break; + + case EfiUsbPortSuspend: + // + // Cause a resume on the specified port if in suspend mode. + // + PortSC &= 0xfff5; + PortSC &= ~USBPORTSC_SUSP; + break; + + case EfiUsbPortPower: + // + // No action + // + break; + + case EfiUsbPortReset: + PortSC &= 0xfff5; + PortSC &= ~USBPORTSC_PR; + break; + + case EfiUsbPortConnectChange: + PortSC &= 0xfff5; + PortSC |= USBPORTSC_CSC; + break; + + case EfiUsbPortEnableChange: + PortSC &= 0xfff5; + PortSC |= USBPORTSC_PEDC; + break; + + case EfiUsbPortSuspendChange: + // + // Root hub does not support this + // + break; + + case EfiUsbPortOverCurrentChange: + // + // Root hub does not support this + // + break; + + case EfiUsbPortResetChange: + // + // Root hub does not support this + // + break; + + default: + gBS->RestoreTPL (OldTpl); + return EFI_INVALID_PARAMETER; + } + + UhciWriteReg (Uhc->PciIo, Offset, PortSC); + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Submits control transfer to a target USB device. + + This : A pointer to the EFI_USB_HC_PROTOCOL instance. + DeviceAddress : Usb device address + IsSlowDevice : Whether the device is of slow speed or full speed + MaximumPacketLength : maximum packet size of the default control endpoint + Request : USB device request to send + TransferDirection : Specifies the data direction for the transfer. + Data : Data buffer to transmit from or receive into + DataLength : Number of bytes of the data + TimeOut : Maximum time, in microseconds + TransferResult : Return result in this + + @return EFI_SUCCESS : Transfer was completed successfully. + @return EFI_OUT_OF_RESOURCES : Failed due to a lack of resources. + @return EFI_INVALID_PARAMETER : Some parameters are invalid. + @return EFI_TIMEOUT : Failed due to timeout. + @return EFI_DEVICE_ERROR : Failed due to host controller or device error. + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciControlTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN BOOLEAN IsSlowDevice, + IN UINT8 MaximumPacketLength, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data, OPTIONAL + IN OUT UINTN *DataLength, OPTIONAL + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +{ + USB_HC_DEV *Uhc; + UHCI_TD_SW *TDs; + EFI_TPL OldTpl; + EFI_STATUS Status; + UHCI_QH_RESULT QhResult; + UINT8 PktId; + UINT8 *RequestPhy; + VOID *RequestMap; + UINT8 *DataPhy; + VOID *DataMap; + + Uhc = UHC_FROM_USB_HC_PROTO (This); + TDs = NULL; + DataPhy = NULL; + DataMap = NULL; + RequestPhy = NULL; + RequestMap = NULL; + + // + // Parameters Checking + // + if (Request == NULL || TransferResult == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IsSlowDevice && (MaximumPacketLength != 8)) { + return EFI_INVALID_PARAMETER; + } + + if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) && + (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) { + + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection != EfiUsbNoData) && (DataLength == NULL)) { + return EFI_INVALID_PARAMETER; + } + + *TransferResult = EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + + // + // If errors exist that cause host controller halt, + // clear status then return EFI_DEVICE_ERROR. + // + UhciAckAllInterrupt (Uhc); + + if (!UhciIsHcWorking (Uhc->PciIo)) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + + // + // Map the Request and data for bus master access, + // then create a list of TD for this transfer + // + Status = UhciMapUserRequest (Uhc, Request, &RequestPhy, &RequestMap); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = UhciMapUserData (Uhc, TransferDirection, Data, DataLength, &PktId, &DataPhy, &DataMap); + + if (EFI_ERROR (Status)) { + Uhc->PciIo->Unmap (Uhc->PciIo, RequestMap); + goto ON_EXIT; + } + + TDs = UhciCreateCtrlTds ( + Uhc, + DeviceAddress, + PktId, + RequestPhy, + DataPhy, + *DataLength, + MaximumPacketLength, + IsSlowDevice + ); + + if (TDs == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto UNMAP_DATA; + } + + // + // According to the speed of the end point, link + // the TD to corrosponding queue head, then check + // the execution result + // + UhciLinkTdToQh (Uhc->CtrlQh, TDs); + Status = UhciExecuteTransfer (Uhc, Uhc->CtrlQh, TDs, TimeOut, IsSlowDevice, &QhResult); + UhciUnlinkTdFromQh (Uhc->CtrlQh, TDs); + + Uhc->PciIo->Flush (Uhc->PciIo); + + *TransferResult = QhResult.Result; + + if (DataLength != NULL) { + *DataLength = QhResult.Complete; + } + + UhciDestoryTds (Uhc, TDs); + +UNMAP_DATA: + Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); + Uhc->PciIo->Unmap (Uhc->PciIo, RequestMap); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Submits bulk transfer to a bulk endpoint of a USB device. + + This :A pointer to the EFI_USB_HC_PROTOCOL instance. + DeviceAddress : Usb device address + EndPointAddress : Endpoint number and endpoint direction + MaximumPacketLength : Maximum packet size of the target endpoint + Data : Data buffer to transmit from or receive into + DataLength : Length of the data buffer + DataToggle : On input, data toggle to use, on output, the next toggle + TimeOut : Indicates the maximum time + TransferResult : Variable to receive the transfer result + + @return EFI_SUCCESS : The bulk transfer was completed successfully. + @return EFI_OUT_OF_RESOURCES : Failed due to lack of resource. + @return EFI_INVALID_PARAMETER : Some parameters are invalid. + @return EFI_TIMEOUT : Failed due to timeout. + @return EFI_DEVICE_ERROR : Failed due to host controller or device error. + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciBulkTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +{ + EFI_USB_DATA_DIRECTION Direction; + EFI_TPL OldTpl; + USB_HC_DEV *Uhc; + UHCI_TD_SW *TDs; + UHCI_QH_SW *BulkQh; + UHCI_QH_RESULT QhResult; + EFI_STATUS Status; + UINT8 PktId; + UINT8 *DataPhy; + VOID *DataMap; + + Uhc = UHC_FROM_USB_HC_PROTO (This); + DataPhy = NULL; + DataMap = NULL; + + if ((DataLength == NULL) || (Data == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (*DataLength == 0) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) && + (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) { + return EFI_INVALID_PARAMETER; + } + + *TransferResult = EFI_USB_ERR_SYSTEM; + Status = EFI_OUT_OF_RESOURCES; + + // + // If has errors that cause host controller halt, + // then return EFI_DEVICE_ERROR directly. + // + UhciAckAllInterrupt (Uhc); + + if (!UhciIsHcWorking (Uhc->PciIo)) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + + // + // Map the source data buffer for bus master access, + // then create a list of TDs + // + if (EndPointAddress & 0x80) { + Direction = EfiUsbDataIn; + } else { + Direction = EfiUsbDataOut; + } + + Status = UhciMapUserData (Uhc, Direction, Data, DataLength, &PktId, &DataPhy, &DataMap); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = EFI_OUT_OF_RESOURCES; + TDs = UhciCreateBulkOrIntTds ( + Uhc, + DeviceAddress, + EndPointAddress, + PktId, + DataPhy, + *DataLength, + DataToggle, + MaximumPacketLength, + FALSE + ); + + if (TDs == NULL) { + Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); + goto ON_EXIT; + } + + + // + // Link the TDs to bulk queue head. According to the platfore + // defintion of UHCI_NO_BW_RECLAMATION, BulkQh is either configured + // to do full speed bandwidth reclamation or not. + // + BulkQh = Uhc->BulkQh; + + UhciLinkTdToQh (BulkQh, TDs); + Status = UhciExecuteTransfer (Uhc, BulkQh, TDs, TimeOut, FALSE, &QhResult); + UhciUnlinkTdFromQh (BulkQh, TDs); + + Uhc->PciIo->Flush (Uhc->PciIo); + + *TransferResult = QhResult.Result; + *DataToggle = QhResult.NextToggle; + *DataLength = QhResult.Complete; + + UhciDestoryTds (Uhc, TDs); + Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Submits an asynchronous interrupt transfer to an interrupt endpoint of a USB device. + + This : A pointer to the EFI_USB_HC_PROTOCOL instance. + DeviceAddress : Target device address + EndPointAddress : Endpoint number with direction + IsSlowDevice : Whether the target device is slow device or full-speed device. + MaximumPacketLength : Maximum packet size of the target endpoint + IsNewTransfer : If TRUE, submit a new async interrupt transfer, otherwise + cancel an existed one + DataToggle : On input, the data toggle to use; On output, next data toggle + PollingInterval : Interrupt poll rate in milliseconds + DataLength : Length of data to receive + CallBackFunction : Function to call periodically + Context : User context + + @return EFI_SUCCESS : Request is submitted or cancelled + @return EFI_INVALID_PARAMETER : Some parameters are invalid. + @return EFI_OUT_OF_RESOURCES : Failed due to a lack of resources. + @return EFI_DEVICE_ERROR : Failed to due to device error + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciAsyncInterruptTransfer ( + IN EFI_USB_HC_PROTOCOL * This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN BOOLEAN IsSlowDevice, + IN UINT8 MaximumPacketLength, + IN BOOLEAN IsNewTransfer, + IN OUT UINT8 *DataToggle, + IN UINTN PollingInterval, OPTIONAL + IN UINTN DataLength, OPTIONAL + IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, OPTIONAL + IN VOID *Context OPTIONAL + ) +{ + USB_HC_DEV *Uhc; + UHCI_QH_SW *Qh; + UHCI_TD_SW *IntTds; + EFI_TPL OldTpl; + EFI_STATUS Status; + UINT8 *DataPtr; + UINT8 *DataPhy; + VOID *DataMap; + UINT8 PktId; + + Uhc = UHC_FROM_USB_HC_PROTO (This); + Qh = NULL; + IntTds = NULL; + DataPtr = NULL; + DataPhy = NULL; + DataMap = NULL; + + if ((EndPointAddress & 0x80) == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Delete Async interrupt transfer request + // + if (!IsNewTransfer) { + OldTpl = gBS->RaiseTPL (UHCI_TPL); + Status = UhciRemoveAsyncReq (Uhc, DeviceAddress, EndPointAddress, DataToggle); + + gBS->RestoreTPL (OldTpl); + return Status; + } + + if (PollingInterval < 1 || PollingInterval > 255) { + return EFI_INVALID_PARAMETER; + } + + if (DataLength == 0) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // If has errors that cause host controller halt, + // then return EFI_DEVICE_ERROR directly. + // + UhciAckAllInterrupt (Uhc); + + if (!UhciIsHcWorking (Uhc->PciIo)) { + return EFI_DEVICE_ERROR; + } + + // + // Allocate and map source data buffer for bus master access. + // + DataPtr = AllocatePool (DataLength); + + if (DataPtr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + + // + // Map the user data then create a queue head and + // list of TD for it. + // + Status = UhciMapUserData ( + Uhc, + EfiUsbDataIn, + DataPtr, + &DataLength, + &PktId, + &DataPhy, + &DataMap + ); + + if (EFI_ERROR (Status)) { + goto FREE_DATA; + } + + Qh = UhciCreateQh (Uhc, PollingInterval); + + if (Qh == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto UNMAP_DATA; + } + + IntTds = UhciCreateBulkOrIntTds ( + Uhc, + DeviceAddress, + EndPointAddress, + PktId, + DataPhy, + DataLength, + DataToggle, + MaximumPacketLength, + IsSlowDevice + ); + + if (IntTds == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto DESTORY_QH; + } + + UhciLinkTdToQh (Qh, IntTds); + + // + // Save QH-TD structures to async Interrupt transfer list, + // for monitor interrupt transfer execution routine use. + // + Status = UhciCreateAsyncReq ( + Uhc, + Qh, + IntTds, + DeviceAddress, + EndPointAddress, + DataLength, + PollingInterval, + DataMap, + DataPtr, + CallBackFunction, + Context, + IsSlowDevice + ); + + if (EFI_ERROR (Status)) { + goto DESTORY_QH; + } + + UhciLinkQhToFrameList (Uhc->FrameBase, Qh); + + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + +DESTORY_QH: + UsbHcFreeMem (Uhc->MemPool, Qh, sizeof (UHCI_QH_SW)); + +UNMAP_DATA: + Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); + +FREE_DATA: + gBS->FreePool (DataPtr); + Uhc->PciIo->Flush (Uhc->PciIo); + + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Submits synchronous interrupt transfer to an interrupt endpoint of a USB device. + + This : A pointer to the EFI_USB_HC_PROTOCOL instance. + DeviceAddress : Device address of the target USB device + EndPointAddress : Endpoint number and direction + IsSlowDevice : Whether the target device is of slow speed or full speed + MaximumPacketLength : Maximum packet size of target endpoint + Data : Data to transmit or receive + DataLength : On input, data length to transmit or buffer size. + On output, the number of bytes transferred. + DataToggle : On input, data toggle to use; On output, next data toggle + TimeOut : Maximum time, in microseconds, transfer is allowed to complete. + TransferResult : Variable to receive transfer result + + @return EFI_SUCCESS : Transfer was completed successfully. + @return EFI_OUT_OF_RESOURCES : Failed due to lack of resource. + @return EFI_INVALID_PARAMETER : Some parameters are invalid. + @return EFI_TIMEOUT : Failed due to timeout. + @return EFI_DEVICE_ERROR : Failed due to host controller or device error + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciSyncInterruptTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN BOOLEAN IsSlowDevice, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult + ) +{ + EFI_STATUS Status; + USB_HC_DEV *Uhc; + UHCI_TD_SW *TDs; + UHCI_QH_RESULT QhResult; + EFI_TPL OldTpl; + UINT8 *DataPhy; + VOID *DataMap; + UINT8 PktId; + + Uhc = UHC_FROM_USB_HC_PROTO (This); + DataPhy = NULL; + DataMap = NULL; + TDs = NULL; + + if ((DataLength == NULL) || (Data == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((EndPointAddress & 0x80) == 0) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataLength == 0) || (MaximumPacketLength > 64)) { + return EFI_INVALID_PARAMETER; + } + + if (IsSlowDevice && (MaximumPacketLength > 8)) { + return EFI_INVALID_PARAMETER; + } + + *TransferResult = EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + + + UhciAckAllInterrupt (Uhc); + + if (!UhciIsHcWorking (Uhc->PciIo)) { + return Status; + } + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + + // + // Map the source data buffer for bus master access. + // Create Tds list, then link it to the UHC's interrupt list + // + Status = UhciMapUserData ( + Uhc, + EfiUsbDataIn, + Data, + DataLength, + &PktId, + &DataPhy, + &DataMap + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + TDs = UhciCreateBulkOrIntTds ( + Uhc, + DeviceAddress, + EndPointAddress, + PktId, + DataPhy, + *DataLength, + DataToggle, + MaximumPacketLength, + IsSlowDevice + ); + + if (TDs == NULL) { + Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); + + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + + UhciLinkTdToQh (Uhc->SyncIntQh, TDs); + + Status = UhciExecuteTransfer (Uhc, Uhc->SyncIntQh, TDs, TimeOut, IsSlowDevice, &QhResult); + + UhciUnlinkTdFromQh (Uhc->SyncIntQh, TDs); + Uhc->PciIo->Flush (Uhc->PciIo); + + *TransferResult = QhResult.Result; + *DataToggle = QhResult.NextToggle; + *DataLength = QhResult.Complete; + + UhciDestoryTds (Uhc, TDs); + Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Submits isochronous transfer to a target USB device. + + This : A pointer to the EFI_USB_HC_PROTOCOL instance. + DeviceAddress : Target device address + EndPointAddress : End point address withdirection + MaximumPacketLength : Maximum packet size of the endpoint + Data : Data to transmit or receive + DataLength : Bytes of the data + TransferResult : Variable to receive the result + + @return EFI_UNSUPPORTED + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciIsochronousTransfer ( + IN EFI_USB_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN UINTN DataLength, + OUT UINT32 *TransferResult + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Submits Async isochronous transfer to a target USB device. + + This : A pointer to the EFI_USB_HC_PROTOCOL instance. + DeviceAddress : Target device address + EndPointAddress : End point address withdirection + MaximumPacketLength : Maximum packet size of the endpoint + Data : Data to transmit or receive + IsochronousCallBack : Function to call when the transfer completes + Context : User context + + @return EFI_UNSUPPORTED + +**/ +STATIC +EFI_STATUS +EFIAPI +UhciAsyncIsochronousTransfer ( + IN EFI_USB_HC_PROTOCOL * This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 MaximumPacketLength, + IN OUT VOID *Data, + IN UINTN DataLength, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack, + IN VOID *Context OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + + + +/** + Provides software reset for the USB host controller according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param Attributes A bit mask of the reset operation to perform. See + below for a list of the supported bit mask values. + + @return EFI_SUCCESS : The reset operation succeeded. + @return EFI_INVALID_PARAMETER : Attributes is not valid. + @return EFI_UNSUPPORTED : This type of reset is not currently supported + @return EFI_DEVICE_ERROR : Other errors + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2Reset ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT16 Attributes + ) +{ + USB_HC_DEV *UhciDev; + + UhciDev = UHC_FROM_USB2_HC_PROTO (This); + + if ((Attributes == EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG) || + (Attributes == EFI_USB_HC_RESET_HOST_WITH_DEBUG)) { + return EFI_UNSUPPORTED; + } + + return UhciReset (&UhciDev->UsbHc, Attributes); +} + + +/** + Retrieves current state of the USB host controller according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB_HC_PROTOCOL instance. + @param State Variable to receive current device state + + @return EFI_SUCCESS : The state is returned + @return EFI_INVALID_PARAMETER : State is not valid. + @return EFI_DEVICE_ERROR : Other errors2006 + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2GetState ( + IN CONST EFI_USB2_HC_PROTOCOL *This, + OUT EFI_USB_HC_STATE *State + ) +{ + USB_HC_DEV *Uhc; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + return UhciGetState (&Uhc->UsbHc, State); +} + + +/** + Sets the USB host controller to a specific state according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB_HC_PROTOCOL instance. + @param State Indicates the state of the host controller that will + be set. + + @return EFI_SUCCESS : Host controller was successfully placed in the state + @return EFI_INVALID_PARAMETER : State is invalid. + @return EFI_DEVICE_ERROR : Failed to set the state + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2SetState ( + IN EFI_USB2_HC_PROTOCOL *This, + IN EFI_USB_HC_STATE State + ) +{ + USB_HC_DEV *Uhc; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + return UhciSetState (&Uhc->UsbHc, State); +} + + +/** + Retrieves capabilities of USB host controller according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance + @param MaxSpeed A pointer to the max speed USB host controller + supports. + @param PortNumber A pointer to the number of root hub ports. + @param Is64BitCapable A pointer to an integer to show whether USB host + controller supports 64-bit memory addressing. + + @return EFI_SUCCESS : capabilities were retrieved successfully. + @return EFI_INVALID_PARAMETER : MaxSpeed or PortNumber or Is64BitCapable is NULL. + @return EFI_DEVICE_ERROR : An error was encountered + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2GetCapability ( + IN EFI_USB2_HC_PROTOCOL *This, + OUT UINT8 *MaxSpeed, + OUT UINT8 *PortNumber, + OUT UINT8 *Is64BitCapable + ) +{ + USB_HC_DEV *Uhc; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + if ((NULL == MaxSpeed) || (NULL == PortNumber) || (NULL == Is64BitCapable)) { + return EFI_INVALID_PARAMETER; + } + + *MaxSpeed = EFI_USB_SPEED_FULL; + *Is64BitCapable = (UINT8) FALSE; + + return UhciGetRootHubPortNumber (&Uhc->UsbHc, PortNumber); +} + + +/** + Retrieves the current status of a USB root hub port according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL. + @param PortNumber The port to get status + @param PortStatus A pointer to the current port status bits and port + status change bits. + + @return EFI_SUCCESS : status of the USB root hub port was returned in PortStatus. + @return EFI_INVALID_PARAMETER : PortNumber is invalid. + @return EFI_DEVICE_ERROR : Can't read register + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2GetRootHubPortStatus ( + IN CONST EFI_USB2_HC_PROTOCOL *This, + IN CONST UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus + ) +{ + USB_HC_DEV *Uhc; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + return UhciGetRootHubPortStatus (&Uhc->UsbHc, PortNumber, PortStatus); +} + + +/** + Sets a feature for the specified root hub port according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL. + @param PortNumber Specifies the root hub port whose feature is + requested to be set. + @param PortFeature Indicates the feature selector associated with the + feature set request. + + @return EFI_SUCCESS : PortFeature was set for the root port + @return EFI_INVALID_PARAMETER : PortNumber is invalid or PortFeature is invalid. + @return EFI_DEVICE_ERROR : Can't read register + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2SetRootHubPortFeature ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + USB_HC_DEV *Uhc; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + return UhciSetRootHubPortFeature (&Uhc->UsbHc, PortNumber, PortFeature); +} + + +/** + Clears a feature for the specified root hub port according to Uefi 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param PortNumber Specifies the root hub port whose feature is + requested to be cleared. + @param PortFeature Indicates the feature selector associated with the + feature clear request. + + @return EFI_SUCCESS : PortFeature was cleared for the USB root hub port + @return EFI_INVALID_PARAMETER : PortNumber is invalid or PortFeature is invalid. + @return EFI_DEVICE_ERROR : Can't read register + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2ClearRootHubPortFeature ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + USB_HC_DEV *Uhc; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + return UhciClearRootHubPortFeature (&Uhc->UsbHc, PortNumber, PortFeature); +} + + +/** + Submits control transfer to a target USB device accroding to UEFI 2.0 spec.. + + This : A pointer to the EFI_USB2_HC_PROTOCOL instance. + DeviceAddress : Target device address + DeviceSpeed : Device speed + MaximumPacketLength : Maximum packet size of the target endpoint + Request : USB device request to send + TransferDirection : Data direction of the Data stage in control transfer + Data : Data to transmit/receive in data stage + DataLength : Length of the data + TimeOut : Maximum time, in microseconds, for transfer to complete. + TransferResult : Variable to receive the transfer result + + @return EFI_SUCCESS : The control transfer was completed successfully. + @return EFI_OUT_OF_RESOURCES : Failed due to lack of resource. + @return EFI_INVALID_PARAMETER : Some parameters are invalid. + @return EFI_TIMEOUT : Failed due to timeout. + @return EFI_DEVICE_ERROR : Failed due to host controller or device error. + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2ControlTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + USB_HC_DEV *Uhc; + BOOLEAN IsSlow; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + IsSlow = (EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE; + + return UhciControlTransfer ( + &Uhc->UsbHc, + DeviceAddress, + IsSlow, + (UINT8) MaximumPacketLength, + Request, + TransferDirection, + Data, + DataLength, + TimeOut, + TransferResult + ); +} + + +/** + Submits bulk transfer to a bulk endpoint of a USB device + + This : A pointer to the EFI_USB2_HC_PROTOCOL instance. + DeviceAddress : Target device address + EndPointAddress : Endpoint number and direction + DeviceSpeed : Device speed + MaximumPacketLength : Maximum packet size of the target endpoint + DataBuffersNumber : Number of data buffers prepared for the transfer. + Data : Array of pointers to the buffers of data + DataLength : On input, size of the data buffer, On output, + actually transferred data size. + DataToggle : On input, data toggle to use; On output, next data toggle + Translator : A pointr to the transaction translator data. + TimeOut : Maximum time out, in microseconds + TransferResult : Variable to receive transfer result + + @return EFI_SUCCESS : The bulk transfer was completed successfully. + @return EFI_OUT_OF_RESOURCES : Failed due to lack of resource. + @return EFI_INVALID_PARAMETER : Some parameters are invalid. + @return EFI_TIMEOUT : Failed due to timeout. + @return EFI_DEVICE_ERROR : Failed due to host controller or device error. + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2BulkTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN UINT8 DataBuffersNumber, + IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM], + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + USB_HC_DEV *Uhc; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + if (Data == NULL || DeviceSpeed == EFI_USB_SPEED_LOW) { + return EFI_INVALID_PARAMETER; + } + + // + // For full-speed bulk transfers only the data pointed by Data[0] shall be used + // + return UhciBulkTransfer ( + &Uhc->UsbHc, + DeviceAddress, + EndPointAddress, + (UINT8) MaximumPacketLength, + *Data, + DataLength, + DataToggle, + TimeOut, + TransferResult + ); +} + + +/** + Submits an asynchronous interrupt transfer to an + interrupt endpoint of a USB device according to UEFI 2.0 spec. + + This : A pointer to the EFI_USB2_HC_PROTOCOL instance. + DeviceAddress : Target device address + EndPointAddress : Endpoint number and direction + DeviceSpeed : Device speed + MaximumPacketLength : Maximum packet size of the target endpoint + IsNewTransfer : If TRUE, submit a new transfer, if FALSE cancel old transfer + DataToggle : On input, data toggle to use; On output, next data toggle + PollingInterval : Interrupt poll rate in milliseconds + DataLength : On input, size of the data buffer, On output, + actually transferred data size. + Translator : A pointr to the transaction translator data. + CallBackFunction : Function to call periodically + Context : User context + + @return EFI_SUCCESS : Transfer was submitted + @return EFI_INVALID_PARAMETER : Some parameters are invalid. + @return EFI_OUT_OF_RESOURCES : Failed due to a lack of resources. + @return EFI_DEVICE_ERROR : Can't read register + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2AsyncInterruptTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN BOOLEAN IsNewTransfer, + IN OUT UINT8 *DataToggle, + IN UINTN PollingInterval, + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, + IN VOID *Context + ) +{ + USB_HC_DEV *Uhc; + BOOLEAN IsSlow; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + IsSlow = (EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE; + + return UhciAsyncInterruptTransfer ( + &Uhc->UsbHc, + DeviceAddress, + EndPointAddress, + IsSlow, + (UINT8) MaximumPacketLength, + IsNewTransfer, + DataToggle, + PollingInterval, + DataLength, + CallBackFunction, + Context + ); +} + + +/** + Submits synchronous interrupt transfer to an interrupt endpoint + of a USB device according to UEFI 2.0 spec. + + This : A pointer to the EFI_USB2_HC_PROTOCOL instance. + DeviceAddress : Target device address + EndPointAddress : Endpoint number and direction + DeviceSpeed : Device speed + MaximumPacketLength : Maximum packet size of the target endpoint + DataBuffersNumber : Number of data buffers prepared for the transfer. + Data : Array of pointers to the buffers of data + DataLength : On input, size of the data buffer, On output, + actually transferred data size. + DataToggle : On input, data toggle to use; On output, next data toggle + TimeOut : Maximum time out, in microseconds + Translator : A pointr to the transaction translator data. + TransferResult : Variable to receive transfer result + + @return EFI_SUCCESS : The transfer was completed successfully. + @return EFI_OUT_OF_RESOURCES : Failed due to lack of resource. + @return EFI_INVALID_PARAMETER : Some parameters are invalid. + @return EFI_TIMEOUT : Failed due to timeout. + @return EFI_DEVICE_ERROR : Failed due to host controller or device error. + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2SyncInterruptTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + USB_HC_DEV *Uhc; + BOOLEAN IsSlow; + + if (DeviceSpeed == EFI_USB_SPEED_HIGH) { + return EFI_INVALID_PARAMETER; + } + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + IsSlow = (EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE; + + return UhciSyncInterruptTransfer ( + &Uhc->UsbHc, + DeviceAddress, + EndPointAddress, + IsSlow, + (UINT8) MaximumPacketLength, + Data, + DataLength, + DataToggle, + TimeOut, + TransferResult + ); +} + + +/** + Submits isochronous transfer to a target USB device according to UEFI 2.0 spec. + + This : A pointer to the EFI_USB2_HC_PROTOCOL instance. + DeviceAddress : Target device address + EndPointAddress : Endpoint number and direction + DeviceSpeed : Device speed + MaximumPacketLength : Maximum packet size of the target endpoint + DataBuffersNumber : Number of data buffers prepared for the transfer. + Data : Array of pointers to the buffers of data + DataLength : On input, size of the data buffer, On output, + actually transferred data size. + Translator : A pointr to the transaction translator data. + TransferResult : Variable to receive transfer result + + @return EFI_UNSUPPORTED + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2IsochronousTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN UINT8 DataBuffersNumber, + IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Submits Async isochronous transfer to a target USB device according to UEFI 2.0 spec. + + This : A pointer to the EFI_USB2_HC_PROTOCOL instance. + DeviceAddress : Target device address + EndPointAddress : Endpoint number and direction + DeviceSpeed : Device speed + MaximumPacketLength : Maximum packet size of the target endpoint + DataBuffersNumber : Number of data buffers prepared for the transfer. + Data : Array of pointers to the buffers of data + Translator : A pointr to the transaction translator data. + IsochronousCallBack : Function to call when the transfer complete + Context : Pass to the call back function as parameter + + @return EFI_UNSUPPORTED + +**/ +STATIC +EFI_STATUS +EFIAPI +Uhci2AsyncIsochronousTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN UINT8 DataBuffersNumber, + IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack, + IN VOID *Context + ) +{ + return EFI_UNSUPPORTED; +} + +//@MT: EFI_DRIVER_ENTRY_POINT (UhciDriverEntryPoint) + +EFI_STATUS +EFIAPI +UhciDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + + Routine Description: + + Entry point for EFI drivers. + + Arguments: + + ImageHandle - EFI_HANDLE + SystemTable - EFI_SYSTEM_TABLE + + Returns: + + EFI_SUCCESS : Driver is successfully loaded + Others : Failed + +--*/ +{ + return EfiLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &gUhciDriverBinding, + ImageHandle, + &gUhciComponentName, + NULL, + NULL + ); +} + + +/** + Test to see if this driver supports ControllerHandle. Any + ControllerHandle that has UsbHcProtocol installed will be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test + @param RemainingDevicePath Not used + + @return EFI_SUCCESS : This driver supports this device. + @return EFI_UNSUPPORTED : This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +UhciDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS OpenStatus; + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + USB_CLASSC UsbClassCReg; + + // + // Test whether there is PCI IO Protocol attached on the controller handle. + // + OpenStatus = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (OpenStatus)) { + return OpenStatus; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + CLASSC_OFFSET, + sizeof (USB_CLASSC) / sizeof (UINT8), + &UsbClassCReg + ); + + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + + // + // Test whether the controller belongs to UHCI type + // + if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || + (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) || + (UsbClassCReg.PI != PCI_CLASSC_PI_UHCI) + ) { + + Status = EFI_UNSUPPORTED; + } + +ON_EXIT: + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; + +} + + +/** + Allocate and initialize the empty UHCI device + + @param PciIo The PCIIO to use + + @return Allocated UHCI device + +**/ +STATIC +USB_HC_DEV * +UhciAllocateDev ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + USB_HC_DEV *Uhc; + EFI_STATUS Status; + + Uhc = AllocateZeroPool (sizeof (USB_HC_DEV)); + + if (Uhc == NULL) { + return NULL; + } + + // + // This driver supports both USB_HC_PROTOCOL and USB2_HC_PROTOCOL. + // USB_HC_PROTOCOL is for EFI 1.1 backward compability. + // + Uhc->Signature = USB_HC_DEV_SIGNATURE; + Uhc->UsbHc.Reset = UhciReset; + Uhc->UsbHc.GetState = UhciGetState; + Uhc->UsbHc.SetState = UhciSetState; + Uhc->UsbHc.ControlTransfer = UhciControlTransfer; + Uhc->UsbHc.BulkTransfer = UhciBulkTransfer; + Uhc->UsbHc.AsyncInterruptTransfer = UhciAsyncInterruptTransfer; + Uhc->UsbHc.SyncInterruptTransfer = UhciSyncInterruptTransfer; + Uhc->UsbHc.IsochronousTransfer = UhciIsochronousTransfer; + Uhc->UsbHc.AsyncIsochronousTransfer = UhciAsyncIsochronousTransfer; + Uhc->UsbHc.GetRootHubPortNumber = UhciGetRootHubPortNumber; + Uhc->UsbHc.GetRootHubPortStatus = UhciGetRootHubPortStatus; + Uhc->UsbHc.SetRootHubPortFeature = UhciSetRootHubPortFeature; + Uhc->UsbHc.ClearRootHubPortFeature = UhciClearRootHubPortFeature; + Uhc->UsbHc.MajorRevision = 0x1; + Uhc->UsbHc.MinorRevision = 0x1; + + Uhc->Usb2Hc.GetCapability = Uhci2GetCapability; + Uhc->Usb2Hc.Reset = Uhci2Reset; + Uhc->Usb2Hc.GetState = Uhci2GetState; + Uhc->Usb2Hc.SetState = Uhci2SetState; + Uhc->Usb2Hc.ControlTransfer = Uhci2ControlTransfer; + Uhc->Usb2Hc.BulkTransfer = Uhci2BulkTransfer; + Uhc->Usb2Hc.AsyncInterruptTransfer = Uhci2AsyncInterruptTransfer; + Uhc->Usb2Hc.SyncInterruptTransfer = Uhci2SyncInterruptTransfer; + Uhc->Usb2Hc.IsochronousTransfer = Uhci2IsochronousTransfer; + Uhc->Usb2Hc.AsyncIsochronousTransfer = Uhci2AsyncIsochronousTransfer; + Uhc->Usb2Hc.GetRootHubPortStatus = Uhci2GetRootHubPortStatus; + Uhc->Usb2Hc.SetRootHubPortFeature = Uhci2SetRootHubPortFeature; + Uhc->Usb2Hc.ClearRootHubPortFeature = Uhci2ClearRootHubPortFeature; + Uhc->Usb2Hc.MajorRevision = 0x1; + Uhc->Usb2Hc.MinorRevision = 0x1; + + Uhc->PciIo = PciIo; + Uhc->MemPool = UsbHcInitMemPool (PciIo, TRUE, 0); + + if (Uhc->MemPool == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + InitializeListHead (&Uhc->AsyncIntList); + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + UhciMonitorAsyncReqList, + Uhc, + &Uhc->AsyncIntMonitor + ); + + if (EFI_ERROR (Status)) { + UsbHcFreeMemPool (Uhc->MemPool); + goto ON_ERROR; + } + + return Uhc; + +ON_ERROR: + gBS->FreePool (Uhc); + return NULL; +} + + +/** + Free the UHCI device and release its associated resources + + @param Uhc The UHCI device to release + + @return None + +**/ +STATIC +VOID +UhciFreeDev ( + IN USB_HC_DEV *Uhc + ) +{ + if (Uhc->AsyncIntMonitor != NULL) { + gBS->CloseEvent (Uhc->AsyncIntMonitor); + } + + if (Uhc->MemPool != NULL) { + UsbHcFreeMemPool (Uhc->MemPool); + } + + if (Uhc->CtrlNameTable) { + FreeUnicodeStringTable (Uhc->CtrlNameTable); + } + + gBS->FreePool (Uhc); +} + + +/** + Uninstall all Uhci Interface + + @param Controller Controller handle + @param This Protocol instance pointer. + + @return VOID + +**/ +STATIC +VOID +UhciCleanDevUp ( + IN EFI_HANDLE Controller, + IN EFI_USB_HC_PROTOCOL *This + ) +{ + USB_HC_DEV *Uhc; + + // + // Uninstall the USB_HC and USB_HC2 protocol, then disable the controller + // + Uhc = UHC_FROM_USB_HC_PROTO (This); + UhciStopHc (Uhc, STALL_1_SECOND); + + gBS->UninstallProtocolInterface ( + Controller, + &gEfiUsbHcProtocolGuid, + &Uhc->UsbHc + ); + + gBS->UninstallProtocolInterface ( + Controller, + &gEfiUsb2HcProtocolGuid, + &Uhc->Usb2Hc + ); + + UhciFreeAllAsyncReq (Uhc); + UhciDestoryFrameList (Uhc); + + Uhc->PciIo->Attributes ( + Uhc->PciIo, + EfiPciIoAttributeOperationDisable, + EFI_PCI_DEVICE_ENABLE, + NULL + ); + + UhciFreeDev (Uhc); +} + + +/** + Starting the Usb UHCI Driver + + @param This Protocol instance pointer. + @param Controller Handle of device to test + @param RemainingDevicePath Not used + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + @retval EFI_DEVICE_ERROR This driver cannot be started due to device Error + EFI_OUT_OF_RESOURCES- Failed due to resource + shortage + +**/ +EFI_STATUS +EFIAPI +UhciDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + USB_HC_DEV *Uhc; + + // + // Open PCIIO, then enable the EHC device and turn off emulation + // + Uhc = NULL; + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + UhciTurnOffUsbEmulation (PciIo); + + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_DEVICE_ENABLE, + NULL + ); + + if (EFI_ERROR (Status)) { + goto CLOSE_PCIIO; + } + + Uhc = UhciAllocateDev (PciIo); + + if (Uhc == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto CLOSE_PCIIO; + } + + // + // Allocate and Init Host Controller's Frame List Entry + // + Status = UhciInitFrameList (Uhc); + + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + goto FREE_UHC; + } + + Status = gBS->SetTimer ( + Uhc->AsyncIntMonitor, + TimerPeriodic, + INTERRUPT_POLLING_TIME + ); + + if (EFI_ERROR (Status)) { + goto FREE_UHC; + } + + // + // Install both USB_HC_PROTOCOL and USB2_HC_PROTOCOL + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiUsbHcProtocolGuid, + &Uhc->UsbHc, + &gEfiUsb2HcProtocolGuid, + &Uhc->Usb2Hc, + NULL + ); + + if (EFI_ERROR (Status)) { + goto FREE_UHC; + } + + // + // Install the component name protocol + // + Uhc->CtrlNameTable = NULL; + + AddUnicodeString ( + "eng", + gUhciComponentName.SupportedLanguages, + &Uhc->CtrlNameTable, + L"Usb Universal Host Controller" + ); + + // + // Start the UHCI hardware, also set its reclamation point to 64 bytes + // + UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, USBCMD_RS | USBCMD_MAXP); + + return EFI_SUCCESS; + +FREE_UHC: + UhciFreeDev (Uhc); + +CLOSE_PCIIO: + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Stop this driver on ControllerHandle. Support stoping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on + @param NumberOfChildren Number of Children in the ChildHandleBuffer + @param ChildHandleBuffer List of handles for the children we need to stop. + + @return EFI_SUCCESS + @return others + +**/ +EFI_STATUS +EFIAPI +UhciDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_USB_HC_PROTOCOL *UsbHc; + EFI_USB2_HC_PROTOCOL *Usb2Hc; + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbHcProtocolGuid, + &UsbHc, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + // + // Test whether the Controller handler passed in is a valid + // Usb controller handle that should be supported, if not, + // return the error status directly + // + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsb2HcProtocolGuid, + &Usb2Hc, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + // + // Test whether the Controller handler passed in is a valid + // Usb controller handle that should be supported, if not, + // return the error status directly + // + if (EFI_ERROR (Status)) { + return Status; + } + + UhciCleanDevUp (Controller, UsbHc); + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; +} + +EFI_DRIVER_BINDING_PROTOCOL gUhciDriverBinding = { + UhciDriverBindingSupported, + UhciDriverBindingStart, + UhciDriverBindingStop, + 0x20, + NULL, + NULL +}; -- cgit v1.2.3