Fixes CodeQL alerts for CWE-457: https://cwe.mitre.org/data/definitions/457.html Cc: Dandan Bi <dandan.bi@intel.com> Cc: Eric Dong <eric.dong@intel.com> Cc: Erich McMillan <emcmillan@microsoft.com> Cc: Guomin Jiang <guomin.jiang@intel.com> Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Liming Gao <gaoliming@byosoft.com.cn> Cc: Michael Kubacki <mikuback@linux.microsoft.com> Cc: Ray Ni <ray.ni@intel.com> Cc: Zhichao Gao <zhichao.gao@intel.com> Co-authored-by: Erich McMillan <emcmillan@microsoft.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn> Reviewed-by: Oliver Smith-Denny <osd@smith-denny.com>
		
			
				
	
	
		
			1869 lines
		
	
	
		
			52 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1869 lines
		
	
	
		
			52 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|   The UHCI driver model and HC protocol routines.
 | |
| 
 | |
| Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
| 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;
 | |
| }
 |