/** @file The module to produce Usb Bus PPI. Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_INFO, "PeiUsbGet Device Descriptor the %d time Success\n", Retry)); break; } } if (Retry == 3) { DEBUG ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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; }