Improve the formatting of DEBUG messages in UsbBusDxe by adding a hyphen to separate the EFI_STATUS code. Signed-off-by: Rebecca Cran <rebecca@bsdio.com> Reviewed-by: Hao A Wu <hao.a.wu@intel.com>
		
			
				
	
	
		
			1271 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1271 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|     Unified interface for RootHub and Hub.
 | |
| 
 | |
| Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
| SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "UsbBus.h"
 | |
| 
 | |
| //
 | |
| // Array that maps the change bit to feature value which is
 | |
| // used to clear these change bit. USB HUB API will clear
 | |
| // these change bit automatically. For non-root hub, these
 | |
| // bits determine whether hub will report the port in changed
 | |
| // bit maps.
 | |
| //
 | |
| USB_CHANGE_FEATURE_MAP  mHubFeatureMap[] = {
 | |
|   { USB_PORT_STAT_C_CONNECTION,  EfiUsbPortConnectChange     },
 | |
|   { USB_PORT_STAT_C_ENABLE,      EfiUsbPortEnableChange      },
 | |
|   { USB_PORT_STAT_C_SUSPEND,     EfiUsbPortSuspendChange     },
 | |
|   { USB_PORT_STAT_C_OVERCURRENT, EfiUsbPortOverCurrentChange },
 | |
|   { USB_PORT_STAT_C_RESET,       EfiUsbPortResetChange       }
 | |
| };
 | |
| 
 | |
| USB_CHANGE_FEATURE_MAP  mRootHubFeatureMap[] = {
 | |
|   { USB_PORT_STAT_C_CONNECTION,  EfiUsbPortConnectChange     },
 | |
|   { USB_PORT_STAT_C_ENABLE,      EfiUsbPortEnableChange      },
 | |
|   { USB_PORT_STAT_C_SUSPEND,     EfiUsbPortSuspendChange     },
 | |
|   { USB_PORT_STAT_C_OVERCURRENT, EfiUsbPortOverCurrentChange },
 | |
|   { USB_PORT_STAT_C_RESET,       EfiUsbPortResetChange       },
 | |
| };
 | |
| 
 | |
| //
 | |
| // USB hub class specific requests. Although USB hub
 | |
| // is related to an interface, these requests are sent
 | |
| // to the control endpoint of the device.
 | |
| //
 | |
| 
 | |
| /**
 | |
|   USB hub control transfer to set the hub depth.
 | |
| 
 | |
|   @param  HubDev                The device of the hub.
 | |
|   @param  Depth                 The depth to set.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Depth of the hub is set.
 | |
|   @retval Others                Failed to set the depth.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubCtrlSetHubDepth (
 | |
|   IN  USB_DEVICE  *HubDev,
 | |
|   IN  UINT16      Depth
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = UsbCtrlRequest (
 | |
|              HubDev,
 | |
|              EfiUsbNoData,
 | |
|              USB_REQ_TYPE_CLASS,
 | |
|              USB_HUB_TARGET_HUB,
 | |
|              USB_HUB_REQ_SET_DEPTH,
 | |
|              Depth,
 | |
|              0,
 | |
|              NULL,
 | |
|              0
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   USB hub control transfer to clear the hub feature.
 | |
| 
 | |
|   @param  HubDev                The device of the hub.
 | |
|   @param  Feature               The feature to clear.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Feature of the hub is cleared.
 | |
|   @retval Others                Failed to clear the feature.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubCtrlClearHubFeature (
 | |
|   IN USB_DEVICE  *HubDev,
 | |
|   IN UINT16      Feature
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = UsbCtrlRequest (
 | |
|              HubDev,
 | |
|              EfiUsbNoData,
 | |
|              USB_REQ_TYPE_CLASS,
 | |
|              USB_HUB_TARGET_HUB,
 | |
|              USB_HUB_REQ_CLEAR_FEATURE,
 | |
|              Feature,
 | |
|              0,
 | |
|              NULL,
 | |
|              0
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Clear the feature of the device's port.
 | |
| 
 | |
|   @param  HubDev                The hub device.
 | |
|   @param  Port                  The port to clear feature.
 | |
|   @param  Feature               The feature to clear.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The feature of the port is cleared.
 | |
|   @retval Others                Failed to clear the feature.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubCtrlClearPortFeature (
 | |
|   IN USB_DEVICE  *HubDev,
 | |
|   IN UINT8       Port,
 | |
|   IN UINT16      Feature
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   //
 | |
|   // In USB bus, all the port index starts from 0. But HUB
 | |
|   // indexes its port from 1. So, port number is added one.
 | |
|   //
 | |
|   Status = UsbCtrlRequest (
 | |
|              HubDev,
 | |
|              EfiUsbNoData,
 | |
|              USB_REQ_TYPE_CLASS,
 | |
|              USB_HUB_TARGET_PORT,
 | |
|              USB_HUB_REQ_CLEAR_FEATURE,
 | |
|              Feature,
 | |
|              (UINT16)(Port + 1),
 | |
|              NULL,
 | |
|              0
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Clear the transaction translate buffer if full/low
 | |
|   speed control/bulk transfer failed and the transfer
 | |
|   uses this hub as translator.Remember to clear the TT
 | |
|   buffer of transaction translator, not that of the
 | |
|   parent.
 | |
| 
 | |
|   @param  HubDev                The hub device.
 | |
|   @param  Port                  The port of the hub.
 | |
|   @param  DevAddr               Address of the failed transaction.
 | |
|   @param  EpNum                 The endpoint number of the failed transaction.
 | |
|   @param  EpType                The type of failed transaction.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The TT buffer is cleared.
 | |
|   @retval Others                Failed to clear the TT buffer.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubCtrlClearTTBuffer (
 | |
|   IN USB_DEVICE  *HubDev,
 | |
|   IN UINT8       Port,
 | |
|   IN UINT16      DevAddr,
 | |
|   IN UINT16      EpNum,
 | |
|   IN UINT16      EpType
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT16      Value;
 | |
| 
 | |
|   //
 | |
|   // Check USB2.0 spec page 424 for wValue's encoding
 | |
|   //
 | |
|   Value = (UINT16)((EpNum & 0x0F) | (DevAddr << 4) |
 | |
|                    ((EpType & 0x03) << 11) | ((EpNum & 0x80) << 15));
 | |
| 
 | |
|   Status = UsbCtrlRequest (
 | |
|              HubDev,
 | |
|              EfiUsbNoData,
 | |
|              USB_REQ_TYPE_CLASS,
 | |
|              USB_HUB_TARGET_PORT,
 | |
|              USB_HUB_REQ_CLEAR_TT,
 | |
|              Value,
 | |
|              (UINT16)(Port + 1),
 | |
|              NULL,
 | |
|              0
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Usb hub control transfer to get the (super speed) hub descriptor.
 | |
| 
 | |
|   @param  HubDev                The hub device.
 | |
|   @param  Buf                   The buffer to hold the descriptor.
 | |
|   @param  Len                   The length to retrieve.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The hub descriptor is retrieved.
 | |
|   @retval Others                Failed to retrieve the hub descriptor.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubCtrlGetHubDesc (
 | |
|   IN  USB_DEVICE  *HubDev,
 | |
|   OUT VOID        *Buf,
 | |
|   IN  UINTN       Len
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       DescType;
 | |
| 
 | |
|   DescType = (HubDev->Speed == EFI_USB_SPEED_SUPER) ?
 | |
|              USB_DESC_TYPE_HUB_SUPER_SPEED :
 | |
|              USB_DESC_TYPE_HUB;
 | |
| 
 | |
|   Status = UsbCtrlRequest (
 | |
|              HubDev,
 | |
|              EfiUsbDataIn,
 | |
|              USB_REQ_TYPE_CLASS,
 | |
|              USB_HUB_TARGET_HUB,
 | |
|              USB_HUB_REQ_GET_DESC,
 | |
|              (UINT16)(DescType << 8),
 | |
|              0,
 | |
|              Buf,
 | |
|              Len
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Usb hub control transfer to get the hub status.
 | |
| 
 | |
|   @param  HubDev                The hub device.
 | |
|   @param  State                 The variable to return the status.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The hub status is returned in State.
 | |
|   @retval Others                Failed to get the hub status.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubCtrlGetHubStatus (
 | |
|   IN  USB_DEVICE  *HubDev,
 | |
|   OUT UINT32      *State
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = UsbCtrlRequest (
 | |
|              HubDev,
 | |
|              EfiUsbDataIn,
 | |
|              USB_REQ_TYPE_CLASS,
 | |
|              USB_HUB_TARGET_HUB,
 | |
|              USB_HUB_REQ_GET_STATUS,
 | |
|              0,
 | |
|              0,
 | |
|              State,
 | |
|              4
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Usb hub control transfer to get the port status.
 | |
| 
 | |
|   @param  HubDev                The hub device.
 | |
|   @param  Port                  The port of the hub.
 | |
|   @param  State                 Variable to return the hub port state.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The port state is returned in State.
 | |
|   @retval Others                Failed to retrieve the port state.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubCtrlGetPortStatus (
 | |
|   IN  USB_DEVICE  *HubDev,
 | |
|   IN  UINT8       Port,
 | |
|   OUT VOID        *State
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   //
 | |
|   // In USB bus, all the port index starts from 0. But HUB
 | |
|   // indexes its port from 1. So, port number is added one.
 | |
|   // No need to convert the hub bit to UEFI definition, they
 | |
|   // are the same
 | |
|   //
 | |
|   Status = UsbCtrlRequest (
 | |
|              HubDev,
 | |
|              EfiUsbDataIn,
 | |
|              USB_REQ_TYPE_CLASS,
 | |
|              USB_HUB_TARGET_PORT,
 | |
|              USB_HUB_REQ_GET_STATUS,
 | |
|              0,
 | |
|              (UINT16)(Port + 1),
 | |
|              State,
 | |
|              4
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Usb hub control transfer to set the port feature.
 | |
| 
 | |
|   @param  HubDev                The Usb hub device.
 | |
|   @param  Port                  The Usb port to set feature for.
 | |
|   @param  Feature               The feature to set.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The feature is set for the port.
 | |
|   @retval Others                Failed to set the feature.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubCtrlSetPortFeature (
 | |
|   IN USB_DEVICE  *HubDev,
 | |
|   IN UINT8       Port,
 | |
|   IN UINT8       Feature
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   //
 | |
|   // In USB bus, all the port index starts from 0. But HUB
 | |
|   // indexes its port from 1. So, port number is added one.
 | |
|   //
 | |
|   Status = UsbCtrlRequest (
 | |
|              HubDev,
 | |
|              EfiUsbNoData,
 | |
|              USB_REQ_TYPE_CLASS,
 | |
|              USB_HUB_TARGET_PORT,
 | |
|              USB_HUB_REQ_SET_FEATURE,
 | |
|              Feature,
 | |
|              (UINT16)(Port + 1),
 | |
|              NULL,
 | |
|              0
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Read the whole usb hub descriptor. It is necessary
 | |
|   to do it in two steps because hub descriptor is of
 | |
|   variable length.
 | |
| 
 | |
|   @param  HubDev                The hub device.
 | |
|   @param  HubDesc               The variable to return the descriptor.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The hub descriptor is read.
 | |
|   @retval Others                Failed to read the hub descriptor.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubReadDesc (
 | |
|   IN  USB_DEVICE              *HubDev,
 | |
|   OUT EFI_USB_HUB_DESCRIPTOR  *HubDesc
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   //
 | |
|   // First get the hub descriptor length
 | |
|   //
 | |
|   Status = UsbHubCtrlGetHubDesc (HubDev, HubDesc, 2);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get the whole hub descriptor
 | |
|   //
 | |
|   return UsbHubCtrlGetHubDesc (HubDev, HubDesc, HubDesc->Length);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Ack the hub change bits. If these bits are not ACKed, Hub will
 | |
|   always return changed bit map from its interrupt endpoint.
 | |
| 
 | |
|   @param  HubDev                The hub device.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The hub change status is ACKed.
 | |
|   @retval Others                Failed to ACK the hub status.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubAckHubStatus (
 | |
|   IN  USB_DEVICE  *HubDev
 | |
|   )
 | |
| {
 | |
|   EFI_USB_PORT_STATUS  HubState;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   Status = UsbHubCtrlGetHubStatus (HubDev, (UINT32 *)&HubState);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if (USB_BIT_IS_SET (HubState.PortChangeStatus, USB_HUB_STAT_C_LOCAL_POWER)) {
 | |
|     UsbHubCtrlClearHubFeature (HubDev, USB_HUB_C_HUB_LOCAL_POWER);
 | |
|   }
 | |
| 
 | |
|   if (USB_BIT_IS_SET (HubState.PortChangeStatus, USB_HUB_STAT_C_OVER_CURRENT)) {
 | |
|     UsbHubCtrlClearHubFeature (HubDev, USB_HUB_C_HUB_OVER_CURRENT);
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Test whether the interface is a hub interface.
 | |
| 
 | |
|   @param  UsbIf                 The interface to test.
 | |
| 
 | |
|   @retval TRUE                  The interface is a hub interface.
 | |
|   @retval FALSE                 The interface isn't a hub interface.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| UsbIsHubInterface (
 | |
|   IN USB_INTERFACE  *UsbIf
 | |
|   )
 | |
| {
 | |
|   EFI_USB_INTERFACE_DESCRIPTOR  *Setting;
 | |
| 
 | |
|   //
 | |
|   // If the hub is a high-speed hub with multiple TT,
 | |
|   // the hub will has a default setting of single TT.
 | |
|   //
 | |
|   Setting = &UsbIf->IfSetting->Desc;
 | |
| 
 | |
|   if ((Setting->InterfaceClass == USB_HUB_CLASS_CODE) &&
 | |
|       (Setting->InterfaceSubClass == USB_HUB_SUBCLASS_CODE))
 | |
|   {
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The callback function to the USB hub status change
 | |
|   interrupt endpoint. It is called periodically by
 | |
|   the underlying host controller.
 | |
| 
 | |
|   @param  Data                  The data read.
 | |
|   @param  DataLength            The length of the data read.
 | |
|   @param  Context               The context.
 | |
|   @param  Result                The result of the last interrupt transfer.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The process is OK.
 | |
|   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| UsbOnHubInterrupt (
 | |
|   IN  VOID    *Data,
 | |
|   IN  UINTN   DataLength,
 | |
|   IN  VOID    *Context,
 | |
|   IN  UINT32  Result
 | |
|   )
 | |
| {
 | |
|   USB_INTERFACE                *HubIf;
 | |
|   EFI_USB_IO_PROTOCOL          *UsbIo;
 | |
|   EFI_USB_ENDPOINT_DESCRIPTOR  *EpDesc;
 | |
|   EFI_STATUS                   Status;
 | |
| 
 | |
|   HubIf  = (USB_INTERFACE *)Context;
 | |
|   UsbIo  = &(HubIf->UsbIo);
 | |
|   EpDesc = &(HubIf->HubEp->Desc);
 | |
| 
 | |
|   if (Result != EFI_USB_NOERROR) {
 | |
|     //
 | |
|     // If endpoint is stalled, clear the stall. Use UsbIo to access
 | |
|     // the control transfer so internal status are maintained.
 | |
|     //
 | |
|     if (USB_BIT_IS_SET (Result, EFI_USB_ERR_STALL)) {
 | |
|       UsbIoClearFeature (
 | |
|         UsbIo,
 | |
|         USB_TARGET_ENDPOINT,
 | |
|         USB_FEATURE_ENDPOINT_HALT,
 | |
|         EpDesc->EndpointAddress
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Delete and submit a new async interrupt
 | |
|     //
 | |
|     Status = UsbIo->UsbAsyncInterruptTransfer (
 | |
|                       UsbIo,
 | |
|                       EpDesc->EndpointAddress,
 | |
|                       FALSE,
 | |
|                       0,
 | |
|                       0,
 | |
|                       NULL,
 | |
|                       NULL
 | |
|                       );
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       DEBUG ((DEBUG_ERROR, "UsbOnHubInterrupt: failed to remove async transfer - %r\n", Status));
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     Status = UsbIo->UsbAsyncInterruptTransfer (
 | |
|                       UsbIo,
 | |
|                       EpDesc->EndpointAddress,
 | |
|                       TRUE,
 | |
|                       USB_HUB_POLL_INTERVAL,
 | |
|                       HubIf->NumOfPort / 8 + 1,
 | |
|                       UsbOnHubInterrupt,
 | |
|                       HubIf
 | |
|                       );
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       DEBUG ((DEBUG_ERROR, "UsbOnHubInterrupt: failed to submit new async transfer - %r\n", Status));
 | |
|     }
 | |
| 
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if ((DataLength == 0) || (Data == NULL)) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // OK, actually something is changed, save the change map
 | |
|   // then signal the HUB to do enumeration. This is a good
 | |
|   // practise since UsbOnHubInterrupt is called in the context
 | |
|   // of host controller's AsyncInterrupt monitor.
 | |
|   //
 | |
|   HubIf->ChangeMap = AllocateZeroPool (DataLength);
 | |
| 
 | |
|   if (HubIf->ChangeMap == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   CopyMem (HubIf->ChangeMap, Data, DataLength);
 | |
|   gBS->SignalEvent (HubIf->HubNotify);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize the device for a non-root hub.
 | |
| 
 | |
|   @param  HubIf                 The USB hub interface.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The hub is initialized.
 | |
|   @retval EFI_DEVICE_ERROR      Failed to initialize the hub.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubInit (
 | |
|   IN USB_INTERFACE  *HubIf
 | |
|   )
 | |
| {
 | |
|   UINT8                   HubDescBuffer[256];
 | |
|   EFI_USB_HUB_DESCRIPTOR  *HubDesc;
 | |
|   USB_ENDPOINT_DESC       *EpDesc;
 | |
|   USB_INTERFACE_SETTING   *Setting;
 | |
|   EFI_USB_IO_PROTOCOL     *UsbIo;
 | |
|   USB_DEVICE              *HubDev;
 | |
|   EFI_STATUS              Status;
 | |
|   UINT8                   Index;
 | |
|   UINT8                   NumEndpoints;
 | |
|   UINT16                  Depth;
 | |
| 
 | |
|   //
 | |
|   // Locate the interrupt endpoint for port change map
 | |
|   //
 | |
|   HubIf->IsHub = FALSE;
 | |
|   Setting      = HubIf->IfSetting;
 | |
|   HubDev       = HubIf->Device;
 | |
|   EpDesc       = NULL;
 | |
|   NumEndpoints = Setting->Desc.NumEndpoints;
 | |
| 
 | |
|   for (Index = 0; Index < NumEndpoints; Index++) {
 | |
|     ASSERT ((Setting->Endpoints != NULL) && (Setting->Endpoints[Index] != NULL));
 | |
| 
 | |
|     EpDesc = Setting->Endpoints[Index];
 | |
| 
 | |
|     if (USB_BIT_IS_SET (EpDesc->Desc.EndpointAddress, USB_ENDPOINT_DIR_IN) &&
 | |
|         (USB_ENDPOINT_TYPE (&EpDesc->Desc) == USB_ENDPOINT_INTERRUPT))
 | |
|     {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (Index == NumEndpoints) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbHubInit: no interrupt endpoint found for hub %d\n", HubDev->Address));
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // The length field of descriptor is UINT8 type, so the buffer
 | |
|   // with 256 bytes is enough to hold the descriptor data.
 | |
|   //
 | |
|   HubDesc = (EFI_USB_HUB_DESCRIPTOR *)HubDescBuffer;
 | |
|   Status  = UsbHubReadDesc (HubDev, HubDesc);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbHubInit: failed to read HUB descriptor - %r\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   HubIf->NumOfPort = HubDesc->NumPorts;
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "UsbHubInit: hub %d has %d ports\n", HubDev->Address, HubIf->NumOfPort));
 | |
| 
 | |
|   //
 | |
|   // OK, set IsHub to TRUE. Now usb bus can handle this device
 | |
|   // as a working HUB. If failed earlier, bus driver will not
 | |
|   // recognize it as a hub. Other parts of the bus should be able
 | |
|   // to work.
 | |
|   //
 | |
|   HubIf->IsHub  = TRUE;
 | |
|   HubIf->HubApi = &mUsbHubApi;
 | |
|   HubIf->HubEp  = EpDesc;
 | |
| 
 | |
|   if (HubIf->Device->Speed == EFI_USB_SPEED_SUPER) {
 | |
|     Depth = (UINT16)(HubIf->Device->Tier - 1);
 | |
|     DEBUG ((DEBUG_INFO, "UsbHubInit: Set Hub Depth as 0x%x\n", Depth));
 | |
|     UsbHubCtrlSetHubDepth (HubIf->Device, Depth);
 | |
| 
 | |
|     for (Index = 0; Index < HubDesc->NumPorts; Index++) {
 | |
|       UsbHubCtrlSetPortFeature (HubIf->Device, Index, USB_HUB_PORT_REMOTE_WAKE_MASK);
 | |
|     }
 | |
|   } else {
 | |
|     //
 | |
|     // Feed power to all the hub ports. It should be ok
 | |
|     // for both gang/individual powered hubs.
 | |
|     //
 | |
|     for (Index = 0; Index < HubDesc->NumPorts; Index++) {
 | |
|       UsbHubCtrlSetPortFeature (HubIf->Device, Index, (EFI_USB_PORT_FEATURE)USB_HUB_PORT_POWER);
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Update for the usb hub has no power on delay requirement
 | |
|     //
 | |
|     if (HubDesc->PwrOn2PwrGood > 0) {
 | |
|       gBS->Stall (HubDesc->PwrOn2PwrGood * USB_SET_PORT_POWER_STALL);
 | |
|     }
 | |
| 
 | |
|     UsbHubAckHubStatus (HubIf->Device);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create an event to enumerate the hub's port. On
 | |
|   //
 | |
|   Status = gBS->CreateEvent (
 | |
|                   EVT_NOTIFY_SIGNAL,
 | |
|                   TPL_CALLBACK,
 | |
|                   UsbHubEnumeration,
 | |
|                   HubIf,
 | |
|                   &HubIf->HubNotify
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "UsbHubInit: failed to create signal for hub %d - %r\n",
 | |
|       HubDev->Address,
 | |
|       Status
 | |
|       ));
 | |
| 
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create AsyncInterrupt to query hub port change endpoint
 | |
|   // periodically. If the hub ports are changed, hub will return
 | |
|   // changed port map from the interrupt endpoint. The port map
 | |
|   // must be able to hold (HubIf->NumOfPort + 1) bits (one bit for
 | |
|   // host change status).
 | |
|   //
 | |
|   UsbIo  = &HubIf->UsbIo;
 | |
|   Status = UsbIo->UsbAsyncInterruptTransfer (
 | |
|                     UsbIo,
 | |
|                     EpDesc->Desc.EndpointAddress,
 | |
|                     TRUE,
 | |
|                     USB_HUB_POLL_INTERVAL,
 | |
|                     HubIf->NumOfPort / 8 + 1,
 | |
|                     UsbOnHubInterrupt,
 | |
|                     HubIf
 | |
|                     );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "UsbHubInit: failed to queue interrupt transfer for hub %d - %r\n",
 | |
|       HubDev->Address,
 | |
|       Status
 | |
|       ));
 | |
| 
 | |
|     gBS->CloseEvent (HubIf->HubNotify);
 | |
|     HubIf->HubNotify = NULL;
 | |
| 
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "UsbHubInit: hub %d initialized\n", HubDev->Address));
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get the port status. This function is required to
 | |
|   ACK the port change bits although it will return
 | |
|   the port changes in PortState. Bus enumeration code
 | |
|   doesn't need to ACK the port change bits.
 | |
| 
 | |
|   @param  HubIf                 The hub interface.
 | |
|   @param  Port                  The port of the hub to get state.
 | |
|   @param  PortState             Variable to return the port state.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The port status is successfully returned.
 | |
|   @retval Others                Failed to return the status.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubGetPortStatus (
 | |
|   IN  USB_INTERFACE        *HubIf,
 | |
|   IN  UINT8                Port,
 | |
|   OUT EFI_USB_PORT_STATUS  *PortState
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = UsbHubCtrlGetPortStatus (HubIf->Device, Port, PortState);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Clear the port change status.
 | |
| 
 | |
|   @param  HubIf                 The hub interface.
 | |
|   @param  Port                  The hub port.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| UsbHubClearPortChange (
 | |
|   IN USB_INTERFACE  *HubIf,
 | |
|   IN UINT8          Port
 | |
|   )
 | |
| {
 | |
|   EFI_USB_PORT_STATUS     PortState;
 | |
|   USB_CHANGE_FEATURE_MAP  *Map;
 | |
|   UINTN                   Index;
 | |
|   EFI_STATUS              Status;
 | |
| 
 | |
|   Status = UsbHubGetPortStatus (HubIf, Port, &PortState);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // OK, get the usb port status, now ACK the change bits.
 | |
|   // Don't return error when failed to clear the change bits.
 | |
|   // It may lead to extra port state report. USB bus should
 | |
|   // be able to handle this.
 | |
|   //
 | |
|   for (Index = 0; Index < ARRAY_SIZE (mHubFeatureMap); Index++) {
 | |
|     Map = &mHubFeatureMap[Index];
 | |
| 
 | |
|     if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) {
 | |
|       UsbHubCtrlClearPortFeature (HubIf->Device, Port, (UINT16)Map->Feature);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Function to set the port feature for non-root hub.
 | |
| 
 | |
|   @param  HubIf                 The hub interface.
 | |
|   @param  Port                  The port of the hub.
 | |
|   @param  Feature               The feature of the port to set.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The hub port feature is set.
 | |
|   @retval Others                Failed to set the port feature.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubSetPortFeature (
 | |
|   IN USB_INTERFACE         *HubIf,
 | |
|   IN UINT8                 Port,
 | |
|   IN EFI_USB_PORT_FEATURE  Feature
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = UsbHubCtrlSetPortFeature (HubIf->Device, Port, (UINT8)Feature);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Interface function to clear the port feature for non-root hub.
 | |
| 
 | |
|   @param  HubIf                 The hub interface.
 | |
|   @param  Port                  The port of the hub to clear feature for.
 | |
|   @param  Feature               The feature to clear.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The port feature is cleared.
 | |
|   @retval Others                Failed to clear the port feature.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubClearPortFeature (
 | |
|   IN USB_INTERFACE         *HubIf,
 | |
|   IN UINT8                 Port,
 | |
|   IN EFI_USB_PORT_FEATURE  Feature
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = UsbHubCtrlClearPortFeature (HubIf->Device, Port, (UINT8)Feature);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Interface function to reset the port.
 | |
| 
 | |
|   @param  HubIf                 The hub interface.
 | |
|   @param  Port                  The port to reset.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The hub port is reset.
 | |
|   @retval EFI_TIMEOUT           Failed to reset the port in time.
 | |
|   @retval Others                Failed to reset the port.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubResetPort (
 | |
|   IN USB_INTERFACE  *HubIf,
 | |
|   IN UINT8          Port
 | |
|   )
 | |
| {
 | |
|   EFI_USB_PORT_STATUS  PortState;
 | |
|   UINTN                Index;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   Status = UsbHubSetPortFeature (HubIf, Port, (EFI_USB_PORT_FEATURE)USB_HUB_PORT_RESET);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Drive the reset signal for worst 20ms. Check USB 2.0 Spec
 | |
|   // section 7.1.7.5 for timing requirements.
 | |
|   //
 | |
|   gBS->Stall (USB_SET_PORT_RESET_STALL);
 | |
| 
 | |
|   //
 | |
|   // Check USB_PORT_STAT_C_RESET bit to see if the resetting state is done.
 | |
|   //
 | |
|   ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS));
 | |
| 
 | |
|   for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) {
 | |
|     Status = UsbHubGetPortStatus (HubIf, Port, &PortState);
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     if (!EFI_ERROR (Status) &&
 | |
|         USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET))
 | |
|     {
 | |
|       gBS->Stall (USB_SET_PORT_RECOVERY_STALL);
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
| 
 | |
|     gBS->Stall (USB_WAIT_PORT_STS_CHANGE_STALL);
 | |
|   }
 | |
| 
 | |
|   return EFI_TIMEOUT;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Release the hub's control of the interface.
 | |
| 
 | |
|   @param  HubIf                 The hub interface.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The interface is release of hub control.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbHubRelease (
 | |
|   IN USB_INTERFACE  *HubIf
 | |
|   )
 | |
| {
 | |
|   EFI_USB_IO_PROTOCOL  *UsbIo;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   UsbIo  = &HubIf->UsbIo;
 | |
|   Status = UsbIo->UsbAsyncInterruptTransfer (
 | |
|                     UsbIo,
 | |
|                     HubIf->HubEp->Desc.EndpointAddress,
 | |
|                     FALSE,
 | |
|                     USB_HUB_POLL_INTERVAL,
 | |
|                     0,
 | |
|                     NULL,
 | |
|                     0
 | |
|                     );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   gBS->CloseEvent (HubIf->HubNotify);
 | |
| 
 | |
|   HubIf->IsHub     = FALSE;
 | |
|   HubIf->HubApi    = NULL;
 | |
|   HubIf->HubEp     = NULL;
 | |
|   HubIf->HubNotify = NULL;
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "UsbHubRelease: hub device %d released\n", HubIf->Device->Address));
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize the interface for root hub.
 | |
| 
 | |
|   @param  HubIf                 The root hub interface.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The interface is initialized for root hub.
 | |
|   @retval Others                Failed to initialize the hub.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbRootHubInit (
 | |
|   IN USB_INTERFACE  *HubIf
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       MaxSpeed;
 | |
|   UINT8       NumOfPort;
 | |
|   UINT8       Support64;
 | |
| 
 | |
|   Status = UsbHcGetCapability (HubIf->Device->Bus, &MaxSpeed, &NumOfPort, &Support64);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((
 | |
|     DEBUG_INFO,
 | |
|     "UsbRootHubInit: root hub %p - max speed %d, %d ports\n",
 | |
|     HubIf,
 | |
|     MaxSpeed,
 | |
|     NumOfPort
 | |
|     ));
 | |
| 
 | |
|   HubIf->IsHub     = TRUE;
 | |
|   HubIf->HubApi    = &mUsbRootHubApi;
 | |
|   HubIf->HubEp     = NULL;
 | |
|   HubIf->MaxSpeed  = MaxSpeed;
 | |
|   HubIf->NumOfPort = NumOfPort;
 | |
|   HubIf->HubNotify = NULL;
 | |
| 
 | |
|   //
 | |
|   // Create a timer to poll root hub ports periodically
 | |
|   //
 | |
|   Status = gBS->CreateEvent (
 | |
|                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
 | |
|                   TPL_CALLBACK,
 | |
|                   UsbRootHubEnumeration,
 | |
|                   HubIf,
 | |
|                   &HubIf->HubNotify
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // It should signal the event immediately here, or device detection
 | |
|   // by bus enumeration might be delayed by the timer interval.
 | |
|   //
 | |
|   gBS->SignalEvent (HubIf->HubNotify);
 | |
| 
 | |
|   Status = gBS->SetTimer (
 | |
|                   HubIf->HubNotify,
 | |
|                   TimerPeriodic,
 | |
|                   USB_ROOTHUB_POLL_INTERVAL
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->CloseEvent (HubIf->HubNotify);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get the port status. This function is required to
 | |
|   ACK the port change bits although it will return
 | |
|   the port changes in PortState. Bus enumeration code
 | |
|   doesn't need to ACK the port change bits.
 | |
| 
 | |
|   @param  HubIf                 The root hub interface.
 | |
|   @param  Port                  The root hub port to get the state.
 | |
|   @param  PortState             Variable to return the port state.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The port state is returned.
 | |
|   @retval Others                Failed to retrieve the port state.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbRootHubGetPortStatus (
 | |
|   IN  USB_INTERFACE        *HubIf,
 | |
|   IN  UINT8                Port,
 | |
|   OUT EFI_USB_PORT_STATUS  *PortState
 | |
|   )
 | |
| {
 | |
|   USB_BUS     *Bus;
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Bus    = HubIf->Device->Bus;
 | |
|   Status = UsbHcGetRootHubPortStatus (Bus, Port, PortState);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Clear the port change status.
 | |
| 
 | |
|   @param  HubIf                 The root hub interface.
 | |
|   @param  Port                  The root hub port.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| UsbRootHubClearPortChange (
 | |
|   IN USB_INTERFACE  *HubIf,
 | |
|   IN UINT8          Port
 | |
|   )
 | |
| {
 | |
|   EFI_USB_PORT_STATUS     PortState;
 | |
|   USB_CHANGE_FEATURE_MAP  *Map;
 | |
|   UINTN                   Index;
 | |
|   EFI_STATUS              Status;
 | |
| 
 | |
|   Status = UsbRootHubGetPortStatus (HubIf, Port, &PortState);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // OK, get the usb port status, now ACK the change bits.
 | |
|   // Don't return error when failed to clear the change bits.
 | |
|   // It may lead to extra port state report. USB bus should
 | |
|   // be able to handle this.
 | |
|   //
 | |
|   for (Index = 0; Index < ARRAY_SIZE (mRootHubFeatureMap); Index++) {
 | |
|     Map = &mRootHubFeatureMap[Index];
 | |
| 
 | |
|     if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) {
 | |
|       UsbHcClearRootHubPortFeature (HubIf->Device->Bus, Port, (EFI_USB_PORT_FEATURE)Map->Feature);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Set the root hub port feature.
 | |
| 
 | |
|   @param  HubIf                 The Usb hub interface.
 | |
|   @param  Port                  The hub port.
 | |
|   @param  Feature               The feature to set.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The root hub port is set with the feature.
 | |
|   @retval Others                Failed to set the feature.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbRootHubSetPortFeature (
 | |
|   IN USB_INTERFACE         *HubIf,
 | |
|   IN UINT8                 Port,
 | |
|   IN EFI_USB_PORT_FEATURE  Feature
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = UsbHcSetRootHubPortFeature (HubIf->Device->Bus, Port, Feature);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Clear the root hub port feature.
 | |
| 
 | |
|   @param  HubIf                 The root hub interface.
 | |
|   @param  Port                  The root hub port.
 | |
|   @param  Feature               The feature to clear.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The root hub port is cleared of the feature.
 | |
|   @retval Others                Failed to clear the feature.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbRootHubClearPortFeature (
 | |
|   IN USB_INTERFACE         *HubIf,
 | |
|   IN UINT8                 Port,
 | |
|   IN EFI_USB_PORT_FEATURE  Feature
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = UsbHcClearRootHubPortFeature (HubIf->Device->Bus, Port, Feature);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Interface function to reset the root hub port.
 | |
| 
 | |
|   @param  RootIf                The root hub interface.
 | |
|   @param  Port                  The port to reset.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The hub port is reset.
 | |
|   @retval EFI_TIMEOUT           Failed to reset the port in time.
 | |
|   @retval EFI_NOT_FOUND         The low/full speed device connected to high  speed.
 | |
|                                 root hub is released to the companion UHCI.
 | |
|   @retval Others                Failed to reset the port.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbRootHubResetPort (
 | |
|   IN USB_INTERFACE  *RootIf,
 | |
|   IN UINT8          Port
 | |
|   )
 | |
| {
 | |
|   USB_BUS              *Bus;
 | |
|   EFI_STATUS           Status;
 | |
|   EFI_USB_PORT_STATUS  PortState;
 | |
|   UINTN                Index;
 | |
| 
 | |
|   //
 | |
|   // Notice: although EHCI requires that ENABLED bit be cleared
 | |
|   // when reset the port, we don't need to care that here. It
 | |
|   // should be handled in the EHCI driver.
 | |
|   //
 | |
|   Bus = RootIf->Device->Bus;
 | |
| 
 | |
|   Status = UsbHcSetRootHubPortFeature (Bus, Port, EfiUsbPortReset);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbRootHubResetPort: failed to start reset on port %d\n", Port));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Drive the reset signal for at least 50ms. Check USB 2.0 Spec
 | |
|   // section 7.1.7.5 for timing requirements.
 | |
|   //
 | |
|   gBS->Stall (USB_SET_ROOT_PORT_RESET_STALL);
 | |
| 
 | |
|   Status = UsbHcClearRootHubPortFeature (Bus, Port, EfiUsbPortReset);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbRootHubResetPort: failed to clear reset on port %d\n", Port));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   gBS->Stall (USB_CLR_ROOT_PORT_RESET_STALL);
 | |
| 
 | |
|   //
 | |
|   // USB host controller won't clear the RESET bit until
 | |
|   // reset is actually finished.
 | |
|   //
 | |
|   ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS));
 | |
| 
 | |
|   for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) {
 | |
|     Status = UsbHcGetRootHubPortStatus (Bus, Port, &PortState);
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_RESET)) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     gBS->Stall (USB_WAIT_PORT_STS_CHANGE_STALL);
 | |
|   }
 | |
| 
 | |
|   if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbRootHubResetPort: reset not finished in time on port %d\n", Port));
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_ENABLE)) {
 | |
|     //
 | |
|     // OK, the port is reset. If root hub is of high speed and
 | |
|     // the device is of low/full speed, release the ownership to
 | |
|     // companion UHCI. If root hub is of full speed, it won't
 | |
|     // automatically enable the port, we need to enable it manually.
 | |
|     //
 | |
|     if (RootIf->MaxSpeed == EFI_USB_SPEED_HIGH) {
 | |
|       DEBUG ((DEBUG_ERROR, "UsbRootHubResetPort: release low/full speed device (%d) to UHCI\n", Port));
 | |
| 
 | |
|       UsbRootHubSetPortFeature (RootIf, Port, EfiUsbPortOwner);
 | |
|       return EFI_NOT_FOUND;
 | |
|     } else {
 | |
|       Status = UsbRootHubSetPortFeature (RootIf, Port, EfiUsbPortEnable);
 | |
| 
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         DEBUG ((DEBUG_ERROR, "UsbRootHubResetPort: failed to enable port %d for UHCI\n", Port));
 | |
|         return Status;
 | |
|       }
 | |
| 
 | |
|       gBS->Stall (USB_SET_ROOT_PORT_ENABLE_STALL);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Release the root hub's control of the interface.
 | |
| 
 | |
|   @param  HubIf                 The root hub interface.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The root hub's control of the interface is
 | |
|                                 released.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbRootHubRelease (
 | |
|   IN USB_INTERFACE  *HubIf
 | |
|   )
 | |
| {
 | |
|   DEBUG ((DEBUG_INFO, "UsbRootHubRelease: root hub released for hub %p\n", HubIf));
 | |
| 
 | |
|   gBS->SetTimer (HubIf->HubNotify, TimerCancel, USB_ROOTHUB_POLL_INTERVAL);
 | |
|   gBS->CloseEvent (HubIf->HubNotify);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| USB_HUB_API  mUsbHubApi = {
 | |
|   UsbHubInit,
 | |
|   UsbHubGetPortStatus,
 | |
|   UsbHubClearPortChange,
 | |
|   UsbHubSetPortFeature,
 | |
|   UsbHubClearPortFeature,
 | |
|   UsbHubResetPort,
 | |
|   UsbHubRelease
 | |
| };
 | |
| 
 | |
| USB_HUB_API  mUsbRootHubApi = {
 | |
|   UsbRootHubInit,
 | |
|   UsbRootHubGetPortStatus,
 | |
|   UsbRootHubClearPortChange,
 | |
|   UsbRootHubSetPortFeature,
 | |
|   UsbRootHubClearPortFeature,
 | |
|   UsbRootHubResetPort,
 | |
|   UsbRootHubRelease
 | |
| };
 |