继续前面的话题,这次研究如何取得一个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()

感谢博主的分享 ,一直在做UEFI下的USB的驱动,这个例子太好了,protocol的调用还是不熟悉,之前折腾了几天 都没有load到gEfiUsbIoProtocol