642 lines
15 KiB
C
642 lines
15 KiB
C
/** @file
|
|
Usb Hub Request
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012 - 2018, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
#include "UsbBus.h"
|
|
#include "Hub.h"
|
|
|
|
typedef struct {
|
|
UINT16 IdVendor;
|
|
UINT16 IdProduct;
|
|
UINT32 Delay;
|
|
} HUB_TABLE;
|
|
|
|
HUB_TABLE HubDelayTable[] = {
|
|
{ 0x8087, 0xffff, 0 }, // Intel virtual HUB without delay
|
|
{ 0x0424, 0x2514, 0 }, // WhitneyPoint virtual HUB without delay
|
|
{ 0x05e3, 0x0612, 3000 }, // Gyroscope GL3520M-OSY10 USB 3.0 HUB with 3 seconds delay to waiting for downstream port ready
|
|
{ 0x2109, 0x0811, 3000 }, // VIA USB 3.0 HUB with 3 seconds delay to waiting for downstream port ready
|
|
{ 0x0000, 0x0000, 0 } // End of list
|
|
};
|
|
|
|
/**
|
|
|
|
Performing HUB delay after port reset
|
|
|
|
@param UsbDevice Pointer of USB_IO_DEVICE
|
|
@param Delay Delay in millisecond
|
|
|
|
**/
|
|
STATIC
|
|
UINTN
|
|
HubDelay (
|
|
IN USB_IO_DEVICE *UsbDevice,
|
|
IN UINTN Delay
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
for (Index = 0; HubDelayTable[Index].IdVendor != 0; Index ++) {
|
|
if (UsbDevice->DeviceSpeed == EFI_USB_SPEED_HIGH &&
|
|
(HubDelayTable[Index].IdVendor == 0xffff || HubDelayTable[Index].IdVendor == UsbDevice->DeviceDescriptor.IdVendor) &&
|
|
(HubDelayTable[Index].IdProduct == 0xffff || HubDelayTable[Index].IdProduct == UsbDevice->DeviceDescriptor.IdProduct)) {
|
|
if (Delay == 0) return HubDelayTable[Index].Delay;
|
|
Delay = HubDelayTable[Index].Delay;
|
|
break;
|
|
}
|
|
}
|
|
if (Delay) mUsbCore->Stall (Delay * 1000);
|
|
return Delay;
|
|
}
|
|
|
|
/**
|
|
|
|
Get a given hub port status
|
|
|
|
@param UsbIo EFI_USB_IO_PROTOCOL instance
|
|
@param Port Usb hub port number (starting from 1).
|
|
@param PortStatus Current Hub port status and change status.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_DEVICE
|
|
@retval EFI_TIME_OUT
|
|
@retval EFI_INVALID_PARAMETER
|
|
|
|
**/
|
|
EFI_STATUS
|
|
HubGetPortStatus (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN UINT8 Port,
|
|
OUT UINT32 *PortStatus
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST DevReq;
|
|
EFI_STATUS EfiStatus;
|
|
UINT32 UsbStatus;
|
|
UINT32 Timeout;
|
|
|
|
ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
|
|
|
|
//
|
|
// Fill Device request packet
|
|
//
|
|
DevReq.RequestType = HUB_GET_PORT_STATUS_REQ_TYPE;
|
|
DevReq.Request = HUB_GET_PORT_STATUS;
|
|
DevReq.Value = 0;
|
|
DevReq.Index = Port;
|
|
DevReq.Length = sizeof (UINT32);
|
|
|
|
Timeout = 3000;
|
|
|
|
EfiStatus = UsbControlTransfer (
|
|
UsbIo,
|
|
&DevReq,
|
|
EfiUsbDataIn,
|
|
Timeout,
|
|
PortStatus,
|
|
sizeof (UINT32),
|
|
&UsbStatus
|
|
);
|
|
|
|
return EfiStatus;
|
|
}
|
|
|
|
/**
|
|
|
|
Set specified feature to a give hub port
|
|
|
|
@param UsbIo EFI_USB_IO_PROTOCOL instance
|
|
@param Port Usb hub port number (starting from 1).
|
|
@param Value New feature value.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_DEVICE
|
|
@retval EFI_TIME_OUT
|
|
@retval EFI_INVALID_PARAMETER
|
|
|
|
**/
|
|
EFI_STATUS
|
|
HubSetPortFeature (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN UINT8 Port,
|
|
IN UINT8 Value
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST DevReq;
|
|
EFI_STATUS EfiStatus;
|
|
UINT32 UsbStatus;
|
|
UINT32 Timeout;
|
|
|
|
ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
|
|
|
|
//
|
|
// Fill Device request packet
|
|
//
|
|
DevReq.RequestType = HUB_SET_PORT_FEATURE_REQ_TYPE;
|
|
DevReq.Request = HUB_SET_PORT_FEATURE;
|
|
DevReq.Value = Value;
|
|
DevReq.Index = Port;
|
|
DevReq.Length = 0;
|
|
|
|
Timeout = 3000;
|
|
EfiStatus = UsbControlTransfer (
|
|
UsbIo,
|
|
&DevReq,
|
|
EfiUsbNoData,
|
|
Timeout,
|
|
NULL,
|
|
0,
|
|
&UsbStatus
|
|
);
|
|
|
|
return EfiStatus;
|
|
}
|
|
|
|
/**
|
|
|
|
Clear a specified feature of a given hub port
|
|
|
|
@param UsbIo EFI_USB_IO_PROTOCOL instance
|
|
@param Port Usb hub port number (starting from 1).
|
|
@param Value Feature value that will be cleared from
|
|
that hub port.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_DEVICE
|
|
@retval EFI_TIME_OUT
|
|
@retval EFI_INVALID_PARAMETER
|
|
|
|
**/
|
|
EFI_STATUS
|
|
HubClearPortFeature (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN UINT8 Port,
|
|
IN UINT8 Value
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST DevReq;
|
|
EFI_STATUS EfiStatus;
|
|
UINT32 UsbStatus;
|
|
UINT32 Timeout;
|
|
|
|
ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
|
|
|
|
//
|
|
// Fill Device request packet
|
|
//
|
|
DevReq.RequestType = HUB_CLEAR_FEATURE_PORT_REQ_TYPE;
|
|
DevReq.Request = HUB_CLEAR_FEATURE_PORT;
|
|
DevReq.Value = Value;
|
|
DevReq.Index = Port;
|
|
DevReq.Length = 0;
|
|
|
|
Timeout = 3000;
|
|
EfiStatus = UsbControlTransfer (
|
|
UsbIo,
|
|
&DevReq,
|
|
EfiUsbNoData,
|
|
Timeout,
|
|
NULL,
|
|
0,
|
|
&UsbStatus
|
|
);
|
|
|
|
return EfiStatus;
|
|
}
|
|
|
|
/**
|
|
|
|
Get Hub Status
|
|
|
|
@param UsbIo EFI_USB_IO_PROTOCOL instance
|
|
@param HubStatus Current Hub status and change status.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_DEVICE
|
|
@retval EFI_TIME_OUT
|
|
|
|
**/
|
|
EFI_STATUS
|
|
HubGetHubStatus (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
OUT UINT32 *HubStatus
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST DevReq;
|
|
EFI_STATUS EfiStatus;
|
|
UINT32 UsbStatus;
|
|
UINT32 Timeout;
|
|
|
|
ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
|
|
|
|
//
|
|
// Fill Device request packet
|
|
//
|
|
DevReq.RequestType = HUB_GET_HUB_STATUS_REQ_TYPE;
|
|
DevReq.Request = HUB_GET_HUB_STATUS;
|
|
DevReq.Value = 0;
|
|
DevReq.Index = 0;
|
|
DevReq.Length = sizeof (UINT32);
|
|
|
|
Timeout = 3000;
|
|
EfiStatus = UsbControlTransfer (
|
|
UsbIo,
|
|
&DevReq,
|
|
EfiUsbDataIn,
|
|
Timeout,
|
|
HubStatus,
|
|
sizeof (UINT32),
|
|
&UsbStatus
|
|
);
|
|
|
|
return EfiStatus;
|
|
}
|
|
|
|
/**
|
|
|
|
Set a specified feature to the hub
|
|
|
|
@param UsbIo EFI_USB_IO_PROTOCOL instance
|
|
@param Value Feature value that will be set to the hub.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_DEVICE
|
|
@retval EFI_TIME_OUT
|
|
|
|
**/
|
|
EFI_STATUS
|
|
HubSetHubFeature (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN UINT8 Value
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST DevReq;
|
|
EFI_STATUS EfiStatus;
|
|
UINT32 UsbStatus;
|
|
UINT32 Timeout;
|
|
|
|
ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
|
|
|
|
//
|
|
// Fill Device request packet
|
|
//
|
|
DevReq.RequestType = HUB_SET_HUB_FEATURE_REQ_TYPE;
|
|
DevReq.Request = HUB_SET_HUB_FEATURE;
|
|
DevReq.Value = Value;
|
|
DevReq.Index = 0;
|
|
DevReq.Length = 0;
|
|
|
|
Timeout = 3000;
|
|
EfiStatus = UsbControlTransfer (
|
|
UsbIo,
|
|
&DevReq,
|
|
EfiUsbNoData,
|
|
Timeout,
|
|
NULL,
|
|
0,
|
|
&UsbStatus
|
|
);
|
|
|
|
return EfiStatus;
|
|
}
|
|
|
|
/**
|
|
|
|
Set a specified feature to the hub
|
|
|
|
@param UsbIo EFI_USB_IO_PROTOCOL instance
|
|
@param Value Feature value that will be cleared from the hub.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_DEVICE
|
|
@retval EFI_TIME_OUT
|
|
|
|
**/
|
|
EFI_STATUS
|
|
HubClearHubFeature (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN UINT8 Value
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST DevReq;
|
|
EFI_STATUS EfiStatus;
|
|
UINT32 UsbStatus;
|
|
UINT32 Timeout;
|
|
|
|
ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
|
|
|
|
//
|
|
// Fill Device request packet
|
|
//
|
|
DevReq.RequestType = HUB_CLEAR_FEATURE_REQ_TYPE;
|
|
DevReq.Request = HUB_CLEAR_FEATURE;
|
|
DevReq.Value = Value;
|
|
DevReq.Index = 0;
|
|
DevReq.Length = 0;
|
|
|
|
Timeout = 3000;
|
|
EfiStatus = UsbControlTransfer (
|
|
UsbIo,
|
|
&DevReq,
|
|
EfiUsbNoData,
|
|
Timeout,
|
|
NULL,
|
|
0,
|
|
&UsbStatus
|
|
);
|
|
|
|
return EfiStatus;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
Get the hub descriptor
|
|
|
|
@param UsbIo EFI_USB_IO_PROTOCOL instance
|
|
@param DescriptorSize The length of Hub Descriptor buffer.
|
|
@param HubDescriptor Caller allocated buffer to store the hub descriptor
|
|
if successfully returned.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_DEVICE
|
|
@retval EFI_TIME_OUT
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetHubDescriptor (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN UINTN DescriptorSize,
|
|
OUT EFI_USB_HUB_DESCRIPTOR *HubDescriptor
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST DevReq;
|
|
EFI_STATUS EfiStatus;
|
|
UINT32 UsbStatus;
|
|
UINT32 Timeout;
|
|
USB_IO_CONTROLLER_DEVICE *UsbIoController;
|
|
|
|
UsbIoController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (UsbIo);
|
|
|
|
ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
|
|
|
|
//
|
|
// Fill Device request packet
|
|
//
|
|
DevReq.RequestType = USB_REQ_TYPE_CLASS | USB_TARGET_DEVICE | USB_ENDPOINT_DIR_IN;
|
|
DevReq.Request = HUB_GET_DESCRIPTOR;
|
|
DevReq.Value = ((UsbIoController->UsbDevice->DeviceSpeed == EFI_USB_SPEED_SUPER) ? USB_DESC_TYPE_SSHUB : USB_DESC_TYPE_HUB) << 8;
|
|
DevReq.Index = 0;
|
|
DevReq.Length = (UINT16) DescriptorSize;
|
|
|
|
Timeout = 3000;
|
|
EfiStatus = UsbControlTransfer (
|
|
UsbIo,
|
|
&DevReq,
|
|
EfiUsbDataIn,
|
|
Timeout,
|
|
HubDescriptor,
|
|
(UINT16) DescriptorSize,
|
|
&UsbStatus
|
|
);
|
|
|
|
return EfiStatus;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
Set the hub depth for super speed hub
|
|
|
|
@param UsbIo EFI_USB_IO_PROTOCOL instance
|
|
@param Depth The depth of hub
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_DEVICE
|
|
@retval EFI_TIME_OUT
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
SetHubDepth (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN UINT8 Depth
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST DevReq;
|
|
EFI_STATUS EfiStatus;
|
|
UINT32 UsbStatus;
|
|
UINT32 Timeout;
|
|
|
|
ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
|
|
|
|
//
|
|
// Fill Device request packet
|
|
//
|
|
DevReq.RequestType = HUB_SET_HUB_DEPTH_REQ_TYPE;
|
|
DevReq.Request = HUB_SET_HUB_DEPTH;
|
|
DevReq.Value = Depth;
|
|
DevReq.Index = 0;
|
|
DevReq.Length = 0;
|
|
|
|
Timeout = 3000;
|
|
EfiStatus = UsbControlTransfer (
|
|
UsbIo,
|
|
&DevReq,
|
|
EfiUsbNoData,
|
|
Timeout,
|
|
NULL,
|
|
0,
|
|
&UsbStatus
|
|
);
|
|
|
|
return EfiStatus;
|
|
}
|
|
|
|
/**
|
|
|
|
Configure the hub
|
|
|
|
@param HubController Indicating the hub controller device that
|
|
will be configured
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_DEVICE_ERROR
|
|
|
|
**/
|
|
EFI_STATUS
|
|
DoHubConfig (
|
|
IN USB_IO_CONTROLLER_DEVICE *HubController
|
|
)
|
|
{
|
|
EFI_USB_IO_PROTOCOL *UsbIo;
|
|
EFI_USB_HUB_DESCRIPTOR HubDescriptor;
|
|
EFI_STATUS Status;
|
|
EFI_USB_HUB_STATUS HubStatus;
|
|
UINTN Index;
|
|
|
|
UsbIo = &HubController->UsbIo;
|
|
|
|
ZeroMem (&HubDescriptor, sizeof (HubDescriptor));
|
|
|
|
//
|
|
// First get the hub descriptor length
|
|
//
|
|
Status = GetHubDescriptor (UsbIo, 2, &HubDescriptor);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// First get the whole descriptor, then
|
|
// get the number of hub ports
|
|
//
|
|
Status = GetHubDescriptor (
|
|
UsbIo,
|
|
HubDescriptor.Length,
|
|
&HubDescriptor
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((gUSBErrorLevel, "Get hub descriptor fail\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
HubController->DownstreamPorts = HubDescriptor.NbrPorts;
|
|
//
|
|
// Power all the hub ports
|
|
//
|
|
for (Index = 0; Index < HubController->DownstreamPorts; Index++) {
|
|
Status = HubSetPortFeature (
|
|
UsbIo,
|
|
(UINT8) (Index + 1),
|
|
EfiUsbPortPower
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
if (Index < (UINTN) HubController->DownstreamPorts - 1 && HubDelay (HubController->UsbDevice, 0) != 0) {
|
|
//
|
|
// Make 1ms stall between set each of port power to workaround specific device firmware fatal
|
|
//
|
|
mUsbCore->Stall (1000);
|
|
}
|
|
}
|
|
//
|
|
// Performing HUB delay after hub port reset
|
|
//
|
|
HubDelay (HubController->UsbDevice, HubDescriptor.PwrOn2PwrGood + 200);
|
|
//
|
|
// Clear Hub Status Change
|
|
//
|
|
Status = HubGetHubStatus (UsbIo, (UINT32 *) &HubStatus);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((gUSBErrorLevel, "Get hub status fail\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
} else {
|
|
//
|
|
// Hub power supply change happens
|
|
//
|
|
if (HubStatus.HubChange & HUB_CHANGE_LOCAL_POWER) {
|
|
HubClearHubFeature (UsbIo, C_HUB_LOCAL_POWER);
|
|
}
|
|
//
|
|
// Hub change overcurrent happens
|
|
//
|
|
if (HubStatus.HubChange & HUB_CHANGE_OVERCURRENT) {
|
|
HubClearHubFeature (UsbIo, C_HUB_OVER_CURRENT);
|
|
}
|
|
}
|
|
//
|
|
// Set Hub Depth for Super Speed Hub
|
|
//
|
|
if (HubDescriptor.DescriptorType == USB_DESC_TYPE_SSHUB) {
|
|
Status = SetHubDepth (
|
|
UsbIo,
|
|
HubController->UsbDevice->HubDepth
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
Tell if a usb controller is a hub controller.
|
|
|
|
@param Dev UsbIoController device structure.
|
|
|
|
@retval TRUE
|
|
@retval FALSE
|
|
|
|
**/
|
|
BOOLEAN
|
|
IsHub (
|
|
IN USB_IO_CONTROLLER_DEVICE *Dev
|
|
)
|
|
{
|
|
EFI_USB_INTERFACE_DESCRIPTOR Interface;
|
|
EFI_USB_IO_PROTOCOL *UsbIo;
|
|
EFI_USB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
|
|
UINT8 Index;
|
|
|
|
if (Dev == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
UsbIo = &Dev->UsbIo;
|
|
|
|
UsbGetInterfaceDescriptor (
|
|
UsbIo,
|
|
&Interface
|
|
);
|
|
|
|
//
|
|
// Check classcode
|
|
//
|
|
if (Interface.InterfaceClass != CLASS_CODE_HUB) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check protocol
|
|
//
|
|
if (Interface.InterfaceSubClass != SUB_CLASS_CODE_HUB &&
|
|
Interface.InterfaceSubClass != SUB_CLASS_CODE_HUB1) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (Index = 0; Index < Interface.NumEndpoints; Index++) {
|
|
UsbGetEndpointDescriptor (
|
|
UsbIo,
|
|
Index,
|
|
&EndpointDescriptor
|
|
);
|
|
|
|
if ((EndpointDescriptor.EndpointAddress & 0x80) == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (EndpointDescriptor.Attributes != 0x03 && EndpointDescriptor.Attributes != 0x13) {
|
|
continue;
|
|
}
|
|
|
|
Dev->HubEndpointAddress = EndpointDescriptor.EndpointAddress;
|
|
Dev->MultiTT = (Interface.InterfaceSubClass == SUB_CLASS_CODE_HUB1) ? 1 : 0;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
} |