EDKII usb stack is using a TPL_CALLBACK timer to monitor async transfer request and signal event if it's done. As usb enumeration and usb mass storage block i/o read/write runs on TPL_CALLBACK and TPL_NOTIFY level respectively, It blocks usb async transfer requests, usually usb mouse /use kb, getting time to run. Without this change, user couldn't get usb mouse/kb state in time (will show a little lag from UI view) when there is other usb transactions, such as a new usb device inserted. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Feng Tian <feng.tian@intel.com> Reviewed-by: Star Zeng <star.zeng@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18169 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			2107 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2107 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file  
 | |
|   The Ehci controller driver.
 | |
| 
 | |
|   EhciDxe driver is responsible for managing the behavior of EHCI controller. 
 | |
|   It implements the interfaces of monitoring the status of all ports and transferring 
 | |
|   Control, Bulk, Interrupt and Isochronous requests to Usb2.0 device.
 | |
| 
 | |
|   Note that EhciDxe driver is enhanced to guarantee that the EHCI controller get attached
 | |
|   to the EHCI controller before a UHCI or OHCI driver attaches to the companion UHCI or 
 | |
|   OHCI controller.  This way avoids the control transfer on a shared port between EHCI 
 | |
|   and companion host controller when UHCI or OHCI gets attached earlier than EHCI and a 
 | |
|   USB 2.0 device inserts.
 | |
| 
 | |
| Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
 | |
| This program and the accompanying materials
 | |
| are licensed and made available under the terms and conditions of the BSD License
 | |
| which accompanies this distribution.  The full text of the license may be found at
 | |
| http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| 
 | |
| #include "Ehci.h"
 | |
| 
 | |
| //
 | |
| // Two arrays used to translate the EHCI port state (change)
 | |
| // to the UEFI protocol's port state (change).
 | |
| //
 | |
| USB_PORT_STATE_MAP  mUsbPortStateMap[] = {
 | |
|   {PORTSC_CONN,     USB_PORT_STAT_CONNECTION},
 | |
|   {PORTSC_ENABLED,  USB_PORT_STAT_ENABLE},
 | |
|   {PORTSC_SUSPEND,  USB_PORT_STAT_SUSPEND},
 | |
|   {PORTSC_OVERCUR,  USB_PORT_STAT_OVERCURRENT},
 | |
|   {PORTSC_RESET,    USB_PORT_STAT_RESET},
 | |
|   {PORTSC_POWER,    USB_PORT_STAT_POWER},
 | |
|   {PORTSC_OWNER,    USB_PORT_STAT_OWNER}
 | |
| };
 | |
| 
 | |
| USB_PORT_STATE_MAP  mUsbPortChangeMap[] = {
 | |
|   {PORTSC_CONN_CHANGE,    USB_PORT_STAT_C_CONNECTION},
 | |
|   {PORTSC_ENABLE_CHANGE,  USB_PORT_STAT_C_ENABLE},
 | |
|   {PORTSC_OVERCUR_CHANGE, USB_PORT_STAT_C_OVERCURRENT}
 | |
| };
 | |
| 
 | |
| EFI_DRIVER_BINDING_PROTOCOL
 | |
| gEhciDriverBinding = {
 | |
|   EhcDriverBindingSupported,
 | |
|   EhcDriverBindingStart,
 | |
|   EhcDriverBindingStop,
 | |
|   0x30,
 | |
|   NULL,
 | |
|   NULL
 | |
| };
 | |
| 
 | |
