继续前面的话题,这次研究如何取得一个USB DISK的信息。最先想到的还是使用 DISK INFO PROTOCOL,不过到了实际编写代码的时候发现:我根本不知道返回值是什么格式。在代码中搜索了几次 gEfiDiskInfoUsbInterfaceGuid 发现无人使用。之后为了弄清楚格式,直接创建了一个256bytes大小的内存,还是用 identify 来获得返回值,最后发现:返回值是空的。因此,也就是说虽然 USB DISK上有安装这个 protocol,但是根本就是一个空的而已。
再调整思路,首先用 DISK INFO PROTOCOL取得USB DISK 的 Handle ,然后,在这个Handle上打开 USBIO,EFI_USB_IO_PROTOCOL.UsbGetStringDescriptor 可以用来取得描述符中相关的字符串【参考1】,具体结构可以在 \EdkCompatibilityPkg\Foundation\Include\IndustryStandard\usb.h 找到:
typedef struct { UINT8 Length; UINT8 DescriptorType; UINT16 BcdUSB; UINT8 DeviceClass; UINT8 DeviceSubClass; UINT8 DeviceProtocol; UINT8 MaxPacketSize0; UINT16 IdVendor; UINT16 IdProduct; UINT16 BcdDevice; UINT8 StrManufacturer; UINT8 StrProduct; UINT8 StrSerialNumber; UINT8 NumConfigurations; } EFI_USB_DEVICE_DESCRIPTOR;
最后,用这个 函数来取得需要的字符串。
完整的代码:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <Library/ShellCEntryLib.h> #include <Protocol/DiskInfo.h> #include <Library/BaseMemoryLib.h> #include <Protocol/IdeControllerInit.h> #include <Library/MemoryAllocationLib.h> #include <Protocol/UsbIo.h> extern EFI_BOOT_SERVICES *gBS; extern EFI_HANDLE gImageHandle; EFI_GUID gEfiDiskInfoProtocolGuid = { 0xD432A67F, 0x14DC, 0x484B, { 0xB3, 0xBB, 0x3F, 0x02, 0x91, 0x84, 0x93, 0x27 }}; EFI_GUID gEfiDiskInfoUsbInterfaceGuid = { 0xCB871572, 0xC11A, 0x47B5, { 0xB4, 0x92, 0x67, 0x5E, 0xAF, 0xA7, 0x77, 0x27 }}; EFI_GUID gEfiUsbIoProtocolGuid = { 0x2B2F68D6, 0x0CD2, 0x44CF, { 0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75 }}; int EFIAPI main ( IN int Argc, IN CHAR16 **Argv ) { EFI_STATUS Status; UINTN HandleIndex, NumHandles; EFI_HANDLE *ControllerHandle = NULL; EFI_DISK_INFO_PROTOCOL *DiskInfoProtocol; EFI_USB_IO_PROTOCOL *USBIO; EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor; CHAR16 *Manufacturer; CHAR16 *Product; CHAR16 *SerialNumber; Status = gBS->LocateHandleBuffer( ByProtocol, &gEfiDiskInfoProtocolGuid, NULL, &NumHandles, &ControllerHandle); for (HandleIndex = 0; HandleIndex < NumHandles; HandleIndex++) { Status = gBS->OpenProtocol( ControllerHandle[HandleIndex], &gEfiDiskInfoProtocolGuid, (VOID**)&DiskInfoProtocol, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR(Status)) { continue; } //We only deal with USB if (!(CompareGuid ( &DiskInfoProtocol->Interface, &gEfiDiskInfoUsbInterfaceGuid))) { continue; } Status = gBS->OpenProtocol( ControllerHandle[HandleIndex], &gEfiUsbIoProtocolGuid, (VOID**)&USBIO, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR(Status)) { Print(L"ERROR : Open USBIO fail.\n"); continue; } Status = USBIO->UsbGetDeviceDescriptor (USBIO, &DeviceDescriptor); if (EFI_ERROR(Status)) { Print(L"ERROR : Get Device Descriptor fail.\n"); return 0; } Print(L"VendorID = %04X\nProductID = %04X\n", DeviceDescriptor.IdVendor, DeviceDescriptor.IdProduct); Status = USBIO->UsbGetStringDescriptor ( USBIO, 0x0409, // English DeviceDescriptor.StrManufacturer, &Manufacturer ); if (EFI_ERROR (Status)) { Manufacturer = L""; } Status = USBIO->UsbGetStringDescriptor ( USBIO, 0x0409, // English DeviceDescriptor.StrProduct, &Product ); if (EFI_ERROR (Status)) { Product = L""; } Status = USBIO->UsbGetStringDescriptor ( USBIO, 0x0409, // English DeviceDescriptor.StrSerialNumber, &SerialNumber ); if (EFI_ERROR (Status)) { SerialNumber = L"";} Print(L" Manufacturer : %s\n",Manufacturer); Print(L" Product : %s\n",Product); Print(L" Serial Number: %s\n",SerialNumber); } return EFI_SUCCESS; }
完整的代码和程序下载:
diskinfousb
另外,还可以直接枚举 USBIO 然后检查对应的 Class发现是USB MASS Storage 即是U盘,然后再重复上面取得字符串信息的动作。
参考:
1. UEFI Spec 2.4 P835 EFI_USB_IO_PROTOCOL.UsbGetStringDescriptor()