/** @file The UHCI driver model and HC protocol routines. Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #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 ((DEBUG_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 ((DEBUG_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); if (!EFI_ERROR (Status)) { *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); if (!EFI_ERROR (Status)) { *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); if (!EFI_ERROR (Status)) { *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; }