| /** @file | |
| The UHCI driver model and HC protocol routines. | |
| Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "Uhci.h" | |
| EFI_DRIVER_BINDING_PROTOCOL gUhciDriverBinding = { | |
| UhciDriverBindingSupported, | |
| UhciDriverBindingStart, | |
| UhciDriverBindingStop, | |
| 0x20, | |
| NULL, | |
| NULL | |
| }; | |
| /** | |
| 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. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| Uhci2Reset ( | |
| IN EFI_USB2_HC_PROTOCOL *This, | |
| IN UINT16 Attributes | |
| ) | |
| { | |
| USB_HC_DEV *Uhc; | |
| EFI_TPL OldTpl; | |
| if ((Attributes == EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG) || | |
| (Attributes == EFI_USB_HC_RESET_HOST_WITH_DEBUG)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
| if (Uhc->DevicePath != NULL) { | |
| // | |
| // Report Status Code to indicate reset happens | |
| // | |
| REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
| EFI_PROGRESS_CODE, | |
| (EFI_IO_BUS_USB | EFI_IOB_PC_RESET), | |
| Uhc->DevicePath | |
| ); | |
| } | |
| OldTpl = gBS->RaiseTPL (UHCI_TPL); | |
| switch (Attributes) { | |
| case EFI_USB_HC_RESET_GLOBAL: | |
| // | |
| // Stop schedule and set the Global Reset bit in the command register | |
| // | |
| UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); | |
| UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET); | |
| gBS->Stall (UHC_ROOT_PORT_RESET_STALL); | |
| // | |
| // Clear the Global Reset bit to zero. | |
| // | |
| UhciClearRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET); | |
| gBS->Stall (UHC_ROOT_PORT_RECOVERY_STALL); | |
| break; | |
| case EFI_USB_HC_RESET_HOST_CONTROLLER: | |
| // | |
| // Stop schedule and set Host Controller Reset bit to 1 | |
| // | |
| UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); | |
| UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_HCRESET); | |
| gBS->Stall (UHC_ROOT_PORT_RECOVERY_STALL); | |
| 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 according to UEFI 2.0 spec. | |
| @param This A pointer to the EFI_USB2_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 errors. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| Uhci2GetState ( | |
| IN EFI_USB2_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_USB2_HC_PROTO (This); | |
| UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); | |
| UsbSts = UhciReadReg (Uhc->PciIo, USBSTS_OFFSET); | |
| if ((UsbCmd & USBCMD_EGSM) !=0 ) { | |
| *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 according to UEFI 2.0 spec. | |
| @param This A pointer to the EFI_USB2_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. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| Uhci2SetState ( | |
| IN EFI_USB2_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_USB2_HC_PROTO (This); | |
| Status = Uhci2GetState (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, UHC_GENERIC_TIMEOUT); | |
| 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 (UHC_FORCE_GLOBAL_RESUME_STALL); | |
| // | |
| // 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 = Uhci2SetState (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 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. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| Uhci2GetCapability ( | |
| IN EFI_USB2_HC_PROTOCOL *This, | |
| OUT UINT8 *MaxSpeed, | |
| OUT UINT8 *PortNumber, | |
| OUT UINT8 *Is64BitCapable | |
| ) | |
| { | |
| USB_HC_DEV *Uhc; | |
| UINT32 Offset; | |
| UINT16 PortSC; | |
| UINT32 Index; | |
| 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; | |
| *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)) { | |
| break; | |
| } | |
| (*PortNumber)++; | |
| } | |
| Uhc->RootPorts = *PortNumber; | |
| DEBUG ((EFI_D_INFO, "Uhci2GetCapability: %d ports\n", (UINT32)Uhc->RootPorts)); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| 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. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| Uhci2GetRootHubPortStatus ( | |
| IN EFI_USB2_HC_PROTOCOL *This, | |
| IN UINT8 PortNumber, | |
| OUT EFI_USB_PORT_STATUS *PortStatus | |
| ) | |
| { | |
| USB_HC_DEV *Uhc; | |
| UINT32 Offset; | |
| UINT16 PortSC; | |
| Uhc = UHC_FROM_USB2_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) != 0) { | |
| PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION; | |
| } | |
| if ((PortSC & USBPORTSC_PED) != 0) { | |
| PortStatus->PortStatus |= USB_PORT_STAT_ENABLE; | |
| } | |
| if ((PortSC & USBPORTSC_SUSP) != 0) { | |
| DEBUG ((EFI_D_INFO, "Uhci2GetRootHubPortStatus: port %d is suspended\n", PortNumber)); | |
| PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND; | |
| } | |
| if ((PortSC & USBPORTSC_PR) != 0) { | |
| PortStatus->PortStatus |= USB_PORT_STAT_RESET; | |
| } | |
| if ((PortSC & USBPORTSC_LSDA) != 0) { | |
| 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) != 0) { | |
| PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION; | |
| } | |
| if ((PortSC & USBPORTSC_PEDC) != 0) { | |
| PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| 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. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| Uhci2SetRootHubPortFeature ( | |
| IN EFI_USB2_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_USB2_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) == 0) { | |
| // | |
| // 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 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. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| Uhci2ClearRootHubPortFeature ( | |
| IN EFI_USB2_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_USB2_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 according to UEFI 2.0 spec. | |
| @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
| @param DeviceAddress Target device address. | |
| @param DeviceSpeed Device speed. | |
| @param MaximumPacketLength Maximum packet size of the target endpoint. | |
| @param Request USB device request to send. | |
| @param TransferDirection Data direction of the Data stage in control transfer. | |
| @param Data Data to transmit/receive in data stage. | |
| @param DataLength Length of the data. | |
| @param TimeOut Maximum time, in microseconds, for transfer to complete. | |
| @param Translator Transaction translator to be used by this device. | |
| @param 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. | |
| **/ | |
| 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; | |
| UHCI_TD_SW *TDs; | |
| EFI_TPL OldTpl; | |
| EFI_STATUS Status; | |
| UHCI_QH_RESULT QhResult; | |
| UINT8 PktId; | |
| UINT8 *RequestPhy; | |
| VOID *RequestMap; | |
| UINT8 *DataPhy; | |
| VOID *DataMap; | |
| BOOLEAN IsSlowDevice; | |
| UINTN TransferDataLength; | |
| Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
| TDs = NULL; | |
| DataPhy = NULL; | |
| DataMap = NULL; | |
| RequestPhy = NULL; | |
| RequestMap = NULL; | |
| IsSlowDevice = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE); | |
| // | |
| // 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) && (Data == NULL || DataLength == NULL)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (TransferDirection == EfiUsbNoData) { | |
| TransferDataLength = 0; | |
| } else { | |
| TransferDataLength = *DataLength; | |
| } | |
| *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, | |
| (UINT8*)Request, | |
| RequestPhy, | |
| (UINT8*)Data, | |
| DataPhy, | |
| TransferDataLength, | |
| (UINT8) 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, 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. | |
| @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
| @param DeviceAddress Target device address. | |
| @param EndPointAddress Endpoint number and direction. | |
| @param DeviceSpeed Device speed. | |
| @param MaximumPacketLength Maximum packet size of the target endpoint. | |
| @param DataBuffersNumber Number of data buffers prepared for the transfer. | |
| @param Data Array of pointers to the buffers of data. | |
| @param DataLength On input, size of the data buffer, On output, | |
| actually transferred data size. | |
| @param DataToggle On input, data toggle to use; On output, next data toggle. | |
| @param TimeOut Maximum time out, in microseconds. | |
| @param Translator A pointr to the transaction translator data. | |
| @param 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. | |
| **/ | |
| 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 | |
| ) | |
| { | |
| 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_USB2_HC_PROTO (This); | |
| DataPhy = NULL; | |
| DataMap = NULL; | |
| if (DeviceSpeed == EFI_USB_SPEED_LOW) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if ((DataLength == NULL) || (*DataLength == 0) || (Data == NULL) || (TransferResult == NULL)) { | |
| 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) != 0) { | |
| 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, | |
| (UINT8 *)*Data, | |
| DataPhy, | |
| *DataLength, | |
| DataToggle, | |
| (UINT8) 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 (Uhc, 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 according to UEFI 2.0 spec. | |
| @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
| @param DeviceAddress Target device address. | |
| @param EndPointAddress Endpoint number and direction. | |
| @param DeviceSpeed Device speed. | |
| @param MaximumPacketLength Maximum packet size of the target endpoint. | |
| @param IsNewTransfer If TRUE, submit a new transfer, if FALSE cancel old transfer. | |
| @param DataToggle On input, data toggle to use; On output, next data toggle. | |
| @param PollingInterval Interrupt poll rate in milliseconds. | |
| @param DataLength On input, size of the data buffer, On output, | |
| actually transferred data size. | |
| @param Translator A pointr to the transaction translator data. | |
| @param CallBackFunction Function to call periodically. | |
| @param 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. | |
| **/ | |
| 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 IsSlowDevice; | |
| UHCI_QH_SW *Qh; | |
| UHCI_TD_SW *IntTds; | |
| EFI_TPL OldTpl; | |
| EFI_STATUS Status; | |
| UINT8 *DataPtr; | |
| UINT8 *DataPhy; | |
| UINT8 PktId; | |
| Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
| Qh = NULL; | |
| IntTds = NULL; | |
| DataPtr = NULL; | |
| DataPhy = NULL; | |
| IsSlowDevice = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE); | |
| 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; | |
| } | |
| if ((EndPointAddress & 0x80) == 0) { | |
| PktId = OUTPUT_PACKET_ID; | |
| } else { | |
| PktId = INPUT_PACKET_ID; | |
| } | |
| // | |
| // Allocate and map source data buffer for bus master access. | |
| // | |
| DataPtr = UsbHcAllocateMem (Uhc->MemPool, DataLength); | |
| if (DataPtr == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| DataPhy = (UINT8 *) (UINTN) UsbHcGetPciAddressForHostMem (Uhc->MemPool, DataPtr, DataLength); | |
| OldTpl = gBS->RaiseTPL (UHCI_TPL); | |
| Qh = UhciCreateQh (Uhc, PollingInterval); | |
| if (Qh == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto FREE_DATA; | |
| } | |
| IntTds = UhciCreateBulkOrIntTds ( | |
| Uhc, | |
| DeviceAddress, | |
| EndPointAddress, | |
| PktId, | |
| DataPtr, | |
| DataPhy, | |
| DataLength, | |
| DataToggle, | |
| (UINT8) MaximumPacketLength, | |
| IsSlowDevice | |
| ); | |
| if (IntTds == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto DESTORY_QH; | |
| } | |
| UhciLinkTdToQh (Uhc, 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, | |
| DataPtr, | |
| CallBackFunction, | |
| Context, | |
| IsSlowDevice | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto DESTORY_QH; | |
| } | |
| UhciLinkQhToFrameList (Uhc, Qh); | |
| gBS->RestoreTPL (OldTpl); | |
| return EFI_SUCCESS; | |
| DESTORY_QH: | |
| UsbHcFreeMem (Uhc->MemPool, Qh, sizeof (UHCI_QH_SW)); | |
| FREE_DATA: | |
| UsbHcFreeMem (Uhc->MemPool, DataPtr, DataLength); | |
| Uhc->PciIo->Flush (Uhc->PciIo); | |
| gBS->RestoreTPL (OldTpl); | |
| return Status; | |
| } | |
| /** | |
| Submits synchronous interrupt transfer to an interrupt endpoint | |
| of a USB device according to UEFI 2.0 spec. | |
| @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
| @param DeviceAddress Target device address. | |
| @param EndPointAddress Endpoint number and direction. | |
| @param DeviceSpeed Device speed. | |
| @param MaximumPacketLength Maximum packet size of the target endpoint. | |
| @param Data Array of pointers to the buffers of data. | |
| @param DataLength On input, size of the data buffer, On output, | |
| actually transferred data size. | |
| @param DataToggle On input, data toggle to use; On output, next data toggle. | |
| @param TimeOut Maximum time out, in microseconds. | |
| @param Translator A pointr to the transaction translator data. | |
| @param 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. | |
| **/ | |
| 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 | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| USB_HC_DEV *Uhc; | |
| UHCI_TD_SW *TDs; | |
| UHCI_QH_RESULT QhResult; | |
| EFI_TPL OldTpl; | |
| UINT8 *DataPhy; | |
| VOID *DataMap; | |
| UINT8 PktId; | |
| BOOLEAN IsSlowDevice; | |
| Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
| DataPhy = NULL; | |
| DataMap = NULL; | |
| TDs = NULL; | |
| if (DeviceSpeed == EFI_USB_SPEED_HIGH) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| IsSlowDevice = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE); | |
| if ((DataLength == NULL) || (Data == NULL) || (TransferResult == NULL)) { | |
| 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, | |
| (UINT8 *)Data, | |
| DataPhy, | |
| *DataLength, | |
| DataToggle, | |
| (UINT8) MaximumPacketLength, | |
| IsSlowDevice | |
| ); | |
| if (TDs == NULL) { | |
| Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ON_EXIT; | |
| } | |
| UhciLinkTdToQh (Uhc, 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 according to UEFI 2.0 spec. | |
| @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
| @param DeviceAddress Target device address. | |
| @param EndPointAddress Endpoint number and direction. | |
| @param DeviceSpeed Device speed. | |
| @param MaximumPacketLength Maximum packet size of the target endpoint. | |
| @param DataBuffersNumber Number of data buffers prepared for the transfer. | |
| @param Data Array of pointers to the buffers of data. | |
| @param DataLength On input, size of the data buffer, On output, | |
| actually transferred data size. | |
| @param Translator A pointr to the transaction translator data. | |
| @param TransferResult Variable to receive transfer result. | |
| @return EFI_UNSUPPORTED | |
| **/ | |
| 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. | |
| @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. | |
| @param DeviceAddress Target device address. | |
| @param EndPointAddress Endpoint number and direction. | |
| @param DeviceSpeed Device speed. | |
| @param MaximumPacketLength Maximum packet size of the target endpoint. | |
| @param DataBuffersNumber Number of data buffers prepared for the transfer. | |
| @param Data Array of pointers to the buffers of data. | |
| @param DataLength On input, size of the data buffer, On output, | |
| actually transferred data size. | |
| @param Translator A pointr to the transaction translator data. | |
| @param IsochronousCallBack Function to call when the transfer complete. | |
| @param Context Pass to the call back function as parameter. | |
| @return EFI_UNSUPPORTED | |
| **/ | |
| 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; | |
| } | |
| /** | |
| Entry point for EFI drivers. | |
| @param ImageHandle EFI_HANDLE. | |
| @param SystemTable EFI_SYSTEM_TABLE. | |
| @retval EFI_SUCCESS Driver is successfully loaded. | |
| @return Others Failed. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| UhciDriverEntryPoint ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| return EfiLibInstallDriverBindingComponentName2 ( | |
| ImageHandle, | |
| SystemTable, | |
| &gUhciDriverBinding, | |
| ImageHandle, | |
| &gUhciComponentName, | |
| &gUhciComponentName2 | |
| ); | |
| } | |
| /** | |
| 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, | |
| (VOID **) &PciIo, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (OpenStatus)) { | |
| return OpenStatus; | |
| } | |
| Status = PciIo->Pci.Read ( | |
| PciIo, | |
| EfiPciIoWidthUint8, | |
| PCI_CLASSCODE_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.ProgInterface != PCI_IF_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. | |
| @param DevicePath The device path of host controller. | |
| @param OriginalPciAttributes The original PCI attributes. | |
| @return Allocated UHCI device. If err, return NULL. | |
| **/ | |
| USB_HC_DEV * | |
| UhciAllocateDev ( | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
| IN UINT64 OriginalPciAttributes | |
| ) | |
| { | |
| 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->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->DevicePath = DevicePath; | |
| Uhc->OriginalPciAttributes = OriginalPciAttributes; | |
| 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_NOTIFY, | |
| UhciMonitorAsyncReqList, | |
| Uhc, | |
| &Uhc->AsyncIntMonitor | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| UsbHcFreeMemPool (Uhc->MemPool); | |
| goto ON_ERROR; | |
| } | |
| return Uhc; | |
| ON_ERROR: | |
| FreePool (Uhc); | |
| return NULL; | |
| } | |
| /** | |
| Free the UHCI device and release its associated resources. | |
| @param Uhc The UHCI device to release. | |
| **/ | |
| VOID | |
| UhciFreeDev ( | |
| IN USB_HC_DEV *Uhc | |
| ) | |
| { | |
| if (Uhc->AsyncIntMonitor != NULL) { | |
| gBS->CloseEvent (Uhc->AsyncIntMonitor); | |
| } | |
| if (Uhc->ExitBootServiceEvent != NULL) { | |
| gBS->CloseEvent (Uhc->ExitBootServiceEvent); | |
| } | |
| if (Uhc->MemPool != NULL) { | |
| UsbHcFreeMemPool (Uhc->MemPool); | |
| } | |
| if (Uhc->CtrlNameTable != NULL) { | |
| FreeUnicodeStringTable (Uhc->CtrlNameTable); | |
| } | |
| FreePool (Uhc); | |
| } | |
| /** | |
| Uninstall all Uhci Interface. | |
| @param Controller Controller handle. | |
| @param This Protocol instance pointer. | |
| **/ | |
| VOID | |
| UhciCleanDevUp ( | |
| IN EFI_HANDLE Controller, | |
| IN EFI_USB2_HC_PROTOCOL *This | |
| ) | |
| { | |
| USB_HC_DEV *Uhc; | |
| EFI_STATUS Status; | |
| // | |
| // Uninstall the USB_HC and USB_HC2 protocol, then disable the controller | |
| // | |
| Uhc = UHC_FROM_USB2_HC_PROTO (This); | |
| Status = gBS->UninstallProtocolInterface ( | |
| Controller, | |
| &gEfiUsb2HcProtocolGuid, | |
| &Uhc->Usb2Hc | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return ; | |
| } | |
| UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); | |
| UhciFreeAllAsyncReq (Uhc); | |
| UhciDestoryFrameList (Uhc); | |
| // | |
| // Restore original PCI attributes | |
| // | |
| Uhc->PciIo->Attributes ( | |
| Uhc->PciIo, | |
| EfiPciIoAttributeOperationSet, | |
| Uhc->OriginalPciAttributes, | |
| NULL | |
| ); | |
| UhciFreeDev (Uhc); | |
| } | |
| /** | |
| One notified function to stop the Host Controller when gBS->ExitBootServices() called. | |
| @param Event Pointer to this event | |
| @param Context Event handler private data | |
| **/ | |
| VOID | |
| EFIAPI | |
| UhcExitBootService ( | |
| EFI_EVENT Event, | |
| VOID *Context | |
| ) | |
| { | |
| USB_HC_DEV *Uhc; | |
| Uhc = (USB_HC_DEV *) Context; | |
| // | |
| // Stop the Host Controller | |
| // | |
| UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); | |
| // | |
| // Reset the Host Controller | |
| // | |
| UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_HCRESET); | |
| gBS->Stall (UHC_ROOT_PORT_RECOVERY_STALL); | |
| } | |
| /** | |
| 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; | |
| UINT64 Supports; | |
| UINT64 OriginalPciAttributes; | |
| BOOLEAN PciAttributesSaved; | |
| EFI_DEVICE_PATH_PROTOCOL *HcDevicePath; | |
| // | |
| // Open PCIIO, then enable the EHC device and turn off emulation | |
| // | |
| Uhc = NULL; | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiPciIoProtocolGuid, | |
| (VOID **) &PciIo, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Open Device Path Protocol for on USB host controller | |
| // | |
| HcDevicePath = NULL; | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID **) &HcDevicePath, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| PciAttributesSaved = FALSE; | |
| // | |
| // Save original PCI attributes | |
| // | |
| Status = PciIo->Attributes ( | |
| PciIo, | |
| EfiPciIoAttributeOperationGet, | |
| 0, | |
| &OriginalPciAttributes | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto CLOSE_PCIIO; | |
| } | |
| PciAttributesSaved = TRUE; | |
| // | |
| // Robustnesss improvement such as for UoL | |
| // Default is not required. | |
| // | |
| if (FeaturePcdGet (PcdTurnOffUsbLegacySupport)) { | |
| UhciTurnOffUsbEmulation (PciIo); | |
| } | |
| Status = PciIo->Attributes ( | |
| PciIo, | |
| EfiPciIoAttributeOperationSupported, | |
| 0, | |
| &Supports | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE; | |
| Status = PciIo->Attributes ( | |
| PciIo, | |
| EfiPciIoAttributeOperationEnable, | |
| Supports, | |
| NULL | |
| ); | |
| } | |
| if (EFI_ERROR (Status)) { | |
| goto CLOSE_PCIIO; | |
| } | |
| Uhc = UhciAllocateDev (PciIo, HcDevicePath, OriginalPciAttributes); | |
| 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, | |
| UHC_ASYNC_POLL_INTERVAL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto FREE_UHC; | |
| } | |
| // | |
| // Install USB2_HC_PROTOCOL | |
| // | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &Controller, | |
| &gEfiUsb2HcProtocolGuid, | |
| &Uhc->Usb2Hc, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto FREE_UHC; | |
| } | |
| // | |
| // Create event to stop the HC when exit boot service. | |
| // | |
| Status = gBS->CreateEventEx ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_NOTIFY, | |
| UhcExitBootService, | |
| Uhc, | |
| &gEfiEventExitBootServicesGuid, | |
| &Uhc->ExitBootServiceEvent | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto UNINSTALL_USBHC; | |
| } | |
| // | |
| // Install the component name protocol | |
| // | |
| Uhc->CtrlNameTable = NULL; | |
| AddUnicodeString2 ( | |
| "eng", | |
| gUhciComponentName.SupportedLanguages, | |
| &Uhc->CtrlNameTable, | |
| L"Usb Universal Host Controller", | |
| TRUE | |
| ); | |
| AddUnicodeString2 ( | |
| "en", | |
| gUhciComponentName2.SupportedLanguages, | |
| &Uhc->CtrlNameTable, | |
| L"Usb Universal Host Controller", | |
| FALSE | |
| ); | |
| // | |
| // Start the UHCI hardware, also set its reclamation point to 64 bytes | |
| // | |
| UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, USBCMD_RS | USBCMD_MAXP); | |
| return EFI_SUCCESS; | |
| UNINSTALL_USBHC: | |
| gBS->UninstallMultipleProtocolInterfaces ( | |
| Controller, | |
| &gEfiUsb2HcProtocolGuid, | |
| &Uhc->Usb2Hc, | |
| NULL | |
| ); | |
| FREE_UHC: | |
| UhciFreeDev (Uhc); | |
| CLOSE_PCIIO: | |
| if (PciAttributesSaved) { | |
| // | |
| // Restore original PCI attributes | |
| // | |
| PciIo->Attributes ( | |
| PciIo, | |
| EfiPciIoAttributeOperationSet, | |
| OriginalPciAttributes, | |
| NULL | |
| ); | |
| } | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiPciIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| Controller | |
| ); | |
| return Status; | |
| } | |
| /** | |
| Stop this driver on ControllerHandle. Support stopping 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_USB2_HC_PROTOCOL *Usb2Hc; | |
| EFI_STATUS Status; | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiUsb2HcProtocolGuid, | |
| (VOID **) &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, Usb2Hc); | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiPciIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| Controller | |
| ); | |
| return EFI_SUCCESS; | |
| } | |