diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c | 1227 |
1 files changed, 1227 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c new file mode 100644 index 00000000..9dfb9f88 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c @@ -0,0 +1,1227 @@ +/** @file + + Wrapper function for usb host controller interface. + +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "UsbBus.h" + +// +// if RemainingDevicePath== NULL, then all Usb child devices in this bus are wanted. +// Use a shor form Usb class Device Path, which could match any usb device, in WantedUsbIoDPList to indicate all Usb devices +// are wanted Usb devices +// +USB_CLASS_FORMAT_DEVICE_PATH mAllUsbClassDevicePath = { + { + { + MESSAGING_DEVICE_PATH, + MSG_USB_CLASS_DP, + { + (UINT8) (sizeof (USB_CLASS_DEVICE_PATH)), + (UINT8) ((sizeof (USB_CLASS_DEVICE_PATH)) >> 8) + } + }, + 0xffff, // VendorId + 0xffff, // ProductId + 0xff, // DeviceClass + 0xff, // DeviceSubClass + 0xff // DeviceProtocol + }, + + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + END_DEVICE_PATH_LENGTH, + 0 + } + } +}; + + +/** + Get the capability of the host controller. + + @param UsbBus The usb driver. + @param MaxSpeed The maximum speed this host controller supports. + @param NumOfPort The number of the root hub port. + @param Is64BitCapable Whether this controller support 64 bit addressing. + + @retval EFI_SUCCESS The host controller capability is returned. + @retval Others Failed to retrieve the host controller capability. + +**/ +EFI_STATUS +UsbHcGetCapability ( + IN USB_BUS *UsbBus, + OUT UINT8 *MaxSpeed, + OUT UINT8 *NumOfPort, + OUT UINT8 *Is64BitCapable + ) +{ + EFI_STATUS Status; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->GetCapability ( + UsbBus->Usb2Hc, + MaxSpeed, + NumOfPort, + Is64BitCapable + ); + + } else { + Status = UsbBus->UsbHc->GetRootHubPortNumber (UsbBus->UsbHc, NumOfPort); + + *MaxSpeed = EFI_USB_SPEED_FULL; + *Is64BitCapable = (UINT8) FALSE; + } + + return Status; +} + + + + + + + + + + +/** + Get the root hub port state. + + @param UsbBus The USB bus driver. + @param PortIndex The index of port. + @param PortStatus The variable to save port state. + + @retval EFI_SUCCESS The root port state is returned in. + @retval Others Failed to get the root hub port state. + +**/ +EFI_STATUS +UsbHcGetRootHubPortStatus ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + OUT EFI_USB_PORT_STATUS *PortStatus + ) +{ + EFI_STATUS Status; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->GetRootHubPortStatus (UsbBus->Usb2Hc, PortIndex, PortStatus); + } else { + Status = UsbBus->UsbHc->GetRootHubPortStatus (UsbBus->UsbHc, PortIndex, PortStatus); + } + + return Status; +} + + +/** + Set the root hub port feature. + + @param UsbBus The USB bus driver. + @param PortIndex The port index. + @param Feature The port feature to set. + + @retval EFI_SUCCESS The port feature is set. + @retval Others Failed to set port feature. + +**/ +EFI_STATUS +UsbHcSetRootHubPortFeature ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + IN EFI_USB_PORT_FEATURE Feature + ) +{ + EFI_STATUS Status; + + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->SetRootHubPortFeature (UsbBus->Usb2Hc, PortIndex, Feature); + } else { + Status = UsbBus->UsbHc->SetRootHubPortFeature (UsbBus->UsbHc, PortIndex, Feature); + } + + return Status; +} + + +/** + Clear the root hub port feature. + + @param UsbBus The USB bus driver. + @param PortIndex The port index. + @param Feature The port feature to clear. + + @retval EFI_SUCCESS The port feature is clear. + @retval Others Failed to clear port feature. + +**/ +EFI_STATUS +UsbHcClearRootHubPortFeature ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + IN EFI_USB_PORT_FEATURE Feature + ) +{ + EFI_STATUS Status; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->ClearRootHubPortFeature (UsbBus->Usb2Hc, PortIndex, Feature); + } else { + Status = UsbBus->UsbHc->ClearRootHubPortFeature (UsbBus->UsbHc, PortIndex, Feature); + } + + return Status; +} + + +/** + Execute a control transfer to the device. + + @param UsbBus The USB bus driver. + @param DevAddr The device address. + @param DevSpeed The device speed. + @param MaxPacket Maximum packet size of endpoint 0. + @param Request The control transfer request. + @param Direction The direction of data stage. + @param Data The buffer holding data. + @param DataLength The length of the data. + @param TimeOut Timeout (in ms) to wait until timeout. + @param Translator The transaction translator for low/full speed device. + @param UsbResult The result of transfer. + + @retval EFI_SUCCESS The control transfer finished without error. + @retval Others The control transfer failed, reason returned in UsbReslt. + +**/ +EFI_STATUS +UsbHcControlTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION Direction, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *UsbResult + ) +{ + EFI_STATUS Status; + BOOLEAN IsSlowDevice; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->ControlTransfer ( + UsbBus->Usb2Hc, + DevAddr, + DevSpeed, + MaxPacket, + Request, + Direction, + Data, + DataLength, + TimeOut, + Translator, + UsbResult + ); + + } else { + IsSlowDevice = (BOOLEAN)(EFI_USB_SPEED_LOW == DevSpeed); + Status = UsbBus->UsbHc->ControlTransfer ( + UsbBus->UsbHc, + DevAddr, + IsSlowDevice, + (UINT8) MaxPacket, + Request, + Direction, + Data, + DataLength, + TimeOut, + UsbResult + ); + } + + return Status; +} + + +/** + Execute a bulk transfer to the device's endpoint. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param BufferNum The number of data buffer. + @param Data Array of pointers to data buffer. + @param DataLength The length of data buffer. + @param DataToggle On input, the initial data toggle to use, also return + the next toggle on output. + @param TimeOut The time to wait until timeout. + @param Translator The transaction translator for low/full speed device. + @param UsbResult The result of USB execution. + + @retval EFI_SUCCESS The bulk transfer is finished without error. + @retval Others Failed to execute bulk transfer, result in UsbResult. + +**/ +EFI_STATUS +UsbHcBulkTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN UINT8 BufferNum, + IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM], + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *UsbResult + ) +{ + EFI_STATUS Status; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->BulkTransfer ( + UsbBus->Usb2Hc, + DevAddr, + EpAddr, + DevSpeed, + MaxPacket, + BufferNum, + Data, + DataLength, + DataToggle, + TimeOut, + Translator, + UsbResult + ); + } else { + Status = UsbBus->UsbHc->BulkTransfer ( + UsbBus->UsbHc, + DevAddr, + EpAddr, + (UINT8) MaxPacket, + *Data, + DataLength, + DataToggle, + TimeOut, + UsbResult + ); + } + + return Status; +} + + +/** + Queue or cancel an asynchronous interrupt transfer. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param IsNewTransfer Whether this is a new request. If not, cancel the old + request. + @param DataToggle Data toggle to use on input, next toggle on output. + @param PollingInterval The interval to poll the interrupt transfer (in ms). + @param DataLength The length of periodical data receive. + @param Translator The transaction translator for low/full speed device. + @param Callback Function to call when data is received. + @param Context The context to the callback. + + @retval EFI_SUCCESS The asynchronous transfer is queued. + @retval Others Failed to queue the transfer. + +**/ +EFI_STATUS +UsbHcAsyncInterruptTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN BOOLEAN IsNewTransfer, + IN OUT UINT8 *DataToggle, + IN UINTN PollingInterval, + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context OPTIONAL + ) +{ + EFI_STATUS Status; + BOOLEAN IsSlowDevice; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->AsyncInterruptTransfer ( + UsbBus->Usb2Hc, + DevAddr, + EpAddr, + DevSpeed, + MaxPacket, + IsNewTransfer, + DataToggle, + PollingInterval, + DataLength, + Translator, + Callback, + Context + ); + } else { + IsSlowDevice = (BOOLEAN)(EFI_USB_SPEED_LOW == DevSpeed); + + Status = UsbBus->UsbHc->AsyncInterruptTransfer ( + UsbBus->UsbHc, + DevAddr, + EpAddr, + IsSlowDevice, + (UINT8) MaxPacket, + IsNewTransfer, + DataToggle, + PollingInterval, + DataLength, + Callback, + Context + ); + } + + return Status; +} + + +/** + Execute a synchronous interrupt transfer to the target endpoint. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param Data Pointer to data buffer. + @param DataLength The length of data buffer. + @param DataToggle On input, the initial data toggle to use, also return + the next toggle on output. + @param TimeOut The time to wait until timeout. + @param Translator The transaction translator for low/full speed device. + @param UsbResult The result of USB execution. + + @retval EFI_SUCCESS The synchronous interrupt transfer is OK. + @retval Others Failed to execute the synchronous interrupt transfer. + +**/ +EFI_STATUS +UsbHcSyncInterruptTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *UsbResult + ) +{ + EFI_STATUS Status; + BOOLEAN IsSlowDevice; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->SyncInterruptTransfer ( + UsbBus->Usb2Hc, + DevAddr, + EpAddr, + DevSpeed, + MaxPacket, + Data, + DataLength, + DataToggle, + TimeOut, + Translator, + UsbResult + ); + } else { + IsSlowDevice = (BOOLEAN) ((EFI_USB_SPEED_LOW == DevSpeed) ? TRUE : FALSE); + Status = UsbBus->UsbHc->SyncInterruptTransfer ( + UsbBus->UsbHc, + DevAddr, + EpAddr, + IsSlowDevice, + (UINT8) MaxPacket, + Data, + DataLength, + DataToggle, + TimeOut, + UsbResult + ); + } + + return Status; +} + + + + + + + + +/** + Open the USB host controller protocol BY_CHILD. + + @param Bus The USB bus driver. + @param Child The child handle. + + @return The open protocol return. + +**/ +EFI_STATUS +UsbOpenHostProtoByChild ( + IN USB_BUS *Bus, + IN EFI_HANDLE Child + ) +{ + EFI_USB_HC_PROTOCOL *UsbHc; + EFI_USB2_HC_PROTOCOL *Usb2Hc; + EFI_STATUS Status; + + if (Bus->Usb2Hc != NULL) { + Status = gBS->OpenProtocol ( + Bus->HostHandle, + &gEfiUsb2HcProtocolGuid, + (VOID **) &Usb2Hc, + mUsbBusDriverBinding.DriverBindingHandle, + Child, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + } else { + Status = gBS->OpenProtocol ( + Bus->HostHandle, + &gEfiUsbHcProtocolGuid, + (VOID **) &UsbHc, + mUsbBusDriverBinding.DriverBindingHandle, + Child, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } + + return Status; +} + + +/** + Close the USB host controller protocol BY_CHILD. + + @param Bus The USB bus driver. + @param Child The child handle. + +**/ +VOID +UsbCloseHostProtoByChild ( + IN USB_BUS *Bus, + IN EFI_HANDLE Child + ) +{ + if (Bus->Usb2Hc != NULL) { + gBS->CloseProtocol ( + Bus->HostHandle, + &gEfiUsb2HcProtocolGuid, + mUsbBusDriverBinding.DriverBindingHandle, + Child + ); + + } else { + gBS->CloseProtocol ( + Bus->HostHandle, + &gEfiUsbHcProtocolGuid, + mUsbBusDriverBinding.DriverBindingHandle, + Child + ); + } +} + + +/** + return the current TPL, copied from the EDKII glue lib. + + @param VOID. + + @return Current TPL. + +**/ +EFI_TPL +UsbGetCurrentTpl ( + VOID + ) +{ + EFI_TPL Tpl; + + Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + gBS->RestoreTPL (Tpl); + + return Tpl; +} + +/** + Create a new device path which only contain the first Usb part of the DevicePath. + + @param DevicePath A full device path which contain the usb nodes. + + @return A new device path which only contain the Usb part of the DevicePath. + +**/ +EFI_DEVICE_PATH_PROTOCOL * +EFIAPI +GetUsbDPFromFullDP ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *UsbDevicePathPtr; + EFI_DEVICE_PATH_PROTOCOL *UsbDevicePathBeginPtr; + EFI_DEVICE_PATH_PROTOCOL *UsbDevicePathEndPtr; + UINTN Size; + + // + // Get the Usb part first Begin node in full device path + // + UsbDevicePathBeginPtr = DevicePath; + while ( (!IsDevicePathEnd (UsbDevicePathBeginPtr))&& + ((UsbDevicePathBeginPtr->Type != MESSAGING_DEVICE_PATH) || + (UsbDevicePathBeginPtr->SubType != MSG_USB_DP && + UsbDevicePathBeginPtr->SubType != MSG_USB_CLASS_DP + && UsbDevicePathBeginPtr->SubType != MSG_USB_WWID_DP + ))) { + + UsbDevicePathBeginPtr = NextDevicePathNode(UsbDevicePathBeginPtr); + } + + // + // Get the Usb part first End node in full device path + // + UsbDevicePathEndPtr = UsbDevicePathBeginPtr; + while ((!IsDevicePathEnd (UsbDevicePathEndPtr))&& + (UsbDevicePathEndPtr->Type == MESSAGING_DEVICE_PATH) && + (UsbDevicePathEndPtr->SubType == MSG_USB_DP || + UsbDevicePathEndPtr->SubType == MSG_USB_CLASS_DP + || UsbDevicePathEndPtr->SubType == MSG_USB_WWID_DP + )) { + + UsbDevicePathEndPtr = NextDevicePathNode(UsbDevicePathEndPtr); + } + + Size = GetDevicePathSize (UsbDevicePathBeginPtr); + Size -= GetDevicePathSize (UsbDevicePathEndPtr); + if (Size ==0){ + // + // The passed in DevicePath does not contain the usb nodes + // + return NULL; + } + + // + // Create a new device path which only contain the above Usb part + // + UsbDevicePathPtr = AllocateZeroPool (Size + sizeof (EFI_DEVICE_PATH_PROTOCOL)); + ASSERT (UsbDevicePathPtr != NULL); + CopyMem (UsbDevicePathPtr, UsbDevicePathBeginPtr, Size); + // + // Append end device path node + // + UsbDevicePathEndPtr = (EFI_DEVICE_PATH_PROTOCOL *) ((UINTN) UsbDevicePathPtr + Size); + SetDevicePathEndNode (UsbDevicePathEndPtr); + return UsbDevicePathPtr; +} + +/** + Check whether a usb device path is in a DEVICE_PATH_LIST_ITEM list. + + @param UsbDP a usb device path of DEVICE_PATH_LIST_ITEM. + @param UsbIoDPList a DEVICE_PATH_LIST_ITEM list. + + @retval TRUE there is a DEVICE_PATH_LIST_ITEM in UsbIoDPList which contains the passed in UsbDP. + @retval FALSE there is no DEVICE_PATH_LIST_ITEM in UsbIoDPList which contains the passed in UsbDP. + +**/ +BOOLEAN +EFIAPI +SearchUsbDPInList ( + IN EFI_DEVICE_PATH_PROTOCOL *UsbDP, + IN LIST_ENTRY *UsbIoDPList + ) +{ + LIST_ENTRY *ListIndex; + DEVICE_PATH_LIST_ITEM *ListItem; + BOOLEAN Found; + UINTN UsbDpDevicePathSize; + + // + // Check that UsbDP and UsbIoDPList are valid + // + if ((UsbIoDPList == NULL) || (UsbDP == NULL)) { + return FALSE; + } + + Found = FALSE; + ListIndex = UsbIoDPList->ForwardLink; + while (ListIndex != UsbIoDPList){ + ListItem = CR(ListIndex, DEVICE_PATH_LIST_ITEM, Link, DEVICE_PATH_LIST_ITEM_SIGNATURE); + // + // Compare DEVICE_PATH_LIST_ITEM.DevicePath[] + // + ASSERT (ListItem->DevicePath != NULL); + + UsbDpDevicePathSize = GetDevicePathSize (UsbDP); + if (UsbDpDevicePathSize == GetDevicePathSize (ListItem->DevicePath)) { + if ((CompareMem (UsbDP, ListItem->DevicePath, UsbDpDevicePathSize)) == 0) { + Found = TRUE; + break; + } + } + ListIndex = ListIndex->ForwardLink; + } + + return Found; +} + +/** + Add a usb device path into the DEVICE_PATH_LIST_ITEM list. + + @param UsbDP a usb device path of DEVICE_PATH_LIST_ITEM. + @param UsbIoDPList a DEVICE_PATH_LIST_ITEM list. + + @retval EFI_INVALID_PARAMETER If parameters are invalid, return this value. + @retval EFI_SUCCESS If Add operation is successful, return this value. + +**/ +EFI_STATUS +EFIAPI +AddUsbDPToList ( + IN EFI_DEVICE_PATH_PROTOCOL *UsbDP, + IN LIST_ENTRY *UsbIoDPList + ) +{ + DEVICE_PATH_LIST_ITEM *ListItem; + + // + // Check that UsbDP and UsbIoDPList are valid + // + if ((UsbIoDPList == NULL) || (UsbDP == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (SearchUsbDPInList (UsbDP, UsbIoDPList)){ + return EFI_SUCCESS; + } + + // + // Prepare the usbio device path DEVICE_PATH_LIST_ITEM structure. + // + ListItem = AllocateZeroPool (sizeof (DEVICE_PATH_LIST_ITEM)); + ASSERT (ListItem != NULL); + ListItem->Signature = DEVICE_PATH_LIST_ITEM_SIGNATURE; + ListItem->DevicePath = DuplicateDevicePath (UsbDP); + + InsertTailList (UsbIoDPList, &ListItem->Link); + + return EFI_SUCCESS; +} + +/** + Check whether usb device, whose interface is UsbIf, matches the usb class which indicated by + UsbClassDevicePathPtr whose is a short form usb class device path. + + @param UsbClassDevicePathPtr a short form usb class device path. + @param UsbIf a usb device interface. + + @retval TRUE the usb device match the usb class. + @retval FALSE the usb device does not match the usb class. + +**/ +BOOLEAN +EFIAPI +MatchUsbClass ( + IN USB_CLASS_DEVICE_PATH *UsbClassDevicePathPtr, + IN USB_INTERFACE *UsbIf + ) +{ + USB_INTERFACE_DESC *IfDesc; + EFI_USB_INTERFACE_DESCRIPTOR *ActIfDesc; + EFI_USB_DEVICE_DESCRIPTOR *DevDesc; + + + if ((UsbClassDevicePathPtr->Header.Type != MESSAGING_DEVICE_PATH) || + (UsbClassDevicePathPtr->Header.SubType != MSG_USB_CLASS_DP)){ + ASSERT (0); + return FALSE; + } + + IfDesc = UsbIf->IfDesc; + ASSERT (IfDesc->ActiveIndex < USB_MAX_INTERFACE_SETTING); + ActIfDesc = &(IfDesc->Settings[IfDesc->ActiveIndex]->Desc); + DevDesc = &(UsbIf->Device->DevDesc->Desc); + + // + // If connect class policy, determine whether to create device handle by the five fields + // in class device path node. + // + // In addition, hub interface is always matched for this policy. + // + if ((ActIfDesc->InterfaceClass == USB_HUB_CLASS_CODE) && + (ActIfDesc->InterfaceSubClass == USB_HUB_SUBCLASS_CODE)) { + return TRUE; + } + + // + // If vendor id or product id is 0xffff, they will be ignored. + // + if ((UsbClassDevicePathPtr->VendorId == 0xffff || UsbClassDevicePathPtr->VendorId == DevDesc->IdVendor) && + (UsbClassDevicePathPtr->ProductId == 0xffff || UsbClassDevicePathPtr->ProductId == DevDesc->IdProduct)) { + + // + // If Class in Device Descriptor is set to 0, the counterparts in interface should be checked. + // + if (DevDesc->DeviceClass == 0) { + if ((UsbClassDevicePathPtr->DeviceClass == ActIfDesc->InterfaceClass || + UsbClassDevicePathPtr->DeviceClass == 0xff) && + (UsbClassDevicePathPtr->DeviceSubClass == ActIfDesc->InterfaceSubClass || + UsbClassDevicePathPtr->DeviceSubClass == 0xff) && + (UsbClassDevicePathPtr->DeviceProtocol == ActIfDesc->InterfaceProtocol || + UsbClassDevicePathPtr->DeviceProtocol == 0xff)) { + return TRUE; + } + + } else if ((UsbClassDevicePathPtr->DeviceClass == DevDesc->DeviceClass || + UsbClassDevicePathPtr->DeviceClass == 0xff) && + (UsbClassDevicePathPtr->DeviceSubClass == DevDesc->DeviceSubClass || + UsbClassDevicePathPtr->DeviceSubClass == 0xff) && + (UsbClassDevicePathPtr->DeviceProtocol == DevDesc->DeviceProtocol || + UsbClassDevicePathPtr->DeviceProtocol == 0xff)) { + + return TRUE; + } + } + + return FALSE; +} + +/** + Check whether usb device, whose interface is UsbIf, matches the usb WWID requirement which indicated by + UsbWWIDDevicePathPtr whose is a short form usb WWID device path. + + @param UsbWWIDDevicePathPtr a short form usb WWID device path. + @param UsbIf a usb device interface. + + @retval TRUE the usb device match the usb WWID requirement. + @retval FALSE the usb device does not match the usb WWID requirement. + +**/ +BOOLEAN +MatchUsbWwid ( + IN USB_WWID_DEVICE_PATH *UsbWWIDDevicePathPtr, + IN USB_INTERFACE *UsbIf + ) +{ + USB_INTERFACE_DESC *IfDesc; + EFI_USB_INTERFACE_DESCRIPTOR *ActIfDesc; + EFI_USB_DEVICE_DESCRIPTOR *DevDesc; + EFI_USB_STRING_DESCRIPTOR *StrDesc; + UINT16 Index; + CHAR16 *CompareStr; + UINTN CompareLen; + UINTN Length; + + if ((UsbWWIDDevicePathPtr->Header.Type != MESSAGING_DEVICE_PATH) || + (UsbWWIDDevicePathPtr->Header.SubType != MSG_USB_WWID_DP )){ + ASSERT (0); + return FALSE; + } + + IfDesc = UsbIf->IfDesc; + ASSERT (IfDesc->ActiveIndex < USB_MAX_INTERFACE_SETTING); + ActIfDesc = &(IfDesc->Settings[IfDesc->ActiveIndex]->Desc); + DevDesc = &(UsbIf->Device->DevDesc->Desc); + + // + // In addition, Hub interface is always matched for this policy. + // + if ((ActIfDesc->InterfaceClass == USB_HUB_CLASS_CODE) && + (ActIfDesc->InterfaceSubClass == USB_HUB_SUBCLASS_CODE)) { + return TRUE; + } + + // + // Check Vendor Id, Product Id and Interface Number. + // + if ((DevDesc->IdVendor != UsbWWIDDevicePathPtr->VendorId) || + (DevDesc->IdProduct != UsbWWIDDevicePathPtr->ProductId) || + (ActIfDesc->InterfaceNumber != UsbWWIDDevicePathPtr->InterfaceNumber)) { + return FALSE; + } + + // + // Check SerialNumber. + // + if (DevDesc->StrSerialNumber == 0) { + return FALSE; + } + + // + // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters. + // + CompareStr = (CHAR16 *) (UINTN) (UsbWWIDDevicePathPtr + 1); + CompareLen = (DevicePathNodeLength (UsbWWIDDevicePathPtr) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16); + if (CompareStr[CompareLen - 1] == L'\0') { + CompareLen--; + } + + // + // Compare serial number in each supported language. + // + for (Index = 0; Index < UsbIf->Device->TotalLangId; Index++) { + StrDesc = UsbGetOneString (UsbIf->Device, DevDesc->StrSerialNumber, UsbIf->Device->LangId[Index]); + if (StrDesc == NULL) { + continue; + } + + Length = (StrDesc->Length - 2) / sizeof (CHAR16); + if ((Length >= CompareLen) && + (CompareMem (StrDesc->String + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) { + return TRUE; + } + } + + return FALSE; +} + +/** + Free a DEVICE_PATH_LIST_ITEM list. + + @param UsbIoDPList a DEVICE_PATH_LIST_ITEM list pointer. + + @retval EFI_INVALID_PARAMETER If parameters are invalid, return this value. + @retval EFI_SUCCESS If free operation is successful, return this value. + +**/ +EFI_STATUS +EFIAPI +UsbBusFreeUsbDPList ( + IN LIST_ENTRY *UsbIoDPList + ) +{ + LIST_ENTRY *ListIndex; + DEVICE_PATH_LIST_ITEM *ListItem; + + // + // Check that ControllerHandle is a valid handle + // + if (UsbIoDPList == NULL) { + return EFI_INVALID_PARAMETER; + } + + ListIndex = UsbIoDPList->ForwardLink; + while (ListIndex != UsbIoDPList){ + ListItem = CR(ListIndex, DEVICE_PATH_LIST_ITEM, Link, DEVICE_PATH_LIST_ITEM_SIGNATURE); + // + // Free DEVICE_PATH_LIST_ITEM.DevicePath[] + // + if (ListItem->DevicePath != NULL){ + FreePool(ListItem->DevicePath); + } + // + // Free DEVICE_PATH_LIST_ITEM itself + // + ListIndex = ListIndex->ForwardLink; + RemoveEntryList (&ListItem->Link); + FreePool (ListItem); + } + + InitializeListHead (UsbIoDPList); + return EFI_SUCCESS; +} + +/** + Store a wanted usb child device info (its Usb part of device path) which is indicated by + RemainingDevicePath in a Usb bus which is indicated by UsbBusId. + + @param UsbBusId Point to EFI_USB_BUS_PROTOCOL interface. + @param RemainingDevicePath The remaining device patch. + + @retval EFI_SUCCESS Add operation is successful. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +UsbBusAddWantedUsbIoDP ( + IN EFI_USB_BUS_PROTOCOL *UsbBusId, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + USB_BUS *Bus; + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePathPtr; + + // + // Check whether remaining device path is valid + // + if (RemainingDevicePath != NULL && !IsDevicePathEnd (RemainingDevicePath)) { + if ((RemainingDevicePath->Type != MESSAGING_DEVICE_PATH) || + (RemainingDevicePath->SubType != MSG_USB_DP && + RemainingDevicePath->SubType != MSG_USB_CLASS_DP + && RemainingDevicePath->SubType != MSG_USB_WWID_DP + )) { + return EFI_INVALID_PARAMETER; + } + } + + if (UsbBusId == NULL){ + return EFI_INVALID_PARAMETER; + } + + Bus = USB_BUS_FROM_THIS (UsbBusId); + + if (RemainingDevicePath == NULL) { + // + // RemainingDevicePath == NULL means all Usb devices in this bus are wanted. + // Here use a Usb class Device Path in WantedUsbIoDPList to indicate all Usb devices + // are wanted Usb devices + // + Status = UsbBusFreeUsbDPList (&Bus->WantedUsbIoDPList); + ASSERT (!EFI_ERROR (Status)); + DevicePathPtr = DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL *) &mAllUsbClassDevicePath); + } else if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // Create new Usb device path according to the usb part in remaining device path + // + DevicePathPtr = GetUsbDPFromFullDP (RemainingDevicePath); + } else { + // + // If RemainingDevicePath is the End of Device Path Node, + // skip enumerate any device and return EFI_SUCCESS + // + return EFI_SUCCESS; + } + + ASSERT (DevicePathPtr != NULL); + Status = AddUsbDPToList (DevicePathPtr, &Bus->WantedUsbIoDPList); + ASSERT (!EFI_ERROR (Status)); + FreePool (DevicePathPtr); + return EFI_SUCCESS; +} + +/** + Check whether a usb child device is the wanted device in a bus. + + @param Bus The Usb bus's private data pointer. + @param UsbIf The usb child device interface. + + @retval True If a usb child device is the wanted device in a bus. + @retval False If a usb child device is *NOT* the wanted device in a bus. + +**/ +BOOLEAN +EFIAPI +UsbBusIsWantedUsbIO ( + IN USB_BUS *Bus, + IN USB_INTERFACE *UsbIf + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePathPtr; + LIST_ENTRY *WantedUsbIoDPListPtr; + LIST_ENTRY *WantedListIndex; + DEVICE_PATH_LIST_ITEM *WantedListItem; + BOOLEAN DoConvert; + UINTN FirstDevicePathSize; + + // + // Check whether passed in parameters are valid + // + if ((UsbIf == NULL) || (Bus == NULL)) { + return FALSE; + } + // + // Check whether UsbIf is Hub + // + if (UsbIf->IsHub) { + return TRUE; + } + + // + // Check whether all Usb devices in this bus are wanted + // + if (SearchUsbDPInList ((EFI_DEVICE_PATH_PROTOCOL *)&mAllUsbClassDevicePath, &Bus->WantedUsbIoDPList)){ + return TRUE; + } + + // + // Check whether the Usb device match any item in WantedUsbIoDPList + // + WantedUsbIoDPListPtr = &Bus->WantedUsbIoDPList; + // + // Create new Usb device path according to the usb part in UsbIo full device path + // + DevicePathPtr = GetUsbDPFromFullDP (UsbIf->DevicePath); + ASSERT (DevicePathPtr != NULL); + + DoConvert = FALSE; + WantedListIndex = WantedUsbIoDPListPtr->ForwardLink; + while (WantedListIndex != WantedUsbIoDPListPtr){ + WantedListItem = CR(WantedListIndex, DEVICE_PATH_LIST_ITEM, Link, DEVICE_PATH_LIST_ITEM_SIGNATURE); + ASSERT (WantedListItem->DevicePath->Type == MESSAGING_DEVICE_PATH); + switch (WantedListItem->DevicePath->SubType) { + case MSG_USB_DP: + FirstDevicePathSize = GetDevicePathSize (WantedListItem->DevicePath); + if (FirstDevicePathSize == GetDevicePathSize (DevicePathPtr)) { + if (CompareMem ( + WantedListItem->DevicePath, + DevicePathPtr, + GetDevicePathSize (DevicePathPtr)) == 0 + ) { + DoConvert = TRUE; + } + } + break; + case MSG_USB_CLASS_DP: + if (MatchUsbClass((USB_CLASS_DEVICE_PATH *)WantedListItem->DevicePath, UsbIf)) { + DoConvert = TRUE; + } + break; + case MSG_USB_WWID_DP: + if (MatchUsbWwid((USB_WWID_DEVICE_PATH *)WantedListItem->DevicePath, UsbIf)) { + DoConvert = TRUE; + } + break; + default: + ASSERT (0); + break; + } + + if (DoConvert) { + break; + } + + WantedListIndex = WantedListIndex->ForwardLink; + } + gBS->FreePool (DevicePathPtr); + + // + // Check whether the new Usb device path is wanted + // + if (DoConvert){ + return TRUE; + } else { + return FALSE; + } +} + +/** + Recursively connect every wanted usb child device to ensure they all fully connected. + Check all the child Usb IO handles in this bus, recursively connecte if it is wanted usb child device. + + @param UsbBusId Point to EFI_USB_BUS_PROTOCOL interface. + + @retval EFI_SUCCESS Connect is done successfully. + @retval EFI_INVALID_PARAMETER The parameter is invalid. + +**/ +EFI_STATUS +EFIAPI +UsbBusRecursivelyConnectWantedUsbIo ( + IN EFI_USB_BUS_PROTOCOL *UsbBusId + ) +{ + USB_BUS *Bus; + EFI_STATUS Status; + UINTN Index; + EFI_USB_IO_PROTOCOL *UsbIo; + USB_INTERFACE *UsbIf; + UINTN UsbIoHandleCount; + EFI_HANDLE *UsbIoBuffer; + EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath; + + if (UsbBusId == NULL){ + return EFI_INVALID_PARAMETER; + } + + Bus = USB_BUS_FROM_THIS (UsbBusId); + + // + // Get all Usb IO handles in system + // + UsbIoHandleCount = 0; + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiUsbIoProtocolGuid, NULL, &UsbIoHandleCount, &UsbIoBuffer); + if (Status == EFI_NOT_FOUND || UsbIoHandleCount == 0) { + return EFI_SUCCESS; + } + ASSERT (!EFI_ERROR (Status)); + + for (Index = 0; Index < UsbIoHandleCount; Index++) { + // + // Check whether the USB IO handle is a child of this bus + // Note: The usb child handle maybe invalid because of hot plugged out during the loop + // + UsbIoDevicePath = NULL; + Status = gBS->HandleProtocol (UsbIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &UsbIoDevicePath); + if (EFI_ERROR (Status) || UsbIoDevicePath == NULL) { + continue; + } + if (CompareMem ( + UsbIoDevicePath, + Bus->DevicePath, + (GetDevicePathSize (Bus->DevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL)) + ) != 0) { + continue; + } + + // + // Get the child Usb IO interface + // + Status = gBS->HandleProtocol( + UsbIoBuffer[Index], + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIo + ); + if (EFI_ERROR (Status)) { + continue; + } + UsbIf = USB_INTERFACE_FROM_USBIO (UsbIo); + + if (UsbBusIsWantedUsbIO (Bus, UsbIf)) { + if (!UsbIf->IsManaged) { + // + // Recursively connect the wanted Usb Io handle + // + DEBUG ((EFI_D_INFO, "UsbBusRecursivelyConnectWantedUsbIo: TPL before connect is %d\n", (UINT32)UsbGetCurrentTpl ())); + Status = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE); + UsbIf->IsManaged = (BOOLEAN)!EFI_ERROR (Status); + DEBUG ((EFI_D_INFO, "UsbBusRecursivelyConnectWantedUsbIo: TPL after connect is %d\n", (UINT32)UsbGetCurrentTpl())); + } + } + } + + FreePool (UsbIoBuffer); + + return EFI_SUCCESS; +} + |