Step to UEFI (71) ----- 获得 USB 设备的 PID 和 VID

一个问题:如何获得 Shell 下面所有 USB 设备的 PID 和 VID ?

首先在网上搜索一下,找到【参考1】,上面使用了 USBIO Protocol。

typedef struct _EFI_USB_IO_PROTOCOL {
  EFI_USB_IO_CONTROL_TRANSFER            UsbControlTransfer;
  EFI_USB_IO_BULK_TRANSFER               UsbBulkTransfer;
  EFI_USB_IO_ASYNC_INTERRUPT_TRANSFER    UsbAsyncInterruptTransfer;
  EFI_USB_IO_SYNC_INTERRPUT_TRANSFER     UsbSyncInterruptTransfer;
  EFI_USB_IO_ISOCHRONOUS_TRANSFER        UsbIsochronousTransfer;
  EFI_USB_IO_ASYNC_ISOCHRONOUS_TRANSFER  UsbAsyncIsochronousTransfer;
  EFI_USB_IO_GET_DEVICE_DESCRIPTOR       UsbGetDeviceDescriptor;
  EFI_USB_IO_GET_CONFIG_DESCRIPTOR       UsbGetConfigDescriptor;
  EFI_USB_IO_GET_INTERFACE_DESCRIPTOR    UsbGetInterfaceDescriptor;
  EFI_USB_IO_GET_ENDPOINT_DESCRIPTOR     UsbGetEndpointDescriptor;
  EFI_USB_IO_GET_STRING_DESCRIPTOR       UsbGetStringDescriptor;
  EFI_USB_IO_GET_SUPPORTED_LANGUAGES     UsbGetSupportedLanguages;
  EFI_USB_IO_PORT_RESET                  UsbPortReset;
} EFI_USB_IO_PROTOCOL;

来自【参考2】。

我们在实体机上用 dh –p USBIO 命令看一下(注意:EDK自带的模拟环境没有设备挂USBIO 这个 Protocol,所以只能用实体机查看)。可以看到 USB设备上都有这个 Protocol,于是,一切都简单了。

image001

我们再把实体机启动到 Windows中,也是同样的顺序,分别是 USB Hub (这是我外接的一个 USB HUB),USB鼠标,USB键盘(这个键盘是一个复合设备,所以会显示为2个),最后是我的 U盘。多说两句对照上面的截图,我们可以看到USB鼠标上附加了一个 SimplePointer Protocol,键盘上有 TxtinEx/Txtin/ConIn Protocol ,U盘上附加了 DiskIo/BlkIo Protocol,后面我们会分别研究一下这些 Protocol 的使用。

image002

每一个USB设备上都有这个 Protocol 所以我们要用 LocateHandleBuffer 和HandleProtocol 这样的组合把所有有这个 Protocol 的设备都取下来,然后调用 UsbIO 中的UsbGetDeviceDescriptor 即可。
弄清楚原理整个程序还是比较简单的,如下:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <Protocol/UsbIo.h>

extern EFI_BOOT_SERVICES         *gBS;

EFI_GUID  gEfiUsbIoProtocolGuid   = 
	{ 0x2B2F68D6, 0x0CD2, 0x44CF, 
		{ 0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75 }};

UINTN GetUSB( )
{
  EFI_STATUS  Status;
  UINTN       HandleIndex, HandleCount;
  EFI_HANDLE  *DevicePathHandleBuffer = NULL;
  EFI_USB_IO_PROTOCOL 			*USBIO;
  EFI_USB_DEVICE_DESCRIPTOR     DeviceDescriptor;

  Status = gBS->LocateHandleBuffer(
                  ByProtocol,
                  &gEfiUsbIoProtocolGuid,
                  NULL,
                  &HandleCount,
                  &DevicePathHandleBuffer);

  if (EFI_ERROR(Status)) 
  {
    Print(L"ERROR : Get USBIO count fail.\n");
    return 0;
  }   

  for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) 
  { 
     Status = gBS->HandleProtocol(
                      DevicePathHandleBuffer[HandleIndex],
                      &gEfiUsbIoProtocolGuid,
                      (VOID**)&USBIO);

      if (EFI_ERROR(Status))
      {
        Print(L"ERROR : Open USBIO fail.\n");
        gBS->FreePool(DevicePathHandleBuffer);  
        return 0;
      }

      Status = USBIO->UsbGetDeviceDescriptor(USBIO, &DeviceDescriptor);     
      if (EFI_ERROR(Status))
      {
        Print(L"ERROR : Usb Get Device Descriptor fail.\n");
        gBS->FreePool(DevicePathHandleBuffer);  
        return 0;
      }

      Print(L"VendorID = %04X, ProductID = %04X\n", 
                              DeviceDescriptor.IdVendor, 
                              DeviceDescriptor.IdProduct);      

  }
  gBS->FreePool(DevicePathHandleBuffer);       
  return HandleCount;
}

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  GetUSB( );
  return EFI_SUCCESS;
}

最终运行结果如下:

image003

完整的代码和程序下载:
GetPIDVID

参考:
1. http://biosren.com/viewthread.php?tid=5925&highlight=usbio 請問如何透過DevicePath獲取對應的device全名?
2. http://wiki.phoenix.com/wiki/index.php/EFI_USB_IO_PROTOCOL EFI USB IO PROTOCOL 同样的,在 UEFI spec中也可以找到上面的定义

《Step to UEFI (71) ----- 获得 USB 设备的 PID 和 VID》有20个想法

  1. 請問前輩,有試過使用EDK2的USB IO PROTOCOL,存取USB HID(MCU)裝置嗎?
    譬如用usb interrupt transfer,寫值到MCU,MCU再parse資料後做相對應動作。

    1. 呃 之前想搞来着,后来发现手上没有合适的开发板。用那个 51的速度太慢,对现在的 chipset 兼容性不好,后来就没有继续。

  2. 看前輩有用Arduino,不知道它有沒有USB接口(Arduino),或許可以試試看...
    我手邊有Microchip的實驗板,參考前輩的sample code可以讀取USB裝置資訊,
    但怎麼用interrupt transfer就不太了解了,,
    手邊的AMI BIOS code也找不到相關範例可以參考...

    1. 哦 。回头我看一下,主要是EFI Driver 设计我还不懂。前一段看了一个 FT232 UEFI Driver 的项目,还没有实验。

  3. 你好,我想请教一个问题,我在使用HandleProtocol或LocateProtocol的时候,总是得到的EFI_STATUS总是错的:0x8000000e。小弟刚入门,还请大哥帮我出坑。感激涕零!

      1. 谢谢您的回复。我有试过,打印的结果为Not Found , 原因应该是和楼下darren所遇到的问题一致。另外,我在尝试 EFI_PCI_IO_PROTOCOL 和 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 这两个pci protocol的时候也是如此情况,Not Found。但是有些Protocol是可以用的,如:BLOCKIO 和 SIMPLE FILE SYSTEM 等。综上我有一个理解上的问题:
        在NT32环境中有些程序无出现Not Found,是NT32环境中没有对应Protocol,还是没有设备挂载这个Protocol?
        请您帮我释疑,万分感激。

        1. NT32 本质是模拟出来的虚拟机,很多硬件没有做到,就是说没有设备挂这个这个 Protocol 然后也就没有对应的 Protocol 了。

  4. 请问是不是在,EDKII 模拟环境下,这个USB demo程序也是没办法运行的 ? 是不是也是在拟环境没有设备挂USBIO Protocol 这个原因造成的?

  5. 你好, 我在模拟的板子, 最后会boot到shell envorinment
    Edk2\ShellPkg\Application\Shell\Shell.c
    目前问题是在Shell环境键盘没有回应
    我参考你的方式在Shell.c UefiMain 尝试去LocateHandleBuffer
    结果发现Get USBIO count fail
    追到最后发现
    CoreLocateHandleBuffer
    CoreLocateHandle

    //
    // Look up the protocol entry and set the head pointer
    //
    Position.ProtEntry = CoreFindProtocolEntry (Protocol, FALSE);
    if (Position.ProtEntry == NULL) {
    Status = EFI_NOT_FOUND;
    break;
    }
    这段回传 EFI_NOT_FOUND;
    看起来是mProtocolDatabase 里面找不到gEfiUsbIoProtocolGuid
    不太理解为什么会找不到, gEfiUsbIoProtocolGuid前面应该很多地方有去Locate
    不知道大大有没有什么想法or方向

      1. 是的, 用的是Simics模擬器, 但不確定是模擬器本身沒有Usb controller 還是Bios Code 問題, 目前就開到shell, keyboard無法輸入, 所以用cmd drivers去檢查
        目前想法是Shell.c 去list 所有的handle 以及 protocol
        還是有其他的方式去驗證呢?

          1. 目前手邊沒有PS2鍵盤, 我使用
            Status = gBS->LocateHandleBuffer (
            AllHandles,
            NULL,
            NULL,
            &HandleCount,
            &HandleBuffer);

            ProtocolsPerHandle(HandleBuffer, &ProtocolBuffer, &ProtocolCount);

            列出所有handle下所掛的protocol GUID 以及Name
            把這功能寫成Application 在Notebook 上跑可以找到UsbIo 和Controller Protocol, 寫在模擬器上Shell.c嘗試log 則都沒有Usb相關的Protocol
            代表模擬器沒有USB Controller? 還是可能BIOS某些地方沒設定好呢?

            謝謝

          2. 我找人问了一下,说是 Simics 可以模拟 USB Controller ,但是怎么开需要看具体环境。建议你再找人问问。

  6. 謝謝! 我後來去掃PCI Device, 的確有USB Controller 沒錯, 目前懷疑是某個usb controller protocol 沒有正確初始化,持續Debug..

回复 ziv2013 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注