| /**
 | |
|   Retrieves the capability of root hub ports.
 | |
| 
 | |
|   @param  This                  This EFI_USB_HC_PROTOCOL instance.
 | |
|   @param  MaxSpeed              Max speed supported by the controller.
 | |
|   @param  PortNumber            Number of the root hub ports.
 | |
|   @param  Is64BitCapable        Whether the controller supports 64-bit memory
 | |
|                                 addressing.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Host controller capability were retrieved successfully.
 | |
|   @retval EFI_INVALID_PARAMETER Either of the three capability pointer is NULL.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcGetCapability (
 | |
|   IN  EFI_USB2_HC_PROTOCOL  *This,
 | |
|   OUT UINT8                 *MaxSpeed,
 | |
|   OUT UINT8                 *PortNumber,
 | |
|   OUT UINT8                 *Is64BitCapable
 | |
|   )
 | |
| {
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   EFI_TPL                 OldTpl;
 | |
| 
 | |
|   if ((MaxSpeed == NULL) || (PortNumber == NULL) || (Is64BitCapable == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   OldTpl          = gBS->RaiseTPL (EHC_TPL);
 | |
|   Ehc             = EHC_FROM_THIS (This);
 | |
| 
 | |
|   *MaxSpeed       = EFI_USB_SPEED_HIGH;
 | |
|   *PortNumber     = (UINT8) (Ehc->HcStructParams & HCSP_NPORTS);
 | |
|   *Is64BitCapable = (UINT8) (Ehc->HcCapParams & HCCP_64BIT);
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "EhcGetCapability: %d ports, 64 bit %d\n", *PortNumber, *Is64BitCapable));
 | |
| 
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Provides software reset for the USB host controller.
 | |
| 
 | |
|   @param  This                  This EFI_USB2_HC_PROTOCOL instance.
 | |
|   @param  Attributes            A bit mask of the reset operation to perform.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The reset operation succeeded.
 | |
|   @retval EFI_INVALID_PARAMETER Attributes is not valid.
 | |
|   @retval EFI_UNSUPPOURTED      The type of reset specified by Attributes is
 | |
|                                 not currently supported by the host controller.
 | |
|   @retval EFI_DEVICE_ERROR      Host controller isn't halted to reset.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcReset (
 | |
|   IN EFI_USB2_HC_PROTOCOL *This,
 | |
|   IN UINT16               Attributes
 | |
|   )
 | |
| {
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   EFI_TPL                 OldTpl;
 | |
|   EFI_STATUS              Status;
 | |
|   UINT32                  DbgCtrlStatus;
 | |
| 
 | |
|   Ehc = EHC_FROM_THIS (This);
 | |
| 
 | |
|   if (Ehc->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),
 | |
|       Ehc->DevicePath
 | |
|       );
 | |
|   }
 | |
| 
 | |
|   OldTpl  = gBS->RaiseTPL (EHC_TPL);
 | |
| 
 | |
|   switch (Attributes) {
 | |
|   case EFI_USB_HC_RESET_GLOBAL:
 | |
|   //
 | |
|   // Flow through, same behavior as Host Controller Reset
 | |
|   //
 | |
|   case EFI_USB_HC_RESET_HOST_CONTROLLER:
 | |
|     //
 | |
|     // Host Controller must be Halt when Reset it
 | |
|     //
 | |
|     if (Ehc->DebugPortNum != 0) {
 | |
|       DbgCtrlStatus = EhcReadDbgRegister(Ehc, 0);
 | |
|       if ((DbgCtrlStatus & (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) == (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) {
 | |
|         Status = EFI_SUCCESS;
 | |
|         goto ON_EXIT;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (!EhcIsHalt (Ehc)) {
 | |
|       Status = EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT);
 | |
| 
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         Status = EFI_DEVICE_ERROR;
 | |
|         goto ON_EXIT;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Clean up the asynchronous transfers, currently only
 | |
|     // interrupt supports asynchronous operation.
 | |
|     //
 | |
|     EhciDelAllAsyncIntTransfers (Ehc);
 | |
|     EhcAckAllInterrupt (Ehc);
 | |
|     EhcFreeSched (Ehc);
 | |
| 
 | |
|     Status = EhcResetHC (Ehc, EHC_RESET_TIMEOUT);
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
| 
 | |
|     Status = EhcInitHC (Ehc);
 | |
|     break;
 | |
| 
 | |
|   case EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG:
 | |
|   case EFI_USB_HC_RESET_HOST_WITH_DEBUG:
 | |
|     Status = EFI_UNSUPPORTED;
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
| ON_EXIT:
 | |
|   DEBUG ((EFI_D_INFO, "EhcReset: exit status %r\n", Status));
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Retrieve the current state of the USB host controller.
 | |
| 
 | |
|   @param  This                   This EFI_USB2_HC_PROTOCOL instance.
 | |
|   @param  State                  Variable to return the current host controller
 | |
|                                  state.
 | |
| 
 | |
|   @retval EFI_SUCCESS            Host controller state was returned in State.
 | |
|   @retval EFI_INVALID_PARAMETER  State is NULL.
 | |
|   @retval EFI_DEVICE_ERROR       An error was encountered while attempting to
 | |
|                                  retrieve the host controller's current state.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcGetState (
 | |
|   IN   EFI_USB2_HC_PROTOCOL  *This,
 | |
|   OUT  EFI_USB_HC_STATE      *State
 | |
|   )
 | |
| {
 | |
|   EFI_TPL                 OldTpl;
 | |
|   USB2_HC_DEV             *Ehc;
 | |
| 
 | |
|   if (State == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   OldTpl  = gBS->RaiseTPL (EHC_TPL);
 | |
|   Ehc     = EHC_FROM_THIS (This);
 | |
| 
 | |
|   if (EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT)) {
 | |
|     *State = EfiUsbHcStateHalt;
 | |
|   } else {
 | |
|     *State = EfiUsbHcStateOperational;
 | |
|   }
 | |
| 
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "EhcGetState: current state %d\n", *State));
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Sets the USB host controller to a specific state.
 | |
| 
 | |
|   @param  This                  This EFI_USB2_HC_PROTOCOL instance.
 | |
|   @param  State                 The state of the host controller that will be set.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The USB host controller was successfully placed
 | |
|                                 in the state specified by State.
 | |
|   @retval EFI_INVALID_PARAMETER State is invalid.
 | |
|   @retval EFI_DEVICE_ERROR      Failed to set the state due to device error.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcSetState (
 | |
|   IN EFI_USB2_HC_PROTOCOL *This,
 | |
|   IN EFI_USB_HC_STATE     State
 | |
|   )
 | |
| {
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   EFI_TPL                 OldTpl;
 | |
|   EFI_STATUS              Status;
 | |
|   EFI_USB_HC_STATE        CurState;
 | |
| 
 | |
|   Status = EhcGetState (This, &CurState);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (CurState == State) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   OldTpl  = gBS->RaiseTPL (EHC_TPL);
 | |
|   Ehc     = EHC_FROM_THIS (This);
 | |
| 
 | |
|   switch (State) {
 | |
|   case EfiUsbHcStateHalt:
 | |
|     Status = EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbHcStateOperational:
 | |
|     if (EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_SYS_ERROR)) {
 | |
|       Status = EFI_DEVICE_ERROR;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Software must not write a one to this field unless the host controller
 | |
|     // is in the Halted state. Doing so will yield undefined results.
 | |
|     // refers to Spec[EHCI1.0-2.3.1]
 | |
|     //
 | |
|     if (!EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT)) {
 | |
|       Status = EFI_DEVICE_ERROR;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     Status = EhcRunHC (Ehc, EHC_GENERIC_TIMEOUT);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbHcStateSuspend:
 | |
|     Status = EFI_UNSUPPORTED;
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "EhcSetState: exit status %r\n", Status));
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Retrieves the current status of a USB root hub port.
 | |
| 
 | |
|   @param  This                  This EFI_USB2_HC_PROTOCOL instance.
 | |
|   @param  PortNumber            The root hub port to retrieve the state from.
 | |
|                                 This value is zero-based.
 | |
|   @param  PortStatus            Variable to receive the port state.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The status of the USB root hub port specified.
 | |
|                                 by PortNumber was returned in PortStatus.
 | |
|   @retval EFI_INVALID_PARAMETER PortNumber is invalid.
 | |
|   @retval EFI_DEVICE_ERROR      Can't read register.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcGetRootHubPortStatus (
 | |
|   IN   EFI_USB2_HC_PROTOCOL  *This,
 | |
|   IN   UINT8                 PortNumber,
 | |
|   OUT  EFI_USB_PORT_STATUS   *PortStatus
 | |
|   )
 | |
| {
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   EFI_TPL                 OldTpl;
 | |
|   UINT32                  Offset;
 | |
|   UINT32                  State;
 | |
|   UINT32                  TotalPort;
 | |
|   UINTN                   Index;
 | |
|   UINTN                   MapSize;
 | |
|   EFI_STATUS              Status;
 | |
|   UINT32                  DbgCtrlStatus;
 | |
| 
 | |
|   if (PortStatus == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   OldTpl    = gBS->RaiseTPL (EHC_TPL);
 | |
| 
 | |
|   Ehc       = EHC_FROM_THIS (This);
 | |
|   Status    = EFI_SUCCESS;
 | |
| 
 | |
|   TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
 | |
| 
 | |
|   if (PortNumber >= TotalPort) {
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   Offset                        = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber));
 | |
|   PortStatus->PortStatus        = 0;
 | |
|   PortStatus->PortChangeStatus  = 0;
 | |
| 
 | |
|   if ((Ehc->DebugPortNum != 0) && (PortNumber == (Ehc->DebugPortNum - 1))) {
 | |
|     DbgCtrlStatus = EhcReadDbgRegister(Ehc, 0);
 | |
|     if ((DbgCtrlStatus & (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) == (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) {
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   State                         = EhcReadOpReg (Ehc, Offset);
 | |
| 
 | |
|   //
 | |
|   // Identify device speed. If in K state, it is low speed.
 | |
|   // If the port is enabled after reset, the device is of
 | |
|   // high speed. The USB bus driver should retrieve the actual
 | |
|   // port speed after reset.
 | |
|   //
 | |
|   if (EHC_BIT_IS_SET (State, PORTSC_LINESTATE_K)) {
 | |
|     PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED;
 | |
| 
 | |
|   } else if (EHC_BIT_IS_SET (State, PORTSC_ENABLED)) {
 | |
|     PortStatus->PortStatus |= USB_PORT_STAT_HIGH_SPEED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Convert the EHCI port/port change state to UEFI status
 | |
|   //
 | |
|   MapSize = sizeof (mUsbPortStateMap) / sizeof (USB_PORT_STATE_MAP);
 | |
| 
 | |
|   for (Index = 0; Index < MapSize; Index++) {
 | |
|     if (EHC_BIT_IS_SET (State, mUsbPortStateMap[Index].HwState)) {
 | |
|       PortStatus->PortStatus = (UINT16) (PortStatus->PortStatus | mUsbPortStateMap[Index].UefiState);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   MapSize = sizeof (mUsbPortChangeMap) / sizeof (USB_PORT_STATE_MAP);
 | |
| 
 | |
|   for (Index = 0; Index < MapSize; Index++) {
 | |
|     if (EHC_BIT_IS_SET (State, mUsbPortChangeMap[Index].HwState)) {
 | |
|       PortStatus->PortChangeStatus = (UINT16) (PortStatus->PortChangeStatus | mUsbPortChangeMap[Index].UefiState);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| ON_EXIT:
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Sets a feature for the specified root hub port.
 | |
| 
 | |
|   @param  This                  This EFI_USB2_HC_PROTOCOL instance.
 | |
|   @param  PortNumber            Root hub port to set.
 | |
|   @param  PortFeature           Feature to set.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The feature specified by PortFeature was set.
 | |
|   @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid.
 | |
|   @retval EFI_DEVICE_ERROR      Can't read register.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcSetRootHubPortFeature (
 | |
|   IN  EFI_USB2_HC_PROTOCOL  *This,
 | |
|   IN  UINT8                 PortNumber,
 | |
|   IN  EFI_USB_PORT_FEATURE  PortFeature
 | |
|   )
 | |
| {
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   EFI_TPL                 OldTpl;
 | |
|   UINT32                  Offset;
 | |
|   UINT32                  State;
 | |
|   UINT32                  TotalPort;
 | |
|   EFI_STATUS              Status;
 | |
| 
 | |
|   OldTpl    = gBS->RaiseTPL (EHC_TPL);
 | |
|   Ehc       = EHC_FROM_THIS (This);
 | |
|   Status    = EFI_SUCCESS;
 | |
| 
 | |
|   TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
 | |
| 
 | |
|   if (PortNumber >= TotalPort) {
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   Offset  = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber));
 | |
|   State   = EhcReadOpReg (Ehc, Offset);
 | |
| 
 | |
|   //
 | |
|   // Mask off the port status change bits, these bits are
 | |
|   // write clean bit
 | |
|   //
 | |
|   State &= ~PORTSC_CHANGE_MASK;
 | |
| 
 | |
|   switch (PortFeature) {
 | |
|   case EfiUsbPortEnable:
 | |
|     //
 | |
|     // Sofeware can't set this bit, Port can only be enable by
 | |
|     // EHCI as a part of the reset and enable
 | |
|     //
 | |
|     State |= PORTSC_ENABLED;
 | |
|     EhcWriteOpReg (Ehc, Offset, State);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbPortSuspend:
 | |
|     State |= PORTSC_SUSPEND;
 | |
|     EhcWriteOpReg (Ehc, Offset, State);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbPortReset:
 | |
|     //
 | |
|     // Make sure Host Controller not halt before reset it
 | |
|     //
 | |
|     if (EhcIsHalt (Ehc)) {
 | |
|       Status = EhcRunHC (Ehc, EHC_GENERIC_TIMEOUT);
 | |
| 
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         DEBUG ((EFI_D_INFO, "EhcSetRootHubPortFeature :failed to start HC - %r\n", Status));
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Set one to PortReset bit must also set zero to PortEnable bit
 | |
|     //
 | |
|     State |= PORTSC_RESET;
 | |
|     State &= ~PORTSC_ENABLED;
 | |
|     EhcWriteOpReg (Ehc, Offset, State);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbPortPower:
 | |
|     //
 | |
|     // Set port power bit when PPC is 1
 | |
|     //
 | |
|     if ((Ehc->HcCapParams & HCSP_PPC) == HCSP_PPC) {
 | |
|       State |= PORTSC_POWER;
 | |
|       EhcWriteOpReg (Ehc, Offset, State);
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbPortOwner:
 | |
|     State |= PORTSC_OWNER;
 | |
|     EhcWriteOpReg (Ehc, Offset, State);
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
| ON_EXIT:
 | |
|   DEBUG ((EFI_D_INFO, "EhcSetRootHubPortFeature: exit status %r\n", Status));
 | |
| 
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Clears a feature for the specified root hub port.
 | |
| 
 | |
|   @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.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The feature specified by PortFeature was cleared
 | |
|                                 for the USB root hub port specified by PortNumber.
 | |
|   @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid.
 | |
|   @retval EFI_DEVICE_ERROR      Can't read register.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcClearRootHubPortFeature (
 | |
|   IN  EFI_USB2_HC_PROTOCOL  *This,
 | |
|   IN  UINT8                 PortNumber,
 | |
|   IN  EFI_USB_PORT_FEATURE  PortFeature
 | |
|   )
 | |
| {
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   EFI_TPL                 OldTpl;
 | |
|   UINT32                  Offset;
 | |
|   UINT32                  State;
 | |
|   UINT32                  TotalPort;
 | |
|   EFI_STATUS              Status;
 | |
| 
 | |
|   OldTpl    = gBS->RaiseTPL (EHC_TPL);
 | |
|   Ehc       = EHC_FROM_THIS (This);
 | |
|   Status    = EFI_SUCCESS;
 | |
| 
 | |
|   TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
 | |
| 
 | |
|   if (PortNumber >= TotalPort) {
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   Offset  = EHC_PORT_STAT_OFFSET + (4 * PortNumber);
 | |
|   State   = EhcReadOpReg (Ehc, Offset);
 | |
|   State &= ~PORTSC_CHANGE_MASK;
 | |
| 
 | |
|   switch (PortFeature) {
 | |
|   case EfiUsbPortEnable:
 | |
|     //
 | |
|     // Clear PORT_ENABLE feature means disable port.
 | |
|     //
 | |
|     State &= ~PORTSC_ENABLED;
 | |
|     EhcWriteOpReg (Ehc, Offset, State);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbPortSuspend:
 | |
|     //
 | |
|     // A write of zero to this bit is ignored by the host
 | |
|     // controller. The host controller will unconditionally
 | |
|     // set this bit to a zero when:
 | |
|     //   1. software sets the Forct Port Resume bit to a zero from a one.
 | |
|     //   2. software sets the Port Reset bit to a one frome a zero.
 | |
|     //
 | |
|     State &= ~PORSTSC_RESUME;
 | |
|     EhcWriteOpReg (Ehc, Offset, State);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbPortReset:
 | |
|     //
 | |
|     // Clear PORT_RESET means clear the reset signal.
 | |
|     //
 | |
|     State &= ~PORTSC_RESET;
 | |
|     EhcWriteOpReg (Ehc, Offset, State);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbPortOwner:
 | |
|     //
 | |
|     // Clear port owner means this port owned by EHC
 | |
|     //
 | |
|     State &= ~PORTSC_OWNER;
 | |
|     EhcWriteOpReg (Ehc, Offset, State);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbPortConnectChange:
 | |
|     //
 | |
|     // Clear connect status change
 | |
|     //
 | |
|     State |= PORTSC_CONN_CHANGE;
 | |
|     EhcWriteOpReg (Ehc, Offset, State);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbPortEnableChange:
 | |
|     //
 | |
|     // Clear enable status change
 | |
|     //
 | |
|     State |= PORTSC_ENABLE_CHANGE;
 | |
|     EhcWriteOpReg (Ehc, Offset, State);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbPortOverCurrentChange:
 | |
|     //
 | |
|     // Clear PortOverCurrent change
 | |
|     //
 | |
|     State |= PORTSC_OVERCUR_CHANGE;
 | |
|     EhcWriteOpReg (Ehc, Offset, State);
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbPortPower:
 | |
|     //
 | |
|     // Clear port power bit when PPC is 1
 | |
|     //
 | |
|     if ((Ehc->HcCapParams & HCSP_PPC) == HCSP_PPC) {
 | |
|       State &= ~PORTSC_POWER;
 | |
|       EhcWriteOpReg (Ehc, Offset, State);
 | |
|     }
 | |
|     break;
 | |
|   case EfiUsbPortSuspendChange:
 | |
|   case EfiUsbPortResetChange:
 | |
|     //
 | |
|     // Not supported or not related operation
 | |
|     //
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     break;
 | |
|   }
 | |
| 
 | |
| ON_EXIT:
 | |
|   DEBUG ((EFI_D_INFO, "EhcClearRootHubPortFeature: exit status %r\n", Status));
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Submits control transfer to a target USB device.
 | |
| 
 | |
|   @param  This                  This EFI_USB2_HC_PROTOCOL instance.
 | |
|   @param  DeviceAddress         The target device address.
 | |
|   @param  DeviceSpeed           Target device speed.
 | |
|   @param  MaximumPacketLength   Maximum packet size the default control transfer
 | |
|                                 endpoint is capable of sending or receiving.
 | |
|   @param  Request               USB device request to send.
 | |
|   @param  TransferDirection     Specifies the data direction for the data stage
 | |
|   @param  Data                  Data buffer to be transmitted or received from USB
 | |
|                                 device.
 | |
|   @param  DataLength            The size (in bytes) of the data buffer.
 | |
|   @param  TimeOut               Indicates the maximum timeout, in millisecond.
 | |
|   @param  Translator            Transaction translator to be used by this device.
 | |
|   @param  TransferResult        Return the result of this control transfer.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Transfer was completed successfully.
 | |
|   @retval EFI_OUT_OF_RESOURCES  The transfer failed due to lack of resources.
 | |
|   @retval EFI_INVALID_PARAMETER Some parameters are invalid.
 | |
|   @retval EFI_TIMEOUT           Transfer failed due to timeout.
 | |
|   @retval EFI_DEVICE_ERROR      Transfer failed due to host controller or device error.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcControlTransfer (
 | |
|   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
 | |
|   )
 | |
| {
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   URB                     *Urb;
 | |
|   EFI_TPL                 OldTpl;
 | |
|   UINT8                   Endpoint;
 | |
|   EFI_STATUS              Status;
 | |
| 
 | |
|   //
 | |
|   // Validate parameters
 | |
|   //
 | |
|   if ((Request == NULL) || (TransferResult == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((TransferDirection != EfiUsbDataIn) &&
 | |
|       (TransferDirection != EfiUsbDataOut) &&
 | |
|       (TransferDirection != EfiUsbNoData)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((TransferDirection == EfiUsbNoData) &&
 | |
|       ((Data != NULL) || (*DataLength != 0))) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((TransferDirection != EfiUsbNoData) &&
 | |
|      ((Data == NULL) || (*DataLength == 0))) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((MaximumPacketLength != 8)  && (MaximumPacketLength != 16) &&
 | |
|       (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((DeviceSpeed == EFI_USB_SPEED_LOW) && (MaximumPacketLength != 8)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   OldTpl          = gBS->RaiseTPL (EHC_TPL);
 | |
|   Ehc             = EHC_FROM_THIS (This);
 | |
| 
 | |
|   Status          = EFI_DEVICE_ERROR;
 | |
|   *TransferResult = EFI_USB_ERR_SYSTEM;
 | |
| 
 | |
|   if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcControlTransfer: HC halted at entrance\n"));
 | |
| 
 | |
|     EhcAckAllInterrupt (Ehc);
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   EhcAckAllInterrupt (Ehc);
 | |
| 
 | |
|   //
 | |
|   // Create a new URB, insert it into the asynchronous
 | |
|   // schedule list, then poll the execution status.
 | |
|   //
 | |
|   //
 | |
|   // Encode the direction in address, although default control
 | |
|   // endpoint is bidirectional. EhcCreateUrb expects this
 | |
|   // combination of Ep addr and its direction.
 | |
|   //
 | |
|   Endpoint = (UINT8) (0 | ((TransferDirection == EfiUsbDataIn) ? 0x80 : 0));
 | |
|   Urb = EhcCreateUrb (
 | |
|           Ehc,
 | |
|           DeviceAddress,
 | |
|           Endpoint,
 | |
|           DeviceSpeed,
 | |
|           0,
 | |
|           MaximumPacketLength,
 | |
|           Translator,
 | |
|           EHC_CTRL_TRANSFER,
 | |
|           Request,
 | |
|           Data,
 | |
|           *DataLength,
 | |
|           NULL,
 | |
|           NULL,
 | |
|           1
 | |
|           );
 | |
| 
 | |
|   if (Urb == NULL) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcControlTransfer: failed to create URB"));
 | |
| 
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   EhcLinkQhToAsync (Ehc, Urb->Qh);
 | |
|   Status = EhcExecTransfer (Ehc, Urb, TimeOut);
 | |
|   EhcUnlinkQhFromAsync (Ehc, Urb->Qh);
 | |
| 
 | |
|   //
 | |
|   // Get the status from URB. The result is updated in EhcCheckUrbResult
 | |
|   // which is called by EhcExecTransfer
 | |
|   //
 | |
|   *TransferResult = Urb->Result;
 | |
|   *DataLength     = Urb->Completed;
 | |
| 
 | |
|   if (*TransferResult == EFI_USB_NOERROR) {
 | |
|     Status = EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   EhcAckAllInterrupt (Ehc);
 | |
|   EhcFreeUrb (Ehc, Urb);
 | |
| 
 | |
| ON_EXIT:
 | |
|   Ehc->PciIo->Flush (Ehc->PciIo);
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcControlTransfer: error - %r, transfer - %x\n", Status, *TransferResult));
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Submits bulk transfer to a bulk endpoint of a USB device.
 | |
| 
 | |
|   @param  This                  This EFI_USB2_HC_PROTOCOL instance.
 | |
|   @param  DeviceAddress         Target device address.
 | |
|   @param  EndPointAddress       Endpoint number and its direction in bit 7.
 | |
|   @param  DeviceSpeed           Device speed, Low speed device doesn't support bulk
 | |
|                                 transfer.
 | |
|   @param  MaximumPacketLength   Maximum packet size the endpoint is capable of
 | |
|                                 sending or receiving.
 | |
|   @param  DataBuffersNumber     Number of data buffers prepared for the transfer.
 | |
|   @param  Data                  Array of pointers to the buffers of data to transmit
 | |
|                                 from or receive into.
 | |
|   @param  DataLength            The lenght of the data buffer.
 | |
|   @param  DataToggle            On input, the initial data toggle for the transfer;
 | |
|                                 On output, it is updated to to next data toggle to
 | |
|                                 use of the subsequent bulk transfer.
 | |
|   @param  TimeOut               Indicates the maximum time, in millisecond, which
 | |
|                                 the transfer is allowed to complete.
 | |
|   @param  Translator            A pointr to the transaction translator data.
 | |
|   @param  TransferResult        A pointer to the detailed result information of the
 | |
|                                 bulk transfer.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The transfer was completed successfully.
 | |
|   @retval EFI_OUT_OF_RESOURCES  The transfer failed due to lack of resource.
 | |
|   @retval EFI_INVALID_PARAMETER Some parameters are invalid.
 | |
|   @retval EFI_TIMEOUT           The transfer failed due to timeout.
 | |
|   @retval EFI_DEVICE_ERROR      The transfer failed due to host controller error.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcBulkTransfer (
 | |
|   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
 | |
|   )
 | |
| {
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   URB                     *Urb;
 | |
|   EFI_TPL                 OldTpl;
 | |
|   EFI_STATUS              Status;
 | |
| 
 | |
|   //
 | |
|   // Validate the parameters
 | |
|   //
 | |
|   if ((DataLength == NULL) || (*DataLength == 0) ||
 | |
|       (Data == NULL) || (Data[0] == NULL) || (TransferResult == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((*DataToggle != 0) && (*DataToggle != 1)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((DeviceSpeed == EFI_USB_SPEED_LOW) ||
 | |
|       ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) ||
 | |
|       ((EFI_USB_SPEED_HIGH == DeviceSpeed) && (MaximumPacketLength > 512))) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   OldTpl          = gBS->RaiseTPL (EHC_TPL);
 | |
|   Ehc             = EHC_FROM_THIS (This);
 | |
| 
 | |
|   *TransferResult = EFI_USB_ERR_SYSTEM;
 | |
|   Status          = EFI_DEVICE_ERROR;
 | |
| 
 | |
|   if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcBulkTransfer: HC is halted\n"));
 | |
| 
 | |
|     EhcAckAllInterrupt (Ehc);
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   EhcAckAllInterrupt (Ehc);
 | |
| 
 | |
|   //
 | |
|   // Create a new URB, insert it into the asynchronous
 | |
|   // schedule list, then poll the execution status.
 | |
|   //
 | |
|   Urb = EhcCreateUrb (
 | |
|           Ehc,
 | |
|           DeviceAddress,
 | |
|           EndPointAddress,
 | |
|           DeviceSpeed,
 | |
|           *DataToggle,
 | |
|           MaximumPacketLength,
 | |
|           Translator,
 | |
|           EHC_BULK_TRANSFER,
 | |
|           NULL,
 | |
|           Data[0],
 | |
|           *DataLength,
 | |
|           NULL,
 | |
|           NULL,
 | |
|           1
 | |
|           );
 | |
| 
 | |
|   if (Urb == NULL) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcBulkTransfer: failed to create URB\n"));
 | |
| 
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   EhcLinkQhToAsync (Ehc, Urb->Qh);
 | |
|   Status = EhcExecTransfer (Ehc, Urb, TimeOut);
 | |
|   EhcUnlinkQhFromAsync (Ehc, Urb->Qh);
 | |
| 
 | |
|   *TransferResult = Urb->Result;
 | |
|   *DataLength     = Urb->Completed;
 | |
|   *DataToggle     = Urb->DataToggle;
 | |
| 
 | |
|   if (*TransferResult == EFI_USB_NOERROR) {
 | |
|     Status = EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   EhcAckAllInterrupt (Ehc);
 | |
|   EhcFreeUrb (Ehc, Urb);
 | |
| 
 | |
| ON_EXIT:
 | |
|   Ehc->PciIo->Flush (Ehc->PciIo);
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcBulkTransfer: error - %r, transfer - %x\n", Status, *TransferResult));
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Submits an asynchronous interrupt transfer to an
 | |
|   interrupt endpoint of a USB device.
 | |
| 
 | |
|   @param  This                  This EFI_USB2_HC_PROTOCOL instance.
 | |
|   @param  DeviceAddress         Target device address.
 | |
|   @param  EndPointAddress       Endpoint number and its direction encoded in bit 7
 | |
|   @param  DeviceSpeed           Indicates device speed.
 | |
|   @param  MaximumPacketLength   Maximum packet size the target endpoint is capable
 | |
|   @param  IsNewTransfer         If TRUE, to submit an new asynchronous interrupt
 | |
|                                 transfer If FALSE, to remove the specified
 | |
|                                 asynchronous interrupt.
 | |
|   @param  DataToggle            On input, the initial data toggle to use; on output,
 | |
|                                 it is updated to indicate the next data toggle.
 | |
|   @param  PollingInterval       The he interval, in milliseconds, that the transfer
 | |
|                                 is polled.
 | |
|   @param  DataLength            The length of data to receive at the rate specified
 | |
|                                 by  PollingInterval.
 | |
|   @param  Translator            Transaction translator to use.
 | |
|   @param  CallBackFunction      Function to call at the rate specified by
 | |
|                                 PollingInterval.
 | |
|   @param  Context               Context to CallBackFunction.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The request has been successfully submitted or canceled.
 | |
|   @retval EFI_INVALID_PARAMETER Some parameters are invalid.
 | |
|   @retval EFI_OUT_OF_RESOURCES  The request failed due to a lack of resources.
 | |
|   @retval EFI_DEVICE_ERROR      The transfer failed due to host controller error.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcAsyncInterruptTransfer (
 | |
|   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 OPTIONAL
 | |
|   )
 | |
| {
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   URB                     *Urb;
 | |
|   EFI_TPL                 OldTpl;
 | |
|   EFI_STATUS              Status;
 | |
|   UINT8                   *Data;
 | |
| 
 | |
|   //
 | |
|   // Validate parameters
 | |
|   //
 | |
|   if (!EHCI_IS_DATAIN (EndPointAddress)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (IsNewTransfer) {
 | |
|     if (DataLength == 0) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     if ((*DataToggle != 1) && (*DataToggle != 0)) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     if ((PollingInterval > 255) || (PollingInterval < 1)) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   OldTpl  = gBS->RaiseTPL (EHC_TPL);
 | |
|   Ehc     = EHC_FROM_THIS (This);
 | |
| 
 | |
|   //
 | |
|   // Delete Async interrupt transfer request. DataToggle will return
 | |
|   // the next data toggle to use.
 | |
|   //
 | |
|   if (!IsNewTransfer) {
 | |
|     Status = EhciDelAsyncIntTransfer (Ehc, DeviceAddress, EndPointAddress, DataToggle);
 | |
| 
 | |
|     DEBUG ((EFI_D_INFO, "EhcAsyncInterruptTransfer: remove old transfer - %r\n", Status));
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcAsyncInterruptTransfer: HC is halt\n"));
 | |
|     EhcAckAllInterrupt (Ehc);
 | |
| 
 | |
|     Status = EFI_DEVICE_ERROR;
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   EhcAckAllInterrupt (Ehc);
 | |
| 
 | |
|   Data = AllocatePool (DataLength);
 | |
| 
 | |
|   if (Data == NULL) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcAsyncInterruptTransfer: failed to allocate buffer\n"));
 | |
| 
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   Urb = EhcCreateUrb (
 | |
|           Ehc,
 | |
|           DeviceAddress,
 | |
|           EndPointAddress,
 | |
|           DeviceSpeed,
 | |
|           *DataToggle,
 | |
|           MaximumPacketLength,
 | |
|           Translator,
 | |
|           EHC_INT_TRANSFER_ASYNC,
 | |
|           NULL,
 | |
|           Data,
 | |
|           DataLength,
 | |
|           CallBackFunction,
 | |
|           Context,
 | |
|           PollingInterval
 | |
|           );
 | |
| 
 | |
|   if (Urb == NULL) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcAsyncInterruptTransfer: failed to create URB\n"));
 | |
| 
 | |
|     gBS->FreePool (Data);
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // New asynchronous transfer must inserted to the head.
 | |
|   // Check the comments in EhcMoniteAsyncRequests
 | |
|   //
 | |
|   EhcLinkQhToPeriod (Ehc, Urb->Qh);
 | |
|   InsertHeadList (&Ehc->AsyncIntTransfers, &Urb->UrbList);
 | |
| 
 | |
| ON_EXIT:
 | |
|   Ehc->PciIo->Flush (Ehc->PciIo);
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Submits synchronous interrupt transfer to an interrupt endpoint
 | |
|   of a USB device.
 | |
| 
 | |
|   @param  This                  This EFI_USB2_HC_PROTOCOL instance.
 | |
|   @param  DeviceAddress         Target device address.
 | |
|   @param  EndPointAddress       Endpoint number and its direction encoded in bit 7
 | |
|   @param  DeviceSpeed           Indicates device speed.
 | |
|   @param  MaximumPacketLength   Maximum packet size the target endpoint is capable
 | |
|                                 of sending or receiving.
 | |
|   @param  Data                  Buffer of data that will be transmitted to  USB
 | |
|                                 device or received from USB device.
 | |
|   @param  DataLength            On input, the size, in bytes, of the data buffer; On
 | |
|                                 output, the number of bytes transferred.
 | |
|   @param  DataToggle            On input, the initial data toggle to use; on output,
 | |
|                                 it is updated to indicate the next data toggle.
 | |
|   @param  TimeOut               Maximum time, in second, to complete.
 | |
|   @param  Translator            Transaction translator to use.
 | |
|   @param  TransferResult        Variable to receive the transfer result.
 | |
| 
 | |
|   @return EFI_SUCCESS           The transfer was completed successfully.
 | |
|   @return EFI_OUT_OF_RESOURCES  The transfer failed due to lack of resource.
 | |
|   @return EFI_INVALID_PARAMETER Some parameters are invalid.
 | |
|   @return EFI_TIMEOUT           The transfer failed due to timeout.
 | |
|   @return EFI_DEVICE_ERROR      The failed due to host controller or device error
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcSyncInterruptTransfer (
 | |
|   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
 | |
|   )
 | |
| {
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   EFI_TPL                 OldTpl;
 | |
|   URB                     *Urb;
 | |
|   EFI_STATUS              Status;
 | |
| 
 | |
|   //
 | |
|   // Validates parameters
 | |
|   //
 | |
|   if ((DataLength == NULL) || (*DataLength == 0) ||
 | |
|       (Data == NULL) || (TransferResult == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((*DataToggle != 1) && (*DataToggle != 0)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (((DeviceSpeed == EFI_USB_SPEED_LOW) && (MaximumPacketLength != 8))  ||
 | |
|       ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) ||
 | |
|       ((DeviceSpeed == EFI_USB_SPEED_HIGH) && (MaximumPacketLength > 3072))) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   OldTpl          = gBS->RaiseTPL (EHC_TPL);
 | |
|   Ehc             = EHC_FROM_THIS (This);
 | |
| 
 | |
|   *TransferResult = EFI_USB_ERR_SYSTEM;
 | |
|   Status          = EFI_DEVICE_ERROR;
 | |
| 
 | |
|   if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcSyncInterruptTransfer: HC is halt\n"));
 | |
| 
 | |
|     EhcAckAllInterrupt (Ehc);
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   EhcAckAllInterrupt (Ehc);
 | |
| 
 | |
|   Urb = EhcCreateUrb (
 | |
|           Ehc,
 | |
|           DeviceAddress,
 | |
|           EndPointAddress,
 | |
|           DeviceSpeed,
 | |
|           *DataToggle,
 | |
|           MaximumPacketLength,
 | |
|           Translator,
 | |
|           EHC_INT_TRANSFER_SYNC,
 | |
|           NULL,
 | |
|           Data,
 | |
|           *DataLength,
 | |
|           NULL,
 | |
|           NULL,
 | |
|           1
 | |
|           );
 | |
| 
 | |
|   if (Urb == NULL) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcSyncInterruptTransfer: failed to create URB\n"));
 | |
| 
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   EhcLinkQhToPeriod (Ehc, Urb->Qh);
 | |
|   Status = EhcExecTransfer (Ehc, Urb, TimeOut);
 | |
|   EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);
 | |
| 
 | |
|   *TransferResult = Urb->Result;
 | |
|   *DataLength     = Urb->Completed;
 | |
|   *DataToggle     = Urb->DataToggle;
 | |
| 
 | |
|   if (*TransferResult == EFI_USB_NOERROR) {
 | |
|     Status = EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
| ON_EXIT:
 | |
|   Ehc->PciIo->Flush (Ehc->PciIo);
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcSyncInterruptTransfer: error - %r, transfer - %x\n", Status, *TransferResult));
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Submits isochronous transfer to a target USB device.
 | |
| 
 | |
|   @param  This                 This EFI_USB2_HC_PROTOCOL instance.
 | |
|   @param  DeviceAddress        Target device address.
 | |
|   @param  EndPointAddress      End point address with its direction.
 | |
|   @param  DeviceSpeed          Device speed, Low speed device doesn't support this
 | |
|                                type.
 | |
|   @param  MaximumPacketLength  Maximum packet size that the endpoint is capable of
 | |
|                                sending or receiving.
 | |
|   @param  DataBuffersNumber    Number of data buffers prepared for the transfer.
 | |
|   @param  Data                 Array of pointers to the buffers of data that will
 | |
|                                be transmitted to USB device or received from USB
 | |
|                                device.
 | |
|   @param  DataLength           The size, in bytes, of the data buffer.
 | |
|   @param  Translator           Transaction translator to use.
 | |
|   @param  TransferResult       Variable to receive the transfer result.
 | |
| 
 | |
|   @return EFI_UNSUPPORTED      Isochronous transfer is unsupported.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcIsochronousTransfer (
 | |
|   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.
 | |
| 
 | |
|   @param  This                 This EFI_USB2_HC_PROTOCOL instance.
 | |
|   @param  DeviceAddress        Target device address.
 | |
|   @param  EndPointAddress      End point address with its direction.
 | |
|   @param  DeviceSpeed          Device speed, Low speed device doesn't support this
 | |
|                                type.
 | |
|   @param  MaximumPacketLength  Maximum packet size that the endpoint is capable of
 | |
|                                sending or receiving.
 | |
|   @param  DataBuffersNumber    Number of data buffers prepared for the transfer.
 | |
|   @param  Data                 Array of pointers to the buffers of data that will
 | |
|                                be transmitted to USB device or received from USB
 | |
|                                device.
 | |
|   @param  DataLength           The size, in bytes, of the data buffer.
 | |
|   @param  Translator           Transaction translator to use.
 | |
|   @param  IsochronousCallBack  Function to be called when the transfer complete.
 | |
|   @param  Context              Context passed to the call back function as
 | |
|                                parameter.
 | |
| 
 | |
|   @return EFI_UNSUPPORTED      Isochronous transfer isn't supported.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcAsyncIsochronousTransfer (
 | |
|   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.
 | |
| 
 | |
|   @return EFI_SUCCESS       Success.
 | |
|           EFI_DEVICE_ERROR  Fail.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcDriverEntryPoint (
 | |
|   IN EFI_HANDLE           ImageHandle,
 | |
|   IN EFI_SYSTEM_TABLE     *SystemTable
 | |
|   )
 | |
| {
 | |
|   return EfiLibInstallDriverBindingComponentName2 (
 | |
|            ImageHandle,
 | |
|            SystemTable,
 | |
|            &gEhciDriverBinding,
 | |
|            ImageHandle,
 | |
|            &gEhciComponentName,
 | |
|            &gEhciComponentName2
 | |
|            );
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Test to see if this driver supports ControllerHandle. Any
 | |
|   ControllerHandle that has Usb2HcProtocol 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
 | |
| EhcDriverBindingSupported (
 | |
|   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_CLASSC              UsbClassCReg;
 | |
| 
 | |
|   //
 | |
|   // Test whether there is PCI IO Protocol attached on the controller handle.
 | |
|   //
 | |
|   Status = gBS->OpenProtocol (
 | |
|                   Controller,
 | |
|                   &gEfiPciIoProtocolGuid,
 | |
|                   (VOID **) &PciIo,
 | |
|                   This->DriverBindingHandle,
 | |
|                   Controller,
 | |
|                   EFI_OPEN_PROTOCOL_BY_DRIVER
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   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 Ehci type
 | |
|   //
 | |
|   if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB)
 | |
|       || ((UsbClassCReg.ProgInterface != PCI_IF_EHCI) && (UsbClassCReg.ProgInterface != PCI_IF_UHCI) && (UsbClassCReg.ProgInterface != PCI_IF_OHCI))) {
 | |
| 
 | |
|     Status = EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
| ON_EXIT:
 | |
|   gBS->CloseProtocol (
 | |
|          Controller,
 | |
|          &gEfiPciIoProtocolGuid,
 | |
|          This->DriverBindingHandle,
 | |
|          Controller
 | |
|          );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get the usb debug port related information.
 | |
| 
 | |
|   @param  Ehc                The EHCI device.
 | |
| 
 | |
|   @retval RETURN_SUCCESS     Get debug port number, bar and offset successfully.
 | |
|   @retval Others             The usb host controller does not supported usb debug port capability.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EhcGetUsbDebugPortInfo (
 | |
|   IN  USB2_HC_DEV     *Ehc
 | |
|  )
 | |
| {
 | |
|   EFI_PCI_IO_PROTOCOL *PciIo;
 | |
|   UINT16              PciStatus;
 | |
|   UINT8               CapabilityPtr;
 | |
|   UINT8               CapabilityId;
 | |
|   UINT16              DebugPort;
 | |
|   EFI_STATUS          Status;
 | |
| 
 | |
|   ASSERT (Ehc->PciIo != NULL);
 | |
|   PciIo = Ehc->PciIo;
 | |
| 
 | |
|   //
 | |
|   // Detect if the EHCI host controller support Capaility Pointer.
 | |
|   //
 | |
|   Status = PciIo->Pci.Read (
 | |
|                         PciIo,
 | |
|                         EfiPciIoWidthUint8,
 | |
|                         PCI_PRIMARY_STATUS_OFFSET,
 | |
|                         sizeof (UINT16),
 | |
|                         &PciStatus
 | |
|                         );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if ((PciStatus & EFI_PCI_STATUS_CAPABILITY) == 0) {
 | |
|     //
 | |
|     // The Pci Device Doesn't Support Capability Pointer.
 | |
|     //
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get Pointer To Capability List
 | |
|   //
 | |
|   Status = PciIo->Pci.Read (
 | |
|                         PciIo,
 | |
|                         EfiPciIoWidthUint8,
 | |
|                         PCI_CAPBILITY_POINTER_OFFSET,
 | |
|                         1,
 | |
|                         &CapabilityPtr
 | |
|                         );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Find Capability ID 0xA, Which Is For Debug Port
 | |
|   //
 | |
|   while (CapabilityPtr != 0) {
 | |
|     Status = PciIo->Pci.Read (
 | |
|                           PciIo,
 | |
|                           EfiPciIoWidthUint8,
 | |
|                           CapabilityPtr,
 | |
|                           1,
 | |
|                           &CapabilityId
 | |
|                           );
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     if (CapabilityId == EHC_DEBUG_PORT_CAP_ID) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     Status = PciIo->Pci.Read (
 | |
|                           PciIo,
 | |
|                           EfiPciIoWidthUint8,
 | |
|                           CapabilityPtr + 1,
 | |
|                           1,
 | |
|                           &CapabilityPtr
 | |
|                           );
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // No Debug Port Capability Found
 | |
|   //
 | |
|   if (CapabilityPtr == 0) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get The Base Address Of Debug Port Register In Debug Port Capability Register
 | |
|   //
 | |
|   Status = PciIo->Pci.Read (
 | |
|                         Ehc->PciIo,
 | |
|                         EfiPciIoWidthUint8,
 | |
|                         CapabilityPtr + 2,
 | |
|                         sizeof (UINT16),
 | |
|                         &DebugPort
 | |
|                         );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Ehc->DebugPortOffset = DebugPort & 0x1FFF;
 | |
|   Ehc->DebugPortBarNum = (UINT8)((DebugPort >> 13) - 1);
 | |
|   Ehc->DebugPortNum    = (UINT8)((Ehc->HcStructParams & 0x00F00000) >> 20);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Create and initialize a USB2_HC_DEV.
 | |
| 
 | |
|   @param  PciIo                  The PciIo on this device.
 | |
|   @param  DevicePath             The device path of host controller.
 | |
|   @param  OriginalPciAttributes  Original PCI attributes.
 | |
| 
 | |
|   @return  The allocated and initialized USB2_HC_DEV structure if created,
 | |
|            otherwise NULL.
 | |
| 
 | |
| **/
 | |
