/** @file | |
The module to produce Usb Bus PPI. | |
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "UsbPeim.h" | |
#include "HubPeim.h" | |
#include "PeiUsbLib.h" | |
// | |
// UsbIo PPI interface function | |
// | |
PEI_USB_IO_PPI mUsbIoPpi = { | |
PeiUsbControlTransfer, | |
PeiUsbBulkTransfer, | |
PeiUsbGetInterfaceDescriptor, | |
PeiUsbGetEndpointDescriptor, | |
PeiUsbPortReset | |
}; | |
EFI_PEI_PPI_DESCRIPTOR mUsbIoPpiList = { | |
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gPeiUsbIoPpiGuid, | |
NULL | |
}; | |
/** | |
The enumeration routine to detect device change. | |
@param PeiServices Describes the list of possible PEI Services. | |
@param UsbHcPpi The pointer of PEI_USB_HOST_CONTROLLER_PPI instance. | |
@param Usb2HcPpi The pointer of PEI_USB2_HOST_CONTROLLER_PPI instance. | |
@retval EFI_SUCCESS The usb is enumerated successfully. | |
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. | |
@retval Others Other failure occurs. | |
**/ | |
EFI_STATUS | |
PeiUsbEnumeration ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi, | |
IN PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi | |
); | |
/** | |
Configure new detected usb device. | |
@param PeiServices Describes the list of possible PEI Services. | |
@param PeiUsbDevice The pointer of PEI_USB_DEVICE instance. | |
@param Port The port to be configured. | |
@param DeviceAddress The device address to be configured. | |
@retval EFI_SUCCESS The new detected usb device is configured successfully. | |
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. | |
@retval Others Other failure occurs. | |
**/ | |
EFI_STATUS | |
PeiConfigureUsbDevice ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_USB_DEVICE *PeiUsbDevice, | |
IN UINT8 Port, | |
IN OUT UINT8 *DeviceAddress | |
); | |
/** | |
Get all configurations from a detected usb device. | |
@param PeiServices Describes the list of possible PEI Services. | |
@param PeiUsbDevice The pointer of PEI_USB_DEVICE instance. | |
@retval EFI_SUCCESS The new detected usb device is configured successfully. | |
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. | |
@retval Others Other failure occurs. | |
**/ | |
EFI_STATUS | |
PeiUsbGetAllConfiguration ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_USB_DEVICE *PeiUsbDevice | |
); | |
/** | |
Get the start position of next wanted descriptor. | |
@param Buffer Buffer containing data to parse. | |
@param Length Buffer length. | |
@param DescType Descriptor type. | |
@param DescLength Descriptor length. | |
@param ParsedBytes Bytes has been parsed. | |
@retval EFI_SUCCESS Get wanted descriptor successfully. | |
@retval EFI_DEVICE_ERROR Error occurred. | |
**/ | |
EFI_STATUS | |
GetExpectedDescriptor ( | |
IN UINT8 *Buffer, | |
IN UINTN Length, | |
IN UINT8 DescType, | |
IN UINT8 DescLength, | |
OUT UINTN *ParsedBytes | |
); | |
/** | |
The entrypoint of the module, it will enumerate all HCs. | |
@param FileHandle Handle of the file being invoked. | |
@param PeiServices Describes the list of possible PEI Services. | |
@retval EFI_SUCCESS Usb initialization is done successfully. | |
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. | |
@retval EFI_UNSUPPORTED Can't find required PPI. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PeimInitializeUsb ( | |
IN EFI_PEI_FILE_HANDLE FileHandle, | |
IN CONST EFI_PEI_SERVICES **PeiServices | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi; | |
PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi; | |
if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { | |
return EFI_SUCCESS; | |
} | |
// | |
// gPeiUsbHostControllerPpiGuid and gPeiUsb2HostControllerPpiGuid should not | |
// be produced at the same time | |
// | |
Index = 0; | |
while (TRUE) { | |
// | |
// Get UsbHcPpi at first. | |
// | |
Status = PeiServicesLocatePpi ( | |
&gPeiUsbHostControllerPpiGuid, | |
Index, | |
NULL, | |
(VOID **)&UsbHcPpi | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// No more host controller, break out | |
// | |
break; | |
} | |
PeiUsbEnumeration ((EFI_PEI_SERVICES **)PeiServices, UsbHcPpi, NULL); | |
Index++; | |
} | |
if (Index == 0) { | |
// | |
// Then try to get Usb2HcPpi. | |
// | |
while (TRUE) { | |
Status = PeiServicesLocatePpi ( | |
&gPeiUsb2HostControllerPpiGuid, | |
Index, | |
NULL, | |
(VOID **)&Usb2HcPpi | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// No more host controller, break out | |
// | |
break; | |
} | |
PeiUsbEnumeration ((EFI_PEI_SERVICES **)PeiServices, NULL, Usb2HcPpi); | |
Index++; | |
} | |
} | |
if (Index == 0) { | |
return EFI_UNSUPPORTED; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
The Hub Enumeration just scans the hub ports one time. It also | |
doesn't support hot-plug. | |
@param PeiServices Describes the list of possible PEI Services. | |
@param PeiUsbDevice The pointer of PEI_USB_DEVICE instance. | |
@param CurrentAddress The DeviceAddress of usb device. | |
@retval EFI_SUCCESS The usb hub is enumerated successfully. | |
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. | |
@retval Others Other failure occurs. | |
**/ | |
EFI_STATUS | |
PeiHubEnumeration ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_USB_DEVICE *PeiUsbDevice, | |
IN UINT8 *CurrentAddress | |
) | |
{ | |
UINTN Index; | |
EFI_STATUS Status; | |
PEI_USB_IO_PPI *UsbIoPpi; | |
EFI_USB_PORT_STATUS PortStatus; | |
UINTN MemPages; | |
EFI_PHYSICAL_ADDRESS AllocateAddress; | |
PEI_USB_DEVICE *NewPeiUsbDevice; | |
UINTN InterfaceIndex; | |
UINTN EndpointIndex; | |
UsbIoPpi = &PeiUsbDevice->UsbIoPpi; | |
DEBUG ((DEBUG_INFO, "PeiHubEnumeration: DownStreamPortNo: %x\n", PeiUsbDevice->DownStreamPortNo)); | |
for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) { | |
Status = PeiHubGetPortStatus ( | |
PeiServices, | |
UsbIoPpi, | |
(UINT8)(Index + 1), | |
(UINT32 *)&PortStatus | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
DEBUG ((DEBUG_INFO, "USB Status --- Port: %x ConnectChange[%04x] Status[%04x]\n", Index, PortStatus.PortChangeStatus, PortStatus.PortStatus)); | |
// | |
// Only handle connection/enable/overcurrent/reset change. | |
// | |
if ((PortStatus.PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { | |
continue; | |
} else { | |
if (IsPortConnect (PortStatus.PortStatus)) { | |
// | |
// Begin to deal with the new device | |
// | |
MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1; | |
Status = PeiServicesAllocatePages ( | |
EfiBootServicesCode, | |
MemPages, | |
&AllocateAddress | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
NewPeiUsbDevice = (PEI_USB_DEVICE *)((UINTN)AllocateAddress); | |
ZeroMem (NewPeiUsbDevice, sizeof (PEI_USB_DEVICE)); | |
NewPeiUsbDevice->Signature = PEI_USB_DEVICE_SIGNATURE; | |
NewPeiUsbDevice->DeviceAddress = 0; | |
NewPeiUsbDevice->MaxPacketSize0 = 8; | |
NewPeiUsbDevice->DataToggle = 0; | |
CopyMem ( | |
&(NewPeiUsbDevice->UsbIoPpi), | |
&mUsbIoPpi, | |
sizeof (PEI_USB_IO_PPI) | |
); | |
CopyMem ( | |
&(NewPeiUsbDevice->UsbIoPpiList), | |
&mUsbIoPpiList, | |
sizeof (EFI_PEI_PPI_DESCRIPTOR) | |
); | |
NewPeiUsbDevice->UsbIoPpiList.Ppi = &NewPeiUsbDevice->UsbIoPpi; | |
NewPeiUsbDevice->AllocateAddress = (UINTN)AllocateAddress; | |
NewPeiUsbDevice->UsbHcPpi = PeiUsbDevice->UsbHcPpi; | |
NewPeiUsbDevice->Usb2HcPpi = PeiUsbDevice->Usb2HcPpi; | |
NewPeiUsbDevice->Tier = (UINT8)(PeiUsbDevice->Tier + 1); | |
NewPeiUsbDevice->IsHub = 0x0; | |
NewPeiUsbDevice->DownStreamPortNo = 0x0; | |
if (((PortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) == 0) || | |
((PortStatus.PortStatus & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) == 0)) | |
{ | |
// | |
// If the port already has reset change flag and is connected and enabled, skip the port reset logic. | |
// | |
PeiResetHubPort (PeiServices, UsbIoPpi, (UINT8)(Index + 1)); | |
PeiHubGetPortStatus ( | |
PeiServices, | |
UsbIoPpi, | |
(UINT8)(Index + 1), | |
(UINT32 *)&PortStatus | |
); | |
} else { | |
PeiHubClearPortFeature ( | |
PeiServices, | |
UsbIoPpi, | |
(UINT8)(Index + 1), | |
EfiUsbPortResetChange | |
); | |
} | |
NewPeiUsbDevice->DeviceSpeed = (UINT8)PeiUsbGetDeviceSpeed (PortStatus.PortStatus); | |
DEBUG ((DEBUG_INFO, "Device Speed =%d\n", PeiUsbDevice->DeviceSpeed)); | |
if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_SUPER_SPEED)) { | |
NewPeiUsbDevice->MaxPacketSize0 = 512; | |
} else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { | |
NewPeiUsbDevice->MaxPacketSize0 = 64; | |
} else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_LOW_SPEED)) { | |
NewPeiUsbDevice->MaxPacketSize0 = 8; | |
} else { | |
NewPeiUsbDevice->MaxPacketSize0 = 8; | |
} | |
if (NewPeiUsbDevice->DeviceSpeed != EFI_USB_SPEED_HIGH) { | |
if (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_HIGH) { | |
NewPeiUsbDevice->Translator.TranslatorPortNumber = (UINT8)Index; | |
NewPeiUsbDevice->Translator.TranslatorHubAddress = *CurrentAddress; | |
} else { | |
CopyMem (&(NewPeiUsbDevice->Translator), &(PeiUsbDevice->Translator), sizeof (EFI_USB2_HC_TRANSACTION_TRANSLATOR)); | |
} | |
} | |
// | |
// Configure that Usb Device | |
// | |
Status = PeiConfigureUsbDevice ( | |
PeiServices, | |
NewPeiUsbDevice, | |
(UINT8)(Index + 1), | |
CurrentAddress | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
DEBUG ((DEBUG_INFO, "PeiHubEnumeration: PeiConfigureUsbDevice Success\n")); | |
Status = PeiServicesInstallPpi (&NewPeiUsbDevice->UsbIoPpiList); | |
if (NewPeiUsbDevice->InterfaceDesc->InterfaceClass == 0x09) { | |
NewPeiUsbDevice->IsHub = 0x1; | |
Status = PeiDoHubConfig (PeiServices, NewPeiUsbDevice); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
PeiHubEnumeration (PeiServices, NewPeiUsbDevice, CurrentAddress); | |
} | |
for (InterfaceIndex = 1; InterfaceIndex < NewPeiUsbDevice->ConfigDesc->NumInterfaces; InterfaceIndex++) { | |
// | |
// Begin to deal with the new device | |
// | |
MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1; | |
Status = PeiServicesAllocatePages ( | |
EfiBootServicesCode, | |
MemPages, | |
&AllocateAddress | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
CopyMem ((VOID *)(UINTN)AllocateAddress, NewPeiUsbDevice, sizeof (PEI_USB_DEVICE)); | |
NewPeiUsbDevice = (PEI_USB_DEVICE *)((UINTN)AllocateAddress); | |
NewPeiUsbDevice->AllocateAddress = (UINTN)AllocateAddress; | |
NewPeiUsbDevice->UsbIoPpiList.Ppi = &NewPeiUsbDevice->UsbIoPpi; | |
NewPeiUsbDevice->InterfaceDesc = NewPeiUsbDevice->InterfaceDescList[InterfaceIndex]; | |
for (EndpointIndex = 0; EndpointIndex < NewPeiUsbDevice->InterfaceDesc->NumEndpoints; EndpointIndex++) { | |
NewPeiUsbDevice->EndpointDesc[EndpointIndex] = NewPeiUsbDevice->EndpointDescList[InterfaceIndex][EndpointIndex]; | |
} | |
Status = PeiServicesInstallPpi (&NewPeiUsbDevice->UsbIoPpiList); | |
if (NewPeiUsbDevice->InterfaceDesc->InterfaceClass == 0x09) { | |
NewPeiUsbDevice->IsHub = 0x1; | |
Status = PeiDoHubConfig (PeiServices, NewPeiUsbDevice); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
PeiHubEnumeration (PeiServices, NewPeiUsbDevice, CurrentAddress); | |
} | |
} | |
} | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
The enumeration routine to detect device change. | |
@param PeiServices Describes the list of possible PEI Services. | |
@param UsbHcPpi The pointer of PEI_USB_HOST_CONTROLLER_PPI instance. | |
@param Usb2HcPpi The pointer of PEI_USB2_HOST_CONTROLLER_PPI instance. | |
@retval EFI_SUCCESS The usb is enumerated successfully. | |
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. | |
@retval Others Other failure occurs. | |
**/ | |
EFI_STATUS | |
PeiUsbEnumeration ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi, | |
IN PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi | |
) | |
{ | |
UINT8 NumOfRootPort; | |
EFI_STATUS Status; | |
UINT8 Index; | |
EFI_USB_PORT_STATUS PortStatus; | |
PEI_USB_DEVICE *PeiUsbDevice; | |
UINTN MemPages; | |
EFI_PHYSICAL_ADDRESS AllocateAddress; | |
UINT8 CurrentAddress; | |
UINTN InterfaceIndex; | |
UINTN EndpointIndex; | |
CurrentAddress = 0; | |
if (Usb2HcPpi != NULL) { | |
Usb2HcPpi->GetRootHubPortNumber ( | |
PeiServices, | |
Usb2HcPpi, | |
(UINT8 *)&NumOfRootPort | |
); | |
} else if (UsbHcPpi != NULL) { | |
UsbHcPpi->GetRootHubPortNumber ( | |
PeiServices, | |
UsbHcPpi, | |
(UINT8 *)&NumOfRootPort | |
); | |
} else { | |
ASSERT (FALSE); | |
return EFI_INVALID_PARAMETER; | |
} | |
DEBUG ((DEBUG_INFO, "PeiUsbEnumeration: NumOfRootPort: %x\n", NumOfRootPort)); | |
for (Index = 0; Index < NumOfRootPort; Index++) { | |
// | |
// First get root port status to detect changes happen | |
// | |
if (Usb2HcPpi != NULL) { | |
Usb2HcPpi->GetRootHubPortStatus ( | |
PeiServices, | |
Usb2HcPpi, | |
(UINT8)Index, | |
&PortStatus | |
); | |
} else { | |
UsbHcPpi->GetRootHubPortStatus ( | |
PeiServices, | |
UsbHcPpi, | |
(UINT8)Index, | |
&PortStatus | |
); | |
} | |
DEBUG ((DEBUG_INFO, "USB Status --- Port: %x ConnectChange[%04x] Status[%04x]\n", Index, PortStatus.PortChangeStatus, PortStatus.PortStatus)); | |
// | |
// Only handle connection/enable/overcurrent/reset change. | |
// | |
if ((PortStatus.PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { | |
continue; | |
} else { | |
if (IsPortConnect (PortStatus.PortStatus)) { | |
MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1; | |
Status = PeiServicesAllocatePages ( | |
EfiBootServicesCode, | |
MemPages, | |
&AllocateAddress | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
PeiUsbDevice = (PEI_USB_DEVICE *)((UINTN)AllocateAddress); | |
ZeroMem (PeiUsbDevice, sizeof (PEI_USB_DEVICE)); | |
PeiUsbDevice->Signature = PEI_USB_DEVICE_SIGNATURE; | |
PeiUsbDevice->DeviceAddress = 0; | |
PeiUsbDevice->MaxPacketSize0 = 8; | |
PeiUsbDevice->DataToggle = 0; | |
CopyMem ( | |
&(PeiUsbDevice->UsbIoPpi), | |
&mUsbIoPpi, | |
sizeof (PEI_USB_IO_PPI) | |
); | |
CopyMem ( | |
&(PeiUsbDevice->UsbIoPpiList), | |
&mUsbIoPpiList, | |
sizeof (EFI_PEI_PPI_DESCRIPTOR) | |
); | |
PeiUsbDevice->UsbIoPpiList.Ppi = &PeiUsbDevice->UsbIoPpi; | |
PeiUsbDevice->AllocateAddress = (UINTN)AllocateAddress; | |
PeiUsbDevice->UsbHcPpi = UsbHcPpi; | |
PeiUsbDevice->Usb2HcPpi = Usb2HcPpi; | |
PeiUsbDevice->IsHub = 0x0; | |
PeiUsbDevice->DownStreamPortNo = 0x0; | |
if (((PortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) == 0) || | |
((PortStatus.PortStatus & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) == 0)) | |
{ | |
// | |
// If the port already has reset change flag and is connected and enabled, skip the port reset logic. | |
// | |
ResetRootPort ( | |
PeiServices, | |
PeiUsbDevice->UsbHcPpi, | |
PeiUsbDevice->Usb2HcPpi, | |
Index, | |
0 | |
); | |
if (Usb2HcPpi != NULL) { | |
Usb2HcPpi->GetRootHubPortStatus ( | |
PeiServices, | |
Usb2HcPpi, | |
(UINT8)Index, | |
&PortStatus | |
); | |
} else { | |
UsbHcPpi->GetRootHubPortStatus ( | |
PeiServices, | |
UsbHcPpi, | |
(UINT8)Index, | |
&PortStatus | |
); | |
} | |
} else { | |
if (Usb2HcPpi != NULL) { | |
Usb2HcPpi->ClearRootHubPortFeature ( | |
PeiServices, | |
Usb2HcPpi, | |
(UINT8)Index, | |
EfiUsbPortResetChange | |
); | |
} else { | |
UsbHcPpi->ClearRootHubPortFeature ( | |
PeiServices, | |
UsbHcPpi, | |
(UINT8)Index, | |
EfiUsbPortResetChange | |
); | |
} | |
} | |
PeiUsbDevice->DeviceSpeed = (UINT8)PeiUsbGetDeviceSpeed (PortStatus.PortStatus); | |
DEBUG ((DEBUG_INFO, "Device Speed =%d\n", PeiUsbDevice->DeviceSpeed)); | |
if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_SUPER_SPEED)) { | |
PeiUsbDevice->MaxPacketSize0 = 512; | |
} else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { | |
PeiUsbDevice->MaxPacketSize0 = 64; | |
} else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_LOW_SPEED)) { | |
PeiUsbDevice->MaxPacketSize0 = 8; | |
} else { | |
PeiUsbDevice->MaxPacketSize0 = 8; | |
} | |
// | |
// Configure that Usb Device | |
// | |
Status = PeiConfigureUsbDevice ( | |
PeiServices, | |
PeiUsbDevice, | |
Index, | |
&CurrentAddress | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
DEBUG ((DEBUG_INFO, "PeiUsbEnumeration: PeiConfigureUsbDevice Success\n")); | |
Status = PeiServicesInstallPpi (&PeiUsbDevice->UsbIoPpiList); | |
if (PeiUsbDevice->InterfaceDesc->InterfaceClass == 0x09) { | |
PeiUsbDevice->IsHub = 0x1; | |
Status = PeiDoHubConfig (PeiServices, PeiUsbDevice); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
PeiHubEnumeration (PeiServices, PeiUsbDevice, &CurrentAddress); | |
} | |
for (InterfaceIndex = 1; InterfaceIndex < PeiUsbDevice->ConfigDesc->NumInterfaces; InterfaceIndex++) { | |
// | |
// Begin to deal with the new device | |
// | |
MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1; | |
Status = PeiServicesAllocatePages ( | |
EfiBootServicesCode, | |
MemPages, | |
&AllocateAddress | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
CopyMem ((VOID *)(UINTN)AllocateAddress, PeiUsbDevice, sizeof (PEI_USB_DEVICE)); | |
PeiUsbDevice = (PEI_USB_DEVICE *)((UINTN)AllocateAddress); | |
PeiUsbDevice->AllocateAddress = (UINTN)AllocateAddress; | |
PeiUsbDevice->UsbIoPpiList.Ppi = &PeiUsbDevice->UsbIoPpi; | |
PeiUsbDevice->InterfaceDesc = PeiUsbDevice->InterfaceDescList[InterfaceIndex]; | |
for (EndpointIndex = 0; EndpointIndex < PeiUsbDevice->InterfaceDesc->NumEndpoints; EndpointIndex++) { | |
PeiUsbDevice->EndpointDesc[EndpointIndex] = PeiUsbDevice->EndpointDescList[InterfaceIndex][EndpointIndex]; | |
} | |
Status = PeiServicesInstallPpi (&PeiUsbDevice->UsbIoPpiList); | |
if (PeiUsbDevice->InterfaceDesc->InterfaceClass == 0x09) { | |
PeiUsbDevice->IsHub = 0x1; | |
Status = PeiDoHubConfig (PeiServices, PeiUsbDevice); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
PeiHubEnumeration (PeiServices, PeiUsbDevice, &CurrentAddress); | |
} | |
} | |
} else { | |
// | |
// Disconnect change happen, currently we don't support | |
// | |
} | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Configure new detected usb device. | |
@param PeiServices Describes the list of possible PEI Services. | |
@param PeiUsbDevice The pointer of PEI_USB_DEVICE instance. | |
@param Port The port to be configured. | |
@param DeviceAddress The device address to be configured. | |
@retval EFI_SUCCESS The new detected usb device is configured successfully. | |
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. | |
@retval Others Other failure occurs. | |
**/ | |
EFI_STATUS | |
PeiConfigureUsbDevice ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_USB_DEVICE *PeiUsbDevice, | |
IN UINT8 Port, | |
IN OUT UINT8 *DeviceAddress | |
) | |
{ | |
EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor; | |
EFI_STATUS Status; | |
PEI_USB_IO_PPI *UsbIoPpi; | |
UINT8 Retry; | |
UsbIoPpi = &PeiUsbDevice->UsbIoPpi; | |
Status = EFI_SUCCESS; | |
ZeroMem (&DeviceDescriptor, sizeof (EFI_USB_DEVICE_DESCRIPTOR)); | |
// | |
// Get USB device descriptor | |
// | |
for (Retry = 0; Retry < 3; Retry++) { | |
Status = PeiUsbGetDescriptor ( | |
PeiServices, | |
UsbIoPpi, | |
(USB_DT_DEVICE << 8), | |
0, | |
8, | |
&DeviceDescriptor | |
); | |
if (!EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_INFO, "PeiUsbGet Device Descriptor the %d time Success\n", Retry)); | |
break; | |
} | |
} | |
if (Retry == 3) { | |
DEBUG ((DEBUG_ERROR, "PeiUsbGet Device Descriptor fail: %x %r\n", Retry, Status)); | |
return Status; | |
} | |
if ((DeviceDescriptor.BcdUSB >= 0x0300) && (DeviceDescriptor.MaxPacketSize0 == 9)) { | |
PeiUsbDevice->MaxPacketSize0 = 1 << 9; | |
} else { | |
PeiUsbDevice->MaxPacketSize0 = DeviceDescriptor.MaxPacketSize0; | |
} | |
(*DeviceAddress)++; | |
Status = PeiUsbSetDeviceAddress ( | |
PeiServices, | |
UsbIoPpi, | |
*DeviceAddress | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "PeiUsbSetDeviceAddress Failed: %r\n", Status)); | |
return Status; | |
} | |
MicroSecondDelay (USB_SET_DEVICE_ADDRESS_STALL); | |
PeiUsbDevice->DeviceAddress = *DeviceAddress; | |
// | |
// Get whole USB device descriptor | |
// | |
Status = PeiUsbGetDescriptor ( | |
PeiServices, | |
UsbIoPpi, | |
(USB_DT_DEVICE << 8), | |
0, | |
(UINT16)sizeof (EFI_USB_DEVICE_DESCRIPTOR), | |
&DeviceDescriptor | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "PeiUsbGetDescriptor First Failed\n")); | |
return Status; | |
} | |
// | |
// Get its default configuration and its first interface | |
// | |
Status = PeiUsbGetAllConfiguration ( | |
PeiServices, | |
PeiUsbDevice | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
MicroSecondDelay (USB_GET_CONFIG_DESCRIPTOR_STALL); | |
Status = PeiUsbSetConfiguration ( | |
PeiServices, | |
UsbIoPpi | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Get all configurations from a detected usb device. | |
@param PeiServices Describes the list of possible PEI Services. | |
@param PeiUsbDevice The pointer of PEI_USB_DEVICE instance. | |
@retval EFI_SUCCESS The new detected usb device is configured successfully. | |
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. | |
@retval Others Other failure occurs. | |
**/ | |
EFI_STATUS | |
PeiUsbGetAllConfiguration ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_USB_DEVICE *PeiUsbDevice | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc; | |
PEI_USB_IO_PPI *UsbIoPpi; | |
UINT16 ConfigDescLength; | |
UINT8 *Ptr; | |
UINTN SkipBytes; | |
UINTN LengthLeft; | |
UINTN InterfaceIndex; | |
UINTN Index; | |
UINTN NumOfEndpoint; | |
UsbIoPpi = &PeiUsbDevice->UsbIoPpi; | |
// | |
// First get its 4-byte configuration descriptor | |
// | |
Status = PeiUsbGetDescriptor ( | |
PeiServices, | |
UsbIoPpi, | |
(USB_DT_CONFIG << 8), // Value | |
0, // Index | |
4, // Length | |
PeiUsbDevice->ConfigurationData | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "PeiUsbGet Config Descriptor First Failed\n")); | |
return Status; | |
} | |
MicroSecondDelay (USB_GET_CONFIG_DESCRIPTOR_STALL); | |
ConfigDesc = (EFI_USB_CONFIG_DESCRIPTOR *)PeiUsbDevice->ConfigurationData; | |
ConfigDescLength = ConfigDesc->TotalLength; | |
// | |
// Reject if TotalLength even cannot cover itself. | |
// | |
if (ConfigDescLength < OFFSET_OF (EFI_USB_CONFIG_DESCRIPTOR, TotalLength) + sizeof (ConfigDesc->TotalLength)) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Reject if TotalLength exceeds the PeiUsbDevice->ConfigurationData. | |
// | |
if (ConfigDescLength > sizeof (PeiUsbDevice->ConfigurationData)) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Then we get the total descriptors for this configuration | |
// | |
Status = PeiUsbGetDescriptor ( | |
PeiServices, | |
UsbIoPpi, | |
(USB_DT_CONFIG << 8), | |
0, | |
ConfigDescLength, | |
PeiUsbDevice->ConfigurationData | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "PeiUsbGet Config Descriptor all Failed\n")); | |
return Status; | |
} | |
// | |
// Parse this configuration descriptor | |
// First get the current config descriptor; | |
// | |
Status = GetExpectedDescriptor ( | |
PeiUsbDevice->ConfigurationData, | |
ConfigDescLength, | |
USB_DT_CONFIG, | |
(UINT8)sizeof (EFI_USB_CONFIG_DESCRIPTOR), | |
&SkipBytes | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Ptr = PeiUsbDevice->ConfigurationData + SkipBytes; | |
PeiUsbDevice->ConfigDesc = (EFI_USB_CONFIG_DESCRIPTOR *)Ptr; | |
Ptr += sizeof (EFI_USB_CONFIG_DESCRIPTOR); | |
LengthLeft = ConfigDescLength - SkipBytes - sizeof (EFI_USB_CONFIG_DESCRIPTOR); | |
for (InterfaceIndex = 0; InterfaceIndex < PeiUsbDevice->ConfigDesc->NumInterfaces; InterfaceIndex++) { | |
// | |
// Get the interface descriptor | |
// | |
Status = GetExpectedDescriptor ( | |
Ptr, | |
LengthLeft, | |
USB_DT_INTERFACE, | |
(UINT8)sizeof (EFI_USB_INTERFACE_DESCRIPTOR), | |
&SkipBytes | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Ptr += SkipBytes; | |
if (InterfaceIndex == 0) { | |
PeiUsbDevice->InterfaceDesc = (EFI_USB_INTERFACE_DESCRIPTOR *)Ptr; | |
} | |
PeiUsbDevice->InterfaceDescList[InterfaceIndex] = (EFI_USB_INTERFACE_DESCRIPTOR *)Ptr; | |
Ptr += sizeof (EFI_USB_INTERFACE_DESCRIPTOR); | |
LengthLeft -= SkipBytes; | |
LengthLeft -= sizeof (EFI_USB_INTERFACE_DESCRIPTOR); | |
// | |
// Parse all the endpoint descriptor within this interface | |
// | |
NumOfEndpoint = PeiUsbDevice->InterfaceDescList[InterfaceIndex]->NumEndpoints; | |
ASSERT (NumOfEndpoint <= MAX_ENDPOINT); | |
for (Index = 0; Index < NumOfEndpoint; Index++) { | |
// | |
// Get the endpoint descriptor | |
// | |
Status = GetExpectedDescriptor ( | |
Ptr, | |
LengthLeft, | |
USB_DT_ENDPOINT, | |
(UINT8)sizeof (EFI_USB_ENDPOINT_DESCRIPTOR), | |
&SkipBytes | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Ptr += SkipBytes; | |
if (InterfaceIndex == 0) { | |
PeiUsbDevice->EndpointDesc[Index] = (EFI_USB_ENDPOINT_DESCRIPTOR *)Ptr; | |
} | |
PeiUsbDevice->EndpointDescList[InterfaceIndex][Index] = (EFI_USB_ENDPOINT_DESCRIPTOR *)Ptr; | |
Ptr += sizeof (EFI_USB_ENDPOINT_DESCRIPTOR); | |
LengthLeft -= SkipBytes; | |
LengthLeft -= sizeof (EFI_USB_ENDPOINT_DESCRIPTOR); | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Get the start position of next wanted descriptor. | |
@param Buffer Buffer containing data to parse. | |
@param Length Buffer length. | |
@param DescType Descriptor type. | |
@param DescLength Descriptor length. | |
@param ParsedBytes Bytes has been parsed. | |
@retval EFI_SUCCESS Get wanted descriptor successfully. | |
@retval EFI_DEVICE_ERROR Error occurred. | |
**/ | |
EFI_STATUS | |
GetExpectedDescriptor ( | |
IN UINT8 *Buffer, | |
IN UINTN Length, | |
IN UINT8 DescType, | |
IN UINT8 DescLength, | |
OUT UINTN *ParsedBytes | |
) | |
{ | |
USB_DESC_HEAD *Head; | |
UINTN Offset; | |
// | |
// Total length is too small that cannot hold the single descriptor header plus data. | |
// | |
if (Length <= sizeof (USB_DESC_HEAD)) { | |
DEBUG ((DEBUG_ERROR, "GetExpectedDescriptor: met mal-format descriptor, total length = %d!\n", Length)); | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// All the descriptor has a common LTV (Length, Type, Value) | |
// format. Skip the descriptor that isn't of this Type | |
// | |
Offset = 0; | |
Head = (USB_DESC_HEAD *)Buffer; | |
while (Offset < Length - sizeof (USB_DESC_HEAD)) { | |
// | |
// Above condition make sure Head->Len and Head->Type are safe to access | |
// | |
Head = (USB_DESC_HEAD *)&Buffer[Offset]; | |
if (Head->Len == 0) { | |
DEBUG ((DEBUG_ERROR, "GetExpectedDescriptor: met mal-format descriptor, Head->Len = 0!\n")); | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Make sure no overflow when adding Head->Len to Offset. | |
// | |
if (Head->Len > MAX_UINTN - Offset) { | |
DEBUG ((DEBUG_ERROR, "GetExpectedDescriptor: met mal-format descriptor, Head->Len = %d!\n", Head->Len)); | |
return EFI_DEVICE_ERROR; | |
} | |
if (Head->Type == DescType) { | |
break; | |
} | |
Offset += Head->Len; | |
} | |
// | |
// Head->Len is invalid resulting data beyond boundary, or | |
// Descriptor cannot be found: No such type. | |
// | |
if (Length < Offset) { | |
DEBUG ((DEBUG_ERROR, "GetExpectedDescriptor: met mal-format descriptor, Offset/Len = %d/%d!\n", Offset, Length)); | |
return EFI_DEVICE_ERROR; | |
} | |
if ((Head->Type != DescType) || (Head->Len < DescLength)) { | |
DEBUG ((DEBUG_ERROR, "GetExpectedDescriptor: descriptor cannot be found, Header(T/L) = %d/%d!\n", Head->Type, Head->Len)); | |
return EFI_DEVICE_ERROR; | |
} | |
*ParsedBytes = Offset; | |
return EFI_SUCCESS; | |
} | |
/** | |
Send reset signal over the given root hub port. | |
@param PeiServices Describes the list of possible PEI Services. | |
@param UsbHcPpi The pointer of PEI_USB_HOST_CONTROLLER_PPI instance. | |
@param Usb2HcPpi The pointer of PEI_USB2_HOST_CONTROLLER_PPI instance. | |
@param PortNum The port to be reset. | |
@param RetryIndex The retry times. | |
**/ | |
VOID | |
ResetRootPort ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi, | |
IN PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi, | |
IN UINT8 PortNum, | |
IN UINT8 RetryIndex | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
EFI_USB_PORT_STATUS PortStatus; | |
if (Usb2HcPpi != NULL) { | |
MicroSecondDelay (200 * 1000); | |
// | |
// reset root port | |
// | |
Status = Usb2HcPpi->SetRootHubPortFeature ( | |
PeiServices, | |
Usb2HcPpi, | |
PortNum, | |
EfiUsbPortReset | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "SetRootHubPortFeature EfiUsbPortReset Failed\n")); | |
return; | |
} | |
// | |
// Drive the reset signal for at least 50ms. Check USB 2.0 Spec | |
// section 7.1.7.5 for timing requirements. | |
// | |
MicroSecondDelay (USB_SET_ROOT_PORT_RESET_STALL); | |
// | |
// clear reset root port | |
// | |
Status = Usb2HcPpi->ClearRootHubPortFeature ( | |
PeiServices, | |
Usb2HcPpi, | |
PortNum, | |
EfiUsbPortReset | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "ClearRootHubPortFeature EfiUsbPortReset Failed\n")); | |
return; | |
} | |
MicroSecondDelay (USB_CLR_ROOT_PORT_RESET_STALL); | |
// | |
// USB host controller won't clear the RESET bit until | |
// reset is actually finished. | |
// | |
ZeroMem (&PortStatus, sizeof (EFI_USB_PORT_STATUS)); | |
for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { | |
Status = Usb2HcPpi->GetRootHubPortStatus ( | |
PeiServices, | |
Usb2HcPpi, | |
PortNum, | |
&PortStatus | |
); | |
if (EFI_ERROR (Status)) { | |
return; | |
} | |
if (!USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_RESET)) { | |
break; | |
} | |
MicroSecondDelay (USB_WAIT_PORT_STS_CHANGE_STALL); | |
} | |
if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) { | |
DEBUG ((DEBUG_ERROR, "ResetRootPort: reset not finished in time on port %d\n", PortNum)); | |
return; | |
} | |
Usb2HcPpi->ClearRootHubPortFeature ( | |
PeiServices, | |
Usb2HcPpi, | |
PortNum, | |
EfiUsbPortResetChange | |
); | |
Usb2HcPpi->ClearRootHubPortFeature ( | |
PeiServices, | |
Usb2HcPpi, | |
PortNum, | |
EfiUsbPortConnectChange | |
); | |
// | |
// Set port enable | |
// | |
Usb2HcPpi->SetRootHubPortFeature ( | |
PeiServices, | |
Usb2HcPpi, | |
PortNum, | |
EfiUsbPortEnable | |
); | |
Usb2HcPpi->ClearRootHubPortFeature ( | |
PeiServices, | |
Usb2HcPpi, | |
PortNum, | |
EfiUsbPortEnableChange | |
); | |
MicroSecondDelay ((RetryIndex + 1) * 50 * 1000); | |
} else { | |
MicroSecondDelay (200 * 1000); | |
// | |
// reset root port | |
// | |
Status = UsbHcPpi->SetRootHubPortFeature ( | |
PeiServices, | |
UsbHcPpi, | |
PortNum, | |
EfiUsbPortReset | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "SetRootHubPortFeature EfiUsbPortReset Failed\n")); | |
return; | |
} | |
// | |
// Drive the reset signal for at least 50ms. Check USB 2.0 Spec | |
// section 7.1.7.5 for timing requirements. | |
// | |
MicroSecondDelay (USB_SET_ROOT_PORT_RESET_STALL); | |
// | |
// clear reset root port | |
// | |
Status = UsbHcPpi->ClearRootHubPortFeature ( | |
PeiServices, | |
UsbHcPpi, | |
PortNum, | |
EfiUsbPortReset | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "ClearRootHubPortFeature EfiUsbPortReset Failed\n")); | |
return; | |
} | |
MicroSecondDelay (USB_CLR_ROOT_PORT_RESET_STALL); | |
// | |
// USB host controller won't clear the RESET bit until | |
// reset is actually finished. | |
// | |
ZeroMem (&PortStatus, sizeof (EFI_USB_PORT_STATUS)); | |
for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { | |
Status = UsbHcPpi->GetRootHubPortStatus ( | |
PeiServices, | |
UsbHcPpi, | |
PortNum, | |
&PortStatus | |
); | |
if (EFI_ERROR (Status)) { | |
return; | |
} | |
if (!USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_RESET)) { | |
break; | |
} | |
MicroSecondDelay (USB_WAIT_PORT_STS_CHANGE_STALL); | |
} | |
if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) { | |
DEBUG ((DEBUG_ERROR, "ResetRootPort: reset not finished in time on port %d\n", PortNum)); | |
return; | |
} | |
UsbHcPpi->ClearRootHubPortFeature ( | |
PeiServices, | |
UsbHcPpi, | |
PortNum, | |
EfiUsbPortResetChange | |
); | |
UsbHcPpi->ClearRootHubPortFeature ( | |
PeiServices, | |
UsbHcPpi, | |
PortNum, | |
EfiUsbPortConnectChange | |
); | |
// | |
// Set port enable | |
// | |
UsbHcPpi->SetRootHubPortFeature ( | |
PeiServices, | |
UsbHcPpi, | |
PortNum, | |
EfiUsbPortEnable | |
); | |
UsbHcPpi->ClearRootHubPortFeature ( | |
PeiServices, | |
UsbHcPpi, | |
PortNum, | |
EfiUsbPortEnableChange | |
); | |
MicroSecondDelay ((RetryIndex + 1) * 50 * 1000); | |
} | |
return; | |
} |