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>
		
			
				
	
	
		
			1090 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1090 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|     Usb bus enumeration support.
 | |
| 
 | |
| Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
| SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "UsbBus.h"
 | |
| 
 | |
| /**
 | |
|   Return the endpoint descriptor in this interface.
 | |
| 
 | |
|   @param  UsbIf                 The interface to search in.
 | |
|   @param  EpAddr                The address of the endpoint to return.
 | |
| 
 | |
|   @return The endpoint descriptor or NULL.
 | |
| 
 | |
| **/
 | |
| USB_ENDPOINT_DESC *
 | |
| UsbGetEndpointDesc (
 | |
|   IN USB_INTERFACE  *UsbIf,
 | |
|   IN UINT8          EpAddr
 | |
|   )
 | |
| {
 | |
|   USB_ENDPOINT_DESC  *EpDesc;
 | |
|   UINT8              Index;
 | |
|   UINT8              NumEndpoints;
 | |
| 
 | |
|   NumEndpoints = UsbIf->IfSetting->Desc.NumEndpoints;
 | |
| 
 | |
|   for (Index = 0; Index < NumEndpoints; Index++) {
 | |
|     EpDesc = UsbIf->IfSetting->Endpoints[Index];
 | |
| 
 | |
|     if (EpDesc->Desc.EndpointAddress == EpAddr) {
 | |
|       return EpDesc;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Free the resource used by USB interface.
 | |
| 
 | |
|   @param  UsbIf                 The USB interface to free.
 | |
| 
 | |
|   @retval EFI_ACCESS_DENIED     The interface is still occupied.
 | |
|   @retval EFI_SUCCESS           The interface is freed.
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbFreeInterface (
 | |
|   IN USB_INTERFACE  *UsbIf
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);
 | |
| 
 | |
|   Status = gBS->UninstallMultipleProtocolInterfaces (
 | |
|                   UsbIf->Handle,
 | |
|                   &gEfiDevicePathProtocolGuid,
 | |
|                   UsbIf->DevicePath,
 | |
|                   &gEfiUsbIoProtocolGuid,
 | |
|                   &UsbIf->UsbIo,
 | |
|                   NULL
 | |
|                   );
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     if (UsbIf->DevicePath != NULL) {
 | |
|       FreePool (UsbIf->DevicePath);
 | |
|     }
 | |
| 
 | |
|     FreePool (UsbIf);
 | |
|   } else {
 | |
|     UsbOpenHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Create an interface for the descriptor IfDesc. Each
 | |
|   device's configuration can have several interfaces.
 | |
| 
 | |
|   @param  Device                The device has the interface descriptor.
 | |
|   @param  IfDesc                The interface descriptor.
 | |
| 
 | |
|   @return The created USB interface for the descriptor, or NULL.
 | |
| 
 | |
| **/
 | |
| USB_INTERFACE *
 | |
| UsbCreateInterface (
 | |
|   IN USB_DEVICE          *Device,
 | |
|   IN USB_INTERFACE_DESC  *IfDesc
 | |
|   )
 | |
| {
 | |
|   USB_DEVICE_PATH  UsbNode;
 | |
|   USB_INTERFACE    *UsbIf;
 | |
|   USB_INTERFACE    *HubIf;
 | |
|   EFI_STATUS       Status;
 | |
| 
 | |
|   UsbIf = AllocateZeroPool (sizeof (USB_INTERFACE));
 | |
| 
 | |
|   if (UsbIf == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   UsbIf->Signature = USB_INTERFACE_SIGNATURE;
 | |
|   UsbIf->Device    = Device;
 | |
|   UsbIf->IfDesc    = IfDesc;
 | |
|   ASSERT (IfDesc->ActiveIndex < USB_MAX_INTERFACE_SETTING);
 | |
|   UsbIf->IfSetting = IfDesc->Settings[IfDesc->ActiveIndex];
 | |
| 
 | |
|   CopyMem (
 | |
|     &(UsbIf->UsbIo),
 | |
|     &mUsbIoProtocol,
 | |
|     sizeof (EFI_USB_IO_PROTOCOL)
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // Install protocols for USBIO and device path
 | |
|   //
 | |
|   UsbNode.Header.Type      = MESSAGING_DEVICE_PATH;
 | |
|   UsbNode.Header.SubType   = MSG_USB_DP;
 | |
|   UsbNode.ParentPortNumber = Device->ParentPort;
 | |
|   UsbNode.InterfaceNumber  = UsbIf->IfSetting->Desc.InterfaceNumber;
 | |
| 
 | |
|   SetDevicePathNodeLength (&UsbNode.Header, sizeof (UsbNode));
 | |
| 
 | |
|   HubIf = Device->ParentIf;
 | |
|   ASSERT (HubIf != NULL);
 | |
| 
 | |
|   UsbIf->DevicePath = AppendDevicePathNode (HubIf->DevicePath, &UsbNode.Header);
 | |
| 
 | |
|   if (UsbIf->DevicePath == NULL) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbCreateInterface: failed to create device path\n"));
 | |
| 
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   Status = gBS->InstallMultipleProtocolInterfaces (
 | |
|                   &UsbIf->Handle,
 | |
|                   &gEfiDevicePathProtocolGuid,
 | |
|                   UsbIf->DevicePath,
 | |
|                   &gEfiUsbIoProtocolGuid,
 | |
|                   &UsbIf->UsbIo,
 | |
|                   NULL
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbCreateInterface: failed to install UsbIo - %r\n", Status));
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Open USB Host Controller Protocol by Child
 | |
|   //
 | |
|   Status = UsbOpenHostProtoByChild (Device->Bus, UsbIf->Handle);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->UninstallMultipleProtocolInterfaces (
 | |
|            UsbIf->Handle,
 | |
|            &gEfiDevicePathProtocolGuid,
 | |
|            UsbIf->DevicePath,
 | |
|            &gEfiUsbIoProtocolGuid,
 | |
|            &UsbIf->UsbIo,
 | |
|            NULL
 | |
|            );
 | |
| 
 | |
|     DEBUG ((DEBUG_ERROR, "UsbCreateInterface: failed to open host for child - %r\n", Status));
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   return UsbIf;
 | |
| 
 | |
| ON_ERROR:
 | |
|   if (UsbIf->DevicePath != NULL) {
 | |
|     FreePool (UsbIf->DevicePath);
 | |
|   }
 | |
| 
 | |
|   FreePool (UsbIf);
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Free the resource used by this USB device.
 | |
| 
 | |
|   @param  Device                The USB device to free.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| UsbFreeDevice (
 | |
|   IN USB_DEVICE  *Device
 | |
|   )
 | |
| {
 | |
|   if (Device->DevDesc != NULL) {
 | |
|     UsbFreeDevDesc (Device->DevDesc);
 | |
|   }
 | |
| 
 | |
|   gBS->FreePool (Device);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Create a device which is on the parent's ParentPort port.
 | |
| 
 | |
|   @param  ParentIf              The parent HUB interface.
 | |
|   @param  ParentPort            The port on the HUB this device is connected to.
 | |
| 
 | |
|   @return Created USB device, Or NULL.
 | |
| 
 | |
| **/
 | |
| USB_DEVICE *
 | |
| UsbCreateDevice (
 | |
|   IN USB_INTERFACE  *ParentIf,
 | |
|   IN UINT8          ParentPort
 | |
|   )
 | |
| {
 | |
|   USB_DEVICE  *Device;
 | |
| 
 | |
|   ASSERT (ParentIf != NULL);
 | |
| 
 | |
|   Device = AllocateZeroPool (sizeof (USB_DEVICE));
 | |
| 
 | |
|   if (Device == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   Device->Bus        = ParentIf->Device->Bus;
 | |
|   Device->MaxPacket0 = 8;
 | |
|   Device->ParentAddr = ParentIf->Device->Address;
 | |
|   Device->ParentIf   = ParentIf;
 | |
|   Device->ParentPort = ParentPort;
 | |
|   Device->Tier       = (UINT8)(ParentIf->Device->Tier + 1);
 | |
|   return Device;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Connect the USB interface with its driver. EFI USB bus will
 | |
|   create a USB interface for each separate interface descriptor.
 | |
| 
 | |
|   @param  UsbIf             The interface to connect driver to.
 | |
| 
 | |
|   @return EFI_SUCCESS       Interface is managed by some driver.
 | |
|   @return Others            Failed to locate a driver for this interface.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbConnectDriver (
 | |
|   IN USB_INTERFACE  *UsbIf
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   EFI_TPL     OldTpl;
 | |
| 
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   //
 | |
|   // Hub is maintained by the USB bus driver. Otherwise try to
 | |
|   // connect drivers with this interface
 | |
|   //
 | |
|   if (UsbIsHubInterface (UsbIf)) {
 | |
|     DEBUG ((DEBUG_INFO, "UsbConnectDriver: found a hub device\n"));
 | |
|     Status = mUsbHubApi.Init (UsbIf);
 | |
|   } else {
 | |
|     //
 | |
|     // This function is called in both UsbIoControlTransfer and
 | |
|     // the timer callback in hub enumeration. So, at least it is
 | |
|     // called at TPL_CALLBACK. Some driver sitting on USB has
 | |
|     // twisted TPL used. It should be no problem for us to connect
 | |
|     // or disconnect at CALLBACK.
 | |
|     //
 | |
| 
 | |
|     //
 | |
|     // Only recursively wanted usb child device
 | |
|     //
 | |
|     if (UsbBusIsWantedUsbIO (UsbIf->Device->Bus, UsbIf)) {
 | |
|       OldTpl = UsbGetCurrentTpl ();
 | |
|       DEBUG ((DEBUG_INFO, "UsbConnectDriver: TPL before connect is %d, %p\n", (UINT32)OldTpl, UsbIf->Handle));
 | |
| 
 | |
|       gBS->RestoreTPL (TPL_CALLBACK);
 | |
| 
 | |
|       Status           = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE);
 | |
|       UsbIf->IsManaged = (BOOLEAN) !EFI_ERROR (Status);
 | |
| 
 | |
|       DEBUG ((DEBUG_INFO, "UsbConnectDriver: TPL after connect is %d\n", (UINT32)UsbGetCurrentTpl ()));
 | |
|       ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
 | |
| 
 | |
|       gBS->RaiseTPL (OldTpl);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Select an alternate setting for the interface.
 | |
|   Each interface can have several mutually exclusive
 | |
|   settings. Only one setting is active. It will
 | |
|   also reset its endpoints' toggle to zero.
 | |
| 
 | |
|   @param  IfDesc                The interface descriptor to set.
 | |
|   @param  Alternate             The alternate setting number to locate.
 | |
| 
 | |
|   @retval EFI_NOT_FOUND         There is no setting with this alternate index.
 | |
|   @retval EFI_SUCCESS           The interface is set to Alternate setting.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbSelectSetting (
 | |
|   IN USB_INTERFACE_DESC  *IfDesc,
 | |
|   IN UINT8               Alternate
 | |
|   )
 | |
| {
 | |
|   USB_INTERFACE_SETTING  *Setting;
 | |
|   UINTN                  Index;
 | |
| 
 | |
|   //
 | |
|   // Locate the active alternate setting
 | |
|   //
 | |
|   Setting = NULL;
 | |
| 
 | |
|   for (Index = 0; Index < IfDesc->NumOfSetting; Index++) {
 | |
|     ASSERT (Index < USB_MAX_INTERFACE_SETTING);
 | |
|     Setting = IfDesc->Settings[Index];
 | |
| 
 | |
|     if (Setting->Desc.AlternateSetting == Alternate) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (Index == IfDesc->NumOfSetting) {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   IfDesc->ActiveIndex = Index;
 | |
| 
 | |
|   ASSERT (Setting != NULL);
 | |
|   DEBUG ((
 | |
|     DEBUG_INFO,
 | |
|     "UsbSelectSetting: setting %d selected for interface %d\n",
 | |
|     Alternate,
 | |
|     Setting->Desc.InterfaceNumber
 | |
|     ));
 | |
| 
 | |
|   //
 | |
|   // Reset the endpoint toggle to zero
 | |
|   //
 | |
|   for (Index = 0; Index < Setting->Desc.NumEndpoints; Index++) {
 | |
|     Setting->Endpoints[Index]->Toggle = 0;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Select a new configuration for the device. Each
 | |
|   device may support several configurations.
 | |
| 
 | |
|   @param  Device                The device to select configuration.
 | |
|   @param  ConfigValue           The index of the configuration ( != 0).
 | |
| 
 | |
|   @retval EFI_NOT_FOUND         There is no configuration with the index.
 | |
|   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource.
 | |
|   @retval EFI_SUCCESS           The configuration is selected.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbSelectConfig (
 | |
|   IN USB_DEVICE  *Device,
 | |
|   IN UINT8       ConfigValue
 | |
|   )
 | |
| {
 | |
|   USB_DEVICE_DESC     *DevDesc;
 | |
|   USB_CONFIG_DESC     *ConfigDesc;
 | |
|   USB_INTERFACE_DESC  *IfDesc;
 | |
|   USB_INTERFACE       *UsbIf;
 | |
|   EFI_STATUS          Status;
 | |
|   UINT8               Index;
 | |
| 
 | |
|   //
 | |
|   // Locate the active config, then set the device's pointer
 | |
|   //
 | |
|   DevDesc    = Device->DevDesc;
 | |
|   ConfigDesc = NULL;
 | |
| 
 | |
|   for (Index = 0; Index < DevDesc->Desc.NumConfigurations; Index++) {
 | |
|     ConfigDesc = DevDesc->Configs[Index];
 | |
| 
 | |
|     if (ConfigDesc->Desc.ConfigurationValue == ConfigValue) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (Index == DevDesc->Desc.NumConfigurations) {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   Device->ActiveConfig = ConfigDesc;
 | |
| 
 | |
|   DEBUG ((
 | |
|     DEBUG_INFO,
 | |
|     "UsbSelectConfig: config %d selected for device %d\n",
 | |
|     ConfigValue,
 | |
|     Device->Address
 | |
|     ));
 | |
| 
 | |
|   //
 | |
|   // Create interfaces for each USB interface descriptor.
 | |
|   //
 | |
|   for (Index = 0; Index < ConfigDesc->Desc.NumInterfaces; Index++) {
 | |
|     //
 | |
|     // First select the default interface setting, and reset
 | |
|     // the endpoint toggles to zero for its endpoints.
 | |
|     //
 | |
|     IfDesc = ConfigDesc->Interfaces[Index];
 | |
|     UsbSelectSetting (IfDesc, IfDesc->Settings[0]->Desc.AlternateSetting);
 | |
| 
 | |
|     //
 | |
|     // Create a USB_INTERFACE and install USB_IO and other protocols
 | |
|     //
 | |
|     UsbIf = UsbCreateInterface (Device, ConfigDesc->Interfaces[Index]);
 | |
| 
 | |
|     if (UsbIf == NULL) {
 | |
|       Device->NumOfInterface = Index;
 | |
|       return EFI_OUT_OF_RESOURCES;
 | |
|     }
 | |
| 
 | |
|     ASSERT (Index < USB_MAX_INTERFACE);
 | |
|     Device->Interfaces[Index] = UsbIf;
 | |
| 
 | |
|     //
 | |
|     // Connect the device to drivers, if it failed, ignore
 | |
|     // the error. Don't let the unsupported interfaces to block
 | |
|     // the supported interfaces.
 | |
|     //
 | |
|     Status = UsbConnectDriver (UsbIf);
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       DEBUG ((
 | |
|         DEBUG_WARN,
 | |
|         "UsbSelectConfig: failed to connect driver - %r, ignored\n",
 | |
|         Status
 | |
|         ));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Device->NumOfInterface = Index;
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Disconnect the USB interface with its driver.
 | |
| 
 | |
|   @param  UsbIf                 The interface to disconnect driver from.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbDisconnectDriver (
 | |
|   IN USB_INTERFACE  *UsbIf
 | |
|   )
 | |
| {
 | |
|   EFI_TPL     OldTpl;
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   //
 | |
|   // Release the hub if it's a hub controller, otherwise
 | |
|   // disconnect the driver if it is managed by other drivers.
 | |
|   //
 | |
|   Status = EFI_SUCCESS;
 | |
|   if (UsbIf->IsHub) {
 | |
|     Status = UsbIf->HubApi->Release (UsbIf);
 | |
|   } else if (UsbIf->IsManaged) {
 | |
|     //
 | |
|     // This function is called in both UsbIoControlTransfer and
 | |
|     // the timer callback in hub enumeration. So, at least it is
 | |
|     // called at TPL_CALLBACK. Some driver sitting on USB has
 | |
|     // twisted TPL used. It should be no problem for us to connect
 | |
|     // or disconnect at CALLBACK.
 | |
|     //
 | |
|     OldTpl = UsbGetCurrentTpl ();
 | |
|     DEBUG ((DEBUG_INFO, "UsbDisconnectDriver: old TPL is %d, %p\n", (UINT32)OldTpl, UsbIf->Handle));
 | |
| 
 | |
|     gBS->RestoreTPL (TPL_CALLBACK);
 | |
| 
 | |
|     Status = gBS->DisconnectController (UsbIf->Handle, NULL, NULL);
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       UsbIf->IsManaged = FALSE;
 | |
|     }
 | |
| 
 | |
|     DEBUG ((DEBUG_INFO, "UsbDisconnectDriver: TPL after disconnect is %d, %d\n", (UINT32)UsbGetCurrentTpl (), Status));
 | |
|     ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
 | |
| 
 | |
|     gBS->RaiseTPL (OldTpl);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Remove the current device configuration.
 | |
| 
 | |
|   @param  Device                The USB device to remove configuration from.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbRemoveConfig (
 | |
|   IN USB_DEVICE  *Device
 | |
|   )
 | |
| {
 | |
|   USB_INTERFACE  *UsbIf;
 | |
|   UINTN          Index;
 | |
|   EFI_STATUS     Status;
 | |
|   EFI_STATUS     ReturnStatus;
 | |
| 
 | |
|   //
 | |
|   // Remove each interface of the device
 | |
|   //
 | |
|   ReturnStatus = EFI_SUCCESS;
 | |
|   for (Index = 0; Index < Device->NumOfInterface; Index++) {
 | |
|     ASSERT (Index < USB_MAX_INTERFACE);
 | |
|     UsbIf = Device->Interfaces[Index];
 | |
| 
 | |
|     if (UsbIf == NULL) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     Status = UsbDisconnectDriver (UsbIf);
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       Status = UsbFreeInterface (UsbIf);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         UsbConnectDriver (UsbIf);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       Device->Interfaces[Index] = NULL;
 | |
|     } else {
 | |
|       ReturnStatus = Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Device->ActiveConfig = NULL;
 | |
|   return ReturnStatus;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Remove the device and all its children from the bus.
 | |
| 
 | |
|   @param  Device                The device to remove.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The device is removed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbRemoveDevice (
 | |
|   IN USB_DEVICE  *Device
 | |
|   )
 | |
| {
 | |
|   USB_BUS     *Bus;
 | |
|   USB_DEVICE  *Child;
 | |
|   EFI_STATUS  Status;
 | |
|   EFI_STATUS  ReturnStatus;
 | |
|   UINTN       Index;
 | |
| 
 | |
|   Bus = Device->Bus;
 | |
| 
 | |
|   //
 | |
|   // Remove all the devices on its downstream ports. Search from devices[1].
 | |
|   // Devices[0] is the root hub.
 | |
|   //
 | |
|   ReturnStatus = EFI_SUCCESS;
 | |
|   for (Index = 1; Index < Bus->MaxDevices; Index++) {
 | |
|     Child = Bus->Devices[Index];
 | |
| 
 | |
|     if ((Child == NULL) || (Child->ParentAddr != Device->Address)) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     Status = UsbRemoveDevice (Child);
 | |
| 
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       Bus->Devices[Index] = NULL;
 | |
|     } else {
 | |
|       Bus->Devices[Index]->DisconnectFail = TRUE;
 | |
|       ReturnStatus                        = Status;
 | |
|       DEBUG ((DEBUG_INFO, "UsbRemoveDevice: failed to remove child %p at parent %p\n", Child, Device));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (EFI_ERROR (ReturnStatus)) {
 | |
|     return ReturnStatus;
 | |
|   }
 | |
| 
 | |
|   Status = UsbRemoveConfig (Device);
 | |
| 
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_INFO, "UsbRemoveDevice: device %d removed\n", Device->Address));
 | |
| 
 | |
|     ASSERT (Device->Address < Bus->MaxDevices);
 | |
|     Bus->Devices[Device->Address] = NULL;
 | |
|     UsbFreeDevice (Device);
 | |
|   } else {
 | |
|     Bus->Devices[Device->Address]->DisconnectFail = TRUE;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Find the child device on the hub's port.
 | |
| 
 | |
|   @param  HubIf                 The hub interface.
 | |
|   @param  Port                  The port of the hub this child is connected to.
 | |
| 
 | |
|   @return The device on the hub's port, or NULL if there is none.
 | |
| 
 | |
| **/
 | |
| USB_DEVICE *
 | |
| UsbFindChild (
 | |
|   IN USB_INTERFACE  *HubIf,
 | |
|   IN UINT8          Port
 | |
|   )
 | |
| {
 | |
|   USB_DEVICE  *Device;
 | |
|   USB_BUS     *Bus;
 | |
|   UINTN       Index;
 | |
| 
 | |
|   Bus = HubIf->Device->Bus;
 | |
| 
 | |
|   //
 | |
|   // Start checking from device 1, device 0 is the root hub
 | |
|   //
 | |
|   for (Index = 1; Index < Bus->MaxDevices; Index++) {
 | |
|     Device = Bus->Devices[Index];
 | |
| 
 | |
|     if ((Device != NULL) && (Device->ParentAddr == HubIf->Device->Address) &&
 | |
|         (Device->ParentPort == Port))
 | |
|     {
 | |
|       return Device;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enumerate and configure the new device on the port of this HUB interface.
 | |
| 
 | |
|   @param  HubIf                 The HUB that has the device connected.
 | |
|   @param  Port                  The port index of the hub (started with zero).
 | |
|   @param  ResetIsNeeded         The boolean to control whether skip the reset of the port.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The device is enumerated (added or removed).
 | |
|   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource for the device.
 | |
|   @retval Others                Failed to enumerate the device.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbEnumerateNewDev (
 | |
|   IN USB_INTERFACE  *HubIf,
 | |
|   IN UINT8          Port,
 | |
|   IN BOOLEAN        ResetIsNeeded
 | |
|   )
 | |
| {
 | |
|   USB_BUS              *Bus;
 | |
|   USB_HUB_API          *HubApi;
 | |
|   USB_DEVICE           *Child;
 | |
|   USB_DEVICE           *Parent;
 | |
|   EFI_USB_PORT_STATUS  PortState;
 | |
|   UINTN                Address;
 | |
|   UINT8                Config;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   Parent  = HubIf->Device;
 | |
|   Bus     = Parent->Bus;
 | |
|   HubApi  = HubIf->HubApi;
 | |
|   Address = Bus->MaxDevices;
 | |
| 
 | |
|   gBS->Stall (USB_WAIT_PORT_STABLE_STALL);
 | |
| 
 | |
|   //
 | |
|   // Hub resets the device for at least 10 milliseconds.
 | |
|   // Host learns device speed. If device is of low/full speed
 | |
|   // and the hub is a EHCI root hub, ResetPort will release
 | |
|   // the device to its companion UHCI and return an error.
 | |
|   //
 | |
|   if (ResetIsNeeded) {
 | |
|     Status = HubApi->ResetPort (HubIf, Port);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       DEBUG ((DEBUG_ERROR, "UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status));
 | |
| 
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     DEBUG ((DEBUG_INFO, "UsbEnumerateNewDev: hub port %d is reset\n", Port));
 | |
|   } else {
 | |
|     DEBUG ((DEBUG_INFO, "UsbEnumerateNewDev: hub port %d reset is skipped\n", Port));
 | |
|   }
 | |
| 
 | |
|   Child = UsbCreateDevice (HubIf, Port);
 | |
| 
 | |
|   if (Child == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // OK, now identify the device speed. After reset, hub
 | |
|   // fully knows the actual device speed.
 | |
|   //
 | |
|   Status = HubApi->GetPortStatus (HubIf, Port, &PortState);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbEnumerateNewDev: failed to get speed of port %d\n", Port));
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbEnumerateNewDev: No device present at port %d\n", Port));
 | |
|     Status = EFI_NOT_FOUND;
 | |
|     goto ON_ERROR;
 | |
|   } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_SUPER_SPEED)) {
 | |
|     Child->Speed      = EFI_USB_SPEED_SUPER;
 | |
|     Child->MaxPacket0 = 512;
 | |
|   } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) {
 | |
|     Child->Speed      = EFI_USB_SPEED_HIGH;
 | |
|     Child->MaxPacket0 = 64;
 | |
|   } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) {
 | |
|     Child->Speed      = EFI_USB_SPEED_LOW;
 | |
|     Child->MaxPacket0 = 8;
 | |
|   } else {
 | |
|     Child->Speed      = EFI_USB_SPEED_FULL;
 | |
|     Child->MaxPacket0 = 8;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "UsbEnumerateNewDev: device is of %d speed\n", Child->Speed));
 | |
| 
 | |
|   if (((Child->Speed == EFI_USB_SPEED_LOW) || (Child->Speed == EFI_USB_SPEED_FULL)) &&
 | |
|       (Parent->Speed == EFI_USB_SPEED_HIGH))
 | |
|   {
 | |
|     //
 | |
|     // If the child is a low or full speed device, it is necessary to
 | |
|     // set the transaction translator. Port TT is 1-based.
 | |
|     // This is quite simple:
 | |
|     //  1. if parent is of high speed, then parent is our translator
 | |
|     //  2. otherwise use parent's translator.
 | |
|     //
 | |
|     Child->Translator.TranslatorHubAddress = Parent->Address;
 | |
|     Child->Translator.TranslatorPortNumber = (UINT8)(Port + 1);
 | |
|   } else {
 | |
|     Child->Translator = Parent->Translator;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((
 | |
|     DEBUG_INFO,
 | |
|     "UsbEnumerateNewDev: device uses translator (%d, %d)\n",
 | |
|     Child->Translator.TranslatorHubAddress,
 | |
|     Child->Translator.TranslatorPortNumber
 | |
|     ));
 | |
| 
 | |
|   //
 | |
|   // After port is reset, hub establishes a signal path between
 | |
|   // the device and host (DEFAULT state). Device's registers are
 | |
|   // reset, use default address 0 (host enumerates one device at
 | |
|   // a time) , and ready to respond to control transfer at EP 0.
 | |
|   //
 | |
| 
 | |
|   //
 | |
|   // Host assigns an address to the device. Device completes the
 | |
|   // status stage with default address, then switches to new address.
 | |
|   // ADDRESS state. Address zero is reserved for root hub.
 | |
|   //
 | |
|   ASSERT (Bus->MaxDevices <= 256);
 | |
|   for (Address = 1; Address < Bus->MaxDevices; Address++) {
 | |
|     if (Bus->Devices[Address] == NULL) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (Address >= Bus->MaxDevices) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbEnumerateNewDev: address pool is full for port %d\n", Port));
 | |
| 
 | |
|     Status = EFI_ACCESS_DENIED;
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   Status                = UsbSetAddress (Child, (UINT8)Address);
 | |
|   Child->Address        = (UINT8)Address;
 | |
|   Bus->Devices[Address] = Child;
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbEnumerateNewDev: failed to set device address - %r\n", Status));
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL);
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address));
 | |
| 
 | |
|   //
 | |
|   // Host sends a Get_Descriptor request to learn the max packet
 | |
|   // size of default pipe (only part of the device's descriptor).
 | |
|   //
 | |
|   Status = UsbGetMaxPacketSize0 (Child);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status));
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0));
 | |
| 
 | |
|   //
 | |
|   // Host learns about the device's abilities by requesting device's
 | |
|   // entire descriptions.
 | |
|   //
 | |
|   Status = UsbBuildDescTable (Child);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status));
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Select a default configuration: UEFI must set the configuration
 | |
|   // before the driver can connect to the device.
 | |
|   //
 | |
|   Config = Child->DevDesc->Configs[0]->Desc.ConfigurationValue;
 | |
|   Status = UsbSetConfig (Child, Config);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status));
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address));
 | |
| 
 | |
|   //
 | |
|   // Host assigns and loads a device driver.
 | |
|   //
 | |
|   Status = UsbSelectConfig (Child, Config);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbEnumerateNewDev: failed to create interfaces - %r\n", Status));
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Report Status Code to indicate USB device has been detected by hotplug
 | |
|   //
 | |
|   REPORT_STATUS_CODE_WITH_DEVICE_PATH (
 | |
|     EFI_PROGRESS_CODE,
 | |
|     (EFI_IO_BUS_USB | EFI_IOB_PC_HOTPLUG),
 | |
|     Bus->DevicePath
 | |
|     );
 | |
|   return EFI_SUCCESS;
 | |
| 
 | |
| ON_ERROR:
 | |
|   //
 | |
|   // If reach here, it means the enumeration process on a given port is interrupted due to error.
 | |
|   // The s/w resources, including the assigned address(Address) and the allocated usb device data
 | |
|   // structure(Bus->Devices[Address]), will NOT be freed here. These resources will be freed when
 | |
|   // the device is unplugged from the port or DriverBindingStop() is invoked.
 | |
|   //
 | |
|   // This way is used to co-work with the lower layer EDKII UHCI/EHCI/XHCI host controller driver.
 | |
|   // It's mainly because to keep UEFI spec unchanged EDKII XHCI driver have to maintain a state machine
 | |
|   // to keep track of the mapping between actual address and request address. If the request address
 | |
|   // (Address) is freed here, the Address value will be used by next enumerated device. Then EDKII XHCI
 | |
|   // host controller driver will have wrong information, which will cause further transaction error.
 | |
|   //
 | |
|   // EDKII UHCI/EHCI doesn't get impacted as it's make sense to reserve s/w resource till it gets unplugged.
 | |
|   //
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Process the events on the port.
 | |
| 
 | |
|   @param  HubIf                 The HUB that has the device connected.
 | |
|   @param  Port                  The port index of the hub (started with zero).
 | |
| 
 | |
|   @retval EFI_SUCCESS           The device is enumerated (added or removed).
 | |
|   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource for the device.
 | |
|   @retval Others                Failed to enumerate the device.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbEnumeratePort (
 | |
|   IN USB_INTERFACE  *HubIf,
 | |
|   IN UINT8          Port
 | |
|   )
 | |
| {
 | |
|   USB_HUB_API          *HubApi;
 | |
|   USB_DEVICE           *Child;
 | |
|   EFI_USB_PORT_STATUS  PortState;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   Child  = NULL;
 | |
|   HubApi = HubIf->HubApi;
 | |
| 
 | |
|   //
 | |
|   // Host learns of the new device by polling the hub for port changes.
 | |
|   //
 | |
|   Status = HubApi->GetPortStatus (HubIf, Port, &PortState);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbEnumeratePort: failed to get state of port %d\n", Port));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Only handle connection/enable/overcurrent/reset change.
 | |
|   // Usb super speed hub may report other changes, such as warm reset change. Ignore them.
 | |
|   //
 | |
|   if ((PortState.PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((
 | |
|     DEBUG_INFO,
 | |
|     "UsbEnumeratePort: port %d state - %02x, change - %02x on %p\n",
 | |
|     Port,
 | |
|     PortState.PortStatus,
 | |
|     PortState.PortChangeStatus,
 | |
|     HubIf
 | |
|     ));
 | |
| 
 | |
|   //
 | |
|   // This driver only process two kinds of events now: over current and
 | |
|   // connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET.
 | |
|   // ENABLE/RESET is used to reset port. SUSPEND isn't supported.
 | |
|   //
 | |
| 
 | |
|   if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) {
 | |
|     if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) {
 | |
|       //
 | |
|       // Case1:
 | |
|       //   Both OverCurrent and OverCurrentChange set, means over current occurs,
 | |
|       //   which probably is caused by short circuit. It has to wait system hardware
 | |
|       //   to perform recovery.
 | |
|       //
 | |
|       DEBUG ((DEBUG_ERROR, "UsbEnumeratePort: Critical Over Current (port %d)\n", Port));
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Case2:
 | |
|     //   Only OverCurrentChange set, means system has been recoveried from
 | |
|     //   over current. As a result, all ports are nearly power-off, so
 | |
|     //   it's necessary to detach and enumerate all ports again.
 | |
|     //
 | |
|     DEBUG ((DEBUG_ERROR, "UsbEnumeratePort: 2.0 device Recovery Over Current (port %d)\n", Port));
 | |
|   }
 | |
| 
 | |
|   if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) {
 | |
|     //
 | |
|     // Case3:
 | |
|     //   1.1 roothub port reg doesn't reflect over-current state, while its counterpart
 | |
|     //   on 2.0 roothub does. When over-current has influence on 1.1 device, the port
 | |
|     //   would be disabled, so it's also necessary to detach and enumerate again.
 | |
|     //
 | |
|     DEBUG ((DEBUG_ERROR, "UsbEnumeratePort: 1.1 device Recovery Over Current (port %d)\n", Port));
 | |
|   }
 | |
| 
 | |
|   if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) {
 | |
|     //
 | |
|     // Case4:
 | |
|     //   Device connected or disconnected normally.
 | |
|     //
 | |
|     DEBUG ((DEBUG_INFO, "UsbEnumeratePort: Device Connect/Disconnect Normally (port %d)\n", Port));
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Following as the above cases, it's safety to remove and create again.
 | |
|   //
 | |
|   Child = UsbFindChild (HubIf, Port);
 | |
| 
 | |
|   if (Child != NULL) {
 | |
|     DEBUG ((DEBUG_INFO, "UsbEnumeratePort: device at port %d removed from root hub %p\n", Port, HubIf));
 | |
|     UsbRemoveDevice (Child);
 | |
|   }
 | |
| 
 | |
|   if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) {
 | |
|     //
 | |
|     // Now, new device connected, enumerate and configure the device
 | |
|     //
 | |
|     DEBUG ((DEBUG_INFO, "UsbEnumeratePort: new device connected at port %d\n", Port));
 | |
|     if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET)) {
 | |
|       Status = UsbEnumerateNewDev (HubIf, Port, FALSE);
 | |
|     } else {
 | |
|       Status = UsbEnumerateNewDev (HubIf, Port, TRUE);
 | |
|     }
 | |
|   } else {
 | |
|     DEBUG ((DEBUG_INFO, "UsbEnumeratePort: device disconnected event on port %d\n", Port));
 | |
|   }
 | |
| 
 | |
|   HubApi->ClearPortChange (HubIf, Port);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enumerate all the changed hub ports.
 | |
| 
 | |
|   @param  Event                 The event that is triggered.
 | |
|   @param  Context               The context to the event.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| UsbHubEnumeration (
 | |
|   IN EFI_EVENT  Event,
 | |
|   IN VOID       *Context
 | |
|   )
 | |
| {
 | |
|   USB_INTERFACE  *HubIf;
 | |
|   UINT8          Byte;
 | |
|   UINT8          Bit;
 | |
|   UINT8          Index;
 | |
|   USB_DEVICE     *Child;
 | |
| 
 | |
|   ASSERT (Context != NULL);
 | |
| 
 | |
|   HubIf = (USB_INTERFACE *)Context;
 | |
| 
 | |
|   for (Index = 0; Index < HubIf->NumOfPort; Index++) {
 | |
|     Child = UsbFindChild (HubIf, Index);
 | |
|     if ((Child != NULL) && (Child->DisconnectFail == TRUE)) {
 | |
|       DEBUG ((DEBUG_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from hub %p, try again\n", Index, HubIf));
 | |
|       UsbRemoveDevice (Child);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (HubIf->ChangeMap == NULL) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // HUB starts its port index with 1.
 | |
|   //
 | |
|   Byte = 0;
 | |
|   Bit  = 1;
 | |
| 
 | |
|   for (Index = 0; Index < HubIf->NumOfPort; Index++) {
 | |
|     if (USB_BIT_IS_SET (HubIf->ChangeMap[Byte], USB_BIT (Bit))) {
 | |
|       UsbEnumeratePort (HubIf, Index);
 | |
|     }
 | |
| 
 | |
|     USB_NEXT_BIT (Byte, Bit);
 | |
|   }
 | |
| 
 | |
|   UsbHubAckHubStatus (HubIf->Device);
 | |
| 
 | |
|   gBS->FreePool (HubIf->ChangeMap);
 | |
|   HubIf->ChangeMap = NULL;
 | |
|   return;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enumerate all the changed hub ports.
 | |
| 
 | |
|   @param  Event                 The event that is triggered.
 | |
|   @param  Context               The context to the event.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| UsbRootHubEnumeration (
 | |
|   IN EFI_EVENT  Event,
 | |
|   IN VOID       *Context
 | |
|   )
 | |
| {
 | |
|   USB_INTERFACE  *RootHub;
 | |
|   UINT8          Index;
 | |
|   USB_DEVICE     *Child;
 | |
| 
 | |
|   RootHub = (USB_INTERFACE *)Context;
 | |
| 
 | |
|   for (Index = 0; Index < RootHub->NumOfPort; Index++) {
 | |
|     Child = UsbFindChild (RootHub, Index);
 | |
|     if ((Child != NULL) && (Child->DisconnectFail == TRUE)) {
 | |
|       DEBUG ((DEBUG_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from root hub %p, try again\n", Index, RootHub));
 | |
|       UsbRemoveDevice (Child);
 | |
|     }
 | |
| 
 | |
|     UsbEnumeratePort (RootHub, Index);
 | |
|   }
 | |
| }
 |