| USB2_HC_DEV *
 | |
| EhcCreateUsb2Hc (
 | |
|   IN EFI_PCI_IO_PROTOCOL       *PciIo,
 | |
|   IN EFI_DEVICE_PATH_PROTOCOL  *DevicePath,
 | |
|   IN UINT64                    OriginalPciAttributes
 | |
|   )
 | |
| {
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   EFI_STATUS              Status;
 | |
| 
 | |
|   Ehc = AllocateZeroPool (sizeof (USB2_HC_DEV));
 | |
| 
 | |
|   if (Ehc == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Init EFI_USB2_HC_PROTOCOL interface and private data structure
 | |
|   //
 | |
|   Ehc->Signature                        = USB2_HC_DEV_SIGNATURE;
 | |
| 
 | |
|   Ehc->Usb2Hc.GetCapability             = EhcGetCapability;
 | |
|   Ehc->Usb2Hc.Reset                     = EhcReset;
 | |
|   Ehc->Usb2Hc.GetState                  = EhcGetState;
 | |
|   Ehc->Usb2Hc.SetState                  = EhcSetState;
 | |
|   Ehc->Usb2Hc.ControlTransfer           = EhcControlTransfer;
 | |
|   Ehc->Usb2Hc.BulkTransfer              = EhcBulkTransfer;
 | |
|   Ehc->Usb2Hc.AsyncInterruptTransfer    = EhcAsyncInterruptTransfer;
 | |
|   Ehc->Usb2Hc.SyncInterruptTransfer     = EhcSyncInterruptTransfer;
 | |
|   Ehc->Usb2Hc.IsochronousTransfer       = EhcIsochronousTransfer;
 | |
|   Ehc->Usb2Hc.AsyncIsochronousTransfer  = EhcAsyncIsochronousTransfer;
 | |
|   Ehc->Usb2Hc.GetRootHubPortStatus      = EhcGetRootHubPortStatus;
 | |
|   Ehc->Usb2Hc.SetRootHubPortFeature     = EhcSetRootHubPortFeature;
 | |
|   Ehc->Usb2Hc.ClearRootHubPortFeature   = EhcClearRootHubPortFeature;
 | |
|   Ehc->Usb2Hc.MajorRevision             = 0x2;
 | |
|   Ehc->Usb2Hc.MinorRevision             = 0x0;
 | |
| 
 | |
|   Ehc->PciIo                 = PciIo;
 | |
|   Ehc->DevicePath            = DevicePath;
 | |
|   Ehc->OriginalPciAttributes = OriginalPciAttributes;
 | |
| 
 | |
|   InitializeListHead (&Ehc->AsyncIntTransfers);
 | |
| 
 | |
|   Ehc->HcStructParams = EhcReadCapRegister (Ehc, EHC_HCSPARAMS_OFFSET);
 | |
|   Ehc->HcCapParams    = EhcReadCapRegister (Ehc, EHC_HCCPARAMS_OFFSET);
 | |
|   Ehc->CapLen         = EhcReadCapRegister (Ehc, EHC_CAPLENGTH_OFFSET) & 0x0FF;
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "EhcCreateUsb2Hc: capability length %d\n", Ehc->CapLen));
 | |
| 
 | |
|   //
 | |
|   // EHCI Controllers with a CapLen of 0 are ignored.
 | |
|   //
 | |
|   if (Ehc->CapLen == 0) {
 | |
|     gBS->FreePool (Ehc);
 | |
|     return NULL;
 | |
|   }
 | |
|   
 | |
|   EhcGetUsbDebugPortInfo (Ehc);
 | |
| 
 | |
|   //
 | |
|   // Create AsyncRequest Polling Timer
 | |
|   //
 | |
|   Status = gBS->CreateEvent (
 | |
|                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
 | |
|                   TPL_NOTIFY,
 | |
|                   EhcMonitorAsyncRequests,
 | |
|                   Ehc,
 | |
|                   &Ehc->PollTimer
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->FreePool (Ehc);
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   return Ehc;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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
 | |
| EhcExitBootService (
 | |
|   EFI_EVENT                      Event,
 | |
|   VOID                           *Context
 | |
|   )
 | |
| 
 | |
| {
 | |
|   USB2_HC_DEV   *Ehc;
 | |
| 
 | |
|   Ehc = (USB2_HC_DEV *) Context;
 | |
| 
 | |
|   //
 | |
|   // Reset the Host Controller
 | |
|   //
 | |
|   EhcResetHC (Ehc, EHC_RESET_TIMEOUT);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Starting the Usb EHCI Driver.
 | |
| 
 | |
|   @param  This                 Protocol instance pointer.
 | |
|   @param  Controller           Handle of device to test.
 | |
|   @param  RemainingDevicePath  Not used.
 | |
| 
 | |
|   @return EFI_SUCCESS          supports this device.
 | |
|   @return EFI_UNSUPPORTED      do not support this device.
 | |
|   @return EFI_DEVICE_ERROR     cannot be started due to device Error.
 | |
|   @return EFI_OUT_OF_RESOURCES cannot allocate resources.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcDriverBindingStart (
 | |
|   IN EFI_DRIVER_BINDING_PROTOCOL *This,
 | |
|   IN EFI_HANDLE                  Controller,
 | |
|   IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS              Status;
 | |
|   USB2_HC_DEV             *Ehc;
 | |
|   EFI_PCI_IO_PROTOCOL     *PciIo;
 | |
|   EFI_PCI_IO_PROTOCOL     *Instance;
 | |
|   UINT64                  Supports;
 | |
|   UINT64                  OriginalPciAttributes;
 | |
|   BOOLEAN                 PciAttributesSaved;
 | |
|   USB_CLASSC              UsbClassCReg;
 | |
|   EFI_HANDLE              *HandleBuffer;
 | |
|   UINTN                   NumberOfHandles;
 | |
|   UINTN                   Index;
 | |
|   UINTN                   CompanionSegmentNumber;
 | |
|   UINTN                   CompanionBusNumber;
 | |
|   UINTN                   CompanionDeviceNumber;
 | |
|   UINTN                   CompanionFunctionNumber;
 | |
|   UINTN                   EhciSegmentNumber;
 | |
|   UINTN                   EhciBusNumber;
 | |
|   UINTN                   EhciDeviceNumber;
 | |
|   UINTN                   EhciFunctionNumber;
 | |
|   UINT32                  State;
 | |
|   EFI_DEVICE_PATH_PROTOCOL  *HcDevicePath;
 | |
| 
 | |
|   //
 | |
|   // Open the PciIo Protocol, then enable the USB host controller
 | |
|   //
 | |
|   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;
 | |
| 
 | |
|   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)) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to enable controller\n"));
 | |
|     goto CLOSE_PCIIO;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get the Pci device class code.
 | |
|   //
 | |
|   Status = PciIo->Pci.Read (
 | |
|                         PciIo,
 | |
|                         EfiPciIoWidthUint8,
 | |
|                         PCI_CLASSCODE_OFFSET,
 | |
|                         sizeof (USB_CLASSC) / sizeof (UINT8),
 | |
|                         &UsbClassCReg
 | |
|                         );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     Status = EFI_UNSUPPORTED;
 | |
|     goto CLOSE_PCIIO;
 | |
|   }
 | |
|   //
 | |
|   // Determine if the device is UHCI or OHCI host controller or not. If yes, then find out the 
 | |
|   // companion usb ehci host controller and force EHCI driver get attached to it before
 | |
|   // UHCI or OHCI driver attaches to UHCI or OHCI host controller.
 | |
|   //
 | |
|   if ((UsbClassCReg.ProgInterface == PCI_IF_UHCI || UsbClassCReg.ProgInterface == PCI_IF_OHCI) &&
 | |
|        (UsbClassCReg.BaseCode == PCI_CLASS_SERIAL) && 
 | |
|        (UsbClassCReg.SubClassCode == PCI_CLASS_SERIAL_USB)) {
 | |
|     Status = PciIo->GetLocation (
 | |
|                     PciIo,
 | |
|                     &CompanionSegmentNumber,
 | |
|                     &CompanionBusNumber,
 | |
|                     &CompanionDeviceNumber,
 | |
|                     &CompanionFunctionNumber
 | |
|                     );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto CLOSE_PCIIO;
 | |
|     }
 | |
| 
 | |
|     Status = gBS->LocateHandleBuffer (
 | |
|                     ByProtocol,
 | |
|                     &gEfiPciIoProtocolGuid,
 | |
|                     NULL,
 | |
|                     &NumberOfHandles,
 | |
|                     &HandleBuffer
 | |
|                     );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto CLOSE_PCIIO;
 | |
|     }
 | |
| 
 | |
|     for (Index = 0; Index < NumberOfHandles; Index++) {
 | |
|       //
 | |
|       // Get the device path on this handle
 | |
|       //
 | |
|       Status = gBS->HandleProtocol (
 | |
|                     HandleBuffer[Index],
 | |
|                     &gEfiPciIoProtocolGuid,
 | |
|                     (VOID **)&Instance
 | |
|                     );
 | |
|       ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|       Status = Instance->Pci.Read (
 | |
|                     Instance,
 | |
|                     EfiPciIoWidthUint8,
 | |
|                     PCI_CLASSCODE_OFFSET,
 | |
|                     sizeof (USB_CLASSC) / sizeof (UINT8),
 | |
|                     &UsbClassCReg
 | |
|                     );
 | |
| 
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         Status = EFI_UNSUPPORTED;
 | |
|         goto CLOSE_PCIIO;
 | |
|       }
 | |
| 
 | |
|       if ((UsbClassCReg.ProgInterface == PCI_IF_EHCI) &&
 | |
|            (UsbClassCReg.BaseCode == PCI_CLASS_SERIAL) && 
 | |
|            (UsbClassCReg.SubClassCode == PCI_CLASS_SERIAL_USB)) {
 | |
|         Status = Instance->GetLocation (
 | |
|                     Instance,
 | |
|                     &EhciSegmentNumber,
 | |
|                     &EhciBusNumber,
 | |
|                     &EhciDeviceNumber,
 | |
|                     &EhciFunctionNumber
 | |
|                     );
 | |
|         if (EFI_ERROR (Status)) {
 | |
|           goto CLOSE_PCIIO;
 | |
|         }
 | |
|         //
 | |
|         // Currently, the judgment on the companion usb host controller is through the
 | |
|         // same bus number, which may vary on different platform.
 | |
|         //
 | |
|         if (EhciBusNumber == CompanionBusNumber) {
 | |
|           gBS->CloseProtocol (
 | |
|                     Controller,
 | |
|                     &gEfiPciIoProtocolGuid,
 | |
|                     This->DriverBindingHandle,
 | |
|                     Controller
 | |
|                     );
 | |
|           EhcDriverBindingStart(This, HandleBuffer[Index], NULL);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     Status = EFI_NOT_FOUND;
 | |
|     goto CLOSE_PCIIO;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create then install USB2_HC_PROTOCOL
 | |
|   //
 | |
|   Ehc = EhcCreateUsb2Hc (PciIo, HcDevicePath, OriginalPciAttributes);
 | |
| 
 | |
|   if (Ehc == NULL) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to create USB2_HC\n"));
 | |
| 
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto CLOSE_PCIIO;
 | |
|   }
 | |
| 
 | |
|   Status = gBS->InstallProtocolInterface (
 | |
|                   &Controller,
 | |
|                   &gEfiUsb2HcProtocolGuid,
 | |
|                   EFI_NATIVE_INTERFACE,
 | |
|                   &Ehc->Usb2Hc
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to install USB2_HC Protocol\n"));
 | |
|     goto FREE_POOL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Robustnesss improvement such as for Duet platform
 | |
|   // Default is not required.
 | |
|   //
 | |
|   if (FeaturePcdGet (PcdTurnOffUsbLegacySupport)) {
 | |
|     EhcClearLegacySupport (Ehc);
 | |
|   }
 | |
| 
 | |
|   if (Ehc->DebugPortNum != 0) {
 | |
|     State = EhcReadDbgRegister(Ehc, 0);
 | |
|     if ((State & (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) != (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) {
 | |
|       EhcResetHC (Ehc, EHC_RESET_TIMEOUT);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Status = EhcInitHC (Ehc);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to init host controller\n"));
 | |
|     goto UNINSTALL_USBHC;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Start the asynchronous interrupt monitor
 | |
|   //
 | |
|   Status = gBS->SetTimer (Ehc->PollTimer, TimerPeriodic, EHC_ASYNC_POLL_INTERVAL);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to start async interrupt monitor\n"));
 | |
| 
 | |
|     EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT);
 | |
|     goto UNINSTALL_USBHC;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create event to stop the HC when exit boot service.
 | |
|   //
 | |
|   Status = gBS->CreateEventEx (
 | |
|                   EVT_NOTIFY_SIGNAL,
 | |
|                   TPL_NOTIFY,
 | |
|                   EhcExitBootService,
 | |
|                   Ehc,
 | |
|                   &gEfiEventExitBootServicesGuid,
 | |
|                   &Ehc->ExitBootServiceEvent
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto UNINSTALL_USBHC;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Install the component name protocol, don't fail the start
 | |
|   // because of something for display.
 | |
|   //
 | |
|   AddUnicodeString2 (
 | |
|     "eng",
 | |
|     gEhciComponentName.SupportedLanguages,
 | |
|     &Ehc->ControllerNameTable,
 | |
|     L"Enhanced Host Controller (USB 2.0)",
 | |
|     TRUE
 | |
|     );
 | |
|   AddUnicodeString2 (
 | |
|     "en",
 | |
|     gEhciComponentName2.SupportedLanguages,
 | |
|     &Ehc->ControllerNameTable,
 | |
|     L"Enhanced Host Controller (USB 2.0)",
 | |
|     FALSE
 | |
|     );
 | |
| 
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "EhcDriverBindingStart: EHCI started for controller @ %p\n", Controller));
 | |
|   return EFI_SUCCESS;
 | |
| 
 | |
| UNINSTALL_USBHC:
 | |
|   gBS->UninstallProtocolInterface (
 | |
|          Controller,
 | |
|          &gEfiUsb2HcProtocolGuid,
 | |
|          &Ehc->Usb2Hc
 | |
|          );
 | |
| 
 | |
| FREE_POOL:
 | |
|   EhcFreeSched (Ehc);
 | |
|   gBS->CloseEvent (Ehc->PollTimer);
 | |
|   gBS->FreePool (Ehc);
 | |
| 
 | |
| 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 stoping any child handles
 | |
|   created by this driver.
 | |
| 
 | |
|   @param  This                 Protocol instance pointer.
 | |
|   @param  Controller           Handle of device to stop driver on.
 | |
|   @param  NumberOfChildren     Number of Children in the ChildHandleBuffer.
 | |
|   @param  ChildHandleBuffer    List of handles for the children we need to stop.
 | |
| 
 | |
|   @return EFI_SUCCESS          Success.
 | |
|   @return EFI_DEVICE_ERROR     Fail.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EhcDriverBindingStop (
 | |
|   IN EFI_DRIVER_BINDING_PROTOCOL *This,
 | |
|   IN EFI_HANDLE                  Controller,
 | |
|   IN UINTN                       NumberOfChildren,
 | |
|   IN EFI_HANDLE                  *ChildHandleBuffer
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   EFI_USB2_HC_PROTOCOL  *Usb2Hc;
 | |
|   EFI_PCI_IO_PROTOCOL   *PciIo;
 | |
|   USB2_HC_DEV           *Ehc;
 | |
| 
 | |
|   //
 | |
|   // Test whether the Controller handler passed in is a valid
 | |
|   // Usb controller handle that should be supported, if not,
 | |
|   // return the error status directly
 | |
|   //
 | |
|   Status = gBS->OpenProtocol (
 | |
|                   Controller,
 | |
|                   &gEfiUsb2HcProtocolGuid,
 | |
|                   (VOID **) &Usb2Hc,
 | |
|                   This->DriverBindingHandle,
 | |
|                   Controller,
 | |
|                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Ehc   = EHC_FROM_THIS (Usb2Hc);
 | |
|   PciIo = Ehc->PciIo;
 | |
| 
 | |
|   Status = gBS->UninstallProtocolInterface (
 | |
|                   Controller,
 | |
|                   &gEfiUsb2HcProtocolGuid,
 | |
|                   Usb2Hc
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Stop AsyncRequest Polling timer then stop the EHCI driver
 | |
|   // and uninstall the EHCI protocl.
 | |
|   //
 | |
|   gBS->SetTimer (Ehc->PollTimer, TimerCancel, EHC_ASYNC_POLL_INTERVAL);
 | |
|   EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT);
 | |
| 
 | |
|   if (Ehc->PollTimer != NULL) {
 | |
|     gBS->CloseEvent (Ehc->PollTimer);
 | |
|   }
 | |
| 
 | |
|   if (Ehc->ExitBootServiceEvent != NULL) {
 | |
|     gBS->CloseEvent (Ehc->ExitBootServiceEvent);
 | |
|   }
 | |
| 
 | |
|   EhcFreeSched (Ehc);
 | |
| 
 | |
|   if (Ehc->ControllerNameTable != NULL) {
 | |
|     FreeUnicodeStringTable (Ehc->ControllerNameTable);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Disable routing of all ports to EHCI controller, so all ports are 
 | |
|   // routed back to the UHCI or OHCI controller.
 | |
|   //
 | |
|   EhcClearOpRegBit (Ehc, EHC_CONFIG_FLAG_OFFSET, CONFIGFLAG_ROUTE_EHC);
 | |
| 
 | |
|   //
 | |
|   // Restore original PCI attributes
 | |
|   //
 | |
|   PciIo->Attributes (
 | |
|                   PciIo,
 | |
|                   EfiPciIoAttributeOperationSet,
 | |
|                   Ehc->OriginalPciAttributes,
 | |
|                   NULL
 | |
|                   );
 | |
| 
 | |
|   gBS->CloseProtocol (
 | |
|          Controller,
 | |
|          &gEfiPciIoProtocolGuid,
 | |
|          This->DriverBindingHandle,
 | |
|          Controller
 | |
|          );
 | |
| 
 | |
|   FreePool (Ehc);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 |