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》上有 10 条评论

  1. eos33

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

    回复
    1. ziv2013 文章作者

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

      回复
  2. eos33

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

    回复
    1. ziv2013 文章作者

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

      回复
  3. kali

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

    回复
      1. kali

        谢谢您的回复。我有试过,打印的结果为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. ziv2013 文章作者

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

          回复
  4. darren

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

    回复

发表评论

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