UEFI开发学习6 – 枚举USB设备
USB设备的枚举还是挺简单的,几行的代码而已。我们知道,UEFI Core会为系统上的每个设备分别生成一个Handle,并在该Handle上安装其相应的Protocols。拿USB 设备来说,它既然是一个设备,那它必安装有EFI_DEVICE_PATH_PROTOCOL协议;它是一个USB设备,那也将支持EFI_USB_IO_PROTOCOL协议;假如这是一个U盘,那它还支持EFI_BLOCK_IO_PROTOCOL协议。等等。
知道了这些,枚举USB设备就很简单了,下面的实现都将以EFI APP的形式做验证,代码如下:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Protocol/BlockIo.h> #include <Library/UefiBootServicesTableLib.h> #include <Protocol/DevicePathToText.h> #include <Protocol/UsbIo.h> int main(int argc, char **argv) { EFI_STATUS Status; EFI_HANDLE *UsbDeviceHandle; UINTN UsbDeviceHandleNumber; Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiUsbIoProtocolGuid, NULL, &UsbDeviceHandleNumber, &UsbDeviceHandle ); if (!EFI_ERROR (Status)) { Print (L"Usb device number: %d\n\r", UsbDeviceHandleNumber); } return EFI_SUCCESS; }
扩展
上面的例子只是简单的得到USB设备的总数,如果要列出每个USB设备的信息,如USB设备的设备路径,厂商,序列号等,该怎么做呢?
1.列出设备路径
思路: 使用 gBS->HandleProtocol()函数找出每个Handle下的EFI_DEVICE_PATH_PROTOCOL实例,然后再用EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.ConvertDevicePathToText()将其转换为字符串即可.
#include <Uefi.h> #include <Library/UefiLib.h> #include <Protocol/BlockIo.h> #include <Library/UefiBootServicesTableLib.h> #include <Protocol/DevicePathToText.h> #include <Protocol/UsbIo.h> int main(int argc, char **argv) { EFI_STATUS Status; EFI_HANDLE *UsbDeviceHandle; UINTN UsbDeviceHandleNumber; EFI_DEVICE_PATH_PROTOCOL *UsbDevicePath; EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *Path2Text; UINTN Index; /* Get all usb device handles */ Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiUsbIoProtocolGuid, NULL, &UsbDeviceHandleNumber, &UsbDeviceHandle ); if (!EFI_ERROR (Status)) { Print (L"Usb device number: %d\n\r\n\r", UsbDeviceHandleNumber); } /* Get device to text instance to convert device path to string */ Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **) &Path2Text); if (EFI_ERROR(Status)) { Print (L"Unsupported\n\r"); return 0; } /* List all usb info */ for (Index = 0; Index < UsbDeviceHandleNumber; Index ++) { /* List device path */ Status = gBS->HandleProtocol (UsbDeviceHandle[Index], &gEfiDevicePathProtocolGuid, (VOID **) &UsbDevicePath); if (!EFI_ERROR (Status)) { Print (L"Device Path: %s\n\r", Path2Text->ConvertDevicePathToText(UsbDevicePath, FALSE, TRUE)); } Print (L"\n\r"); } return EFI_SUCCESS; }
2.列出产商及序列号
思路:在USB手册《Universal Serial Bus Specification Revision 2.0》9.6章节有相关的定义,如设备,配置,接口,终结点等信息。下表为USB设备信息的具体定义:
由上表可看到,Offset14,15,16就是我们所需的信息了。但是注意到,这里定义的只是该信息的一个索引值,而并非字符串。再看到9.6.7 String章节,其描述了USB设备信息的字符串支持多种语言,并使用Unicode编码存放,需要提供LANGID(LANGID文档下载)才可获取对应的字符串。
那最后要如何读取这些信息呢?UEFI已经给我们提供了相应的Protocol,那便是EFI_USB_IO_PROTOCOL。首先使用EFI_USB_IO_PROTOCOL.UsbGetDeviceDescriptor()获取产商和序列号的索引值,再用EFI_USB_IO_PROTOCOL.UsbGetStringDescriptor()取得相应的字符串即可。代码如下:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Protocol/BlockIo.h> #include <Library/UefiBootServicesTableLib.h> #include <Protocol/DevicePathToText.h> #include <Protocol/UsbIo.h> #define LANGID_ENGLISH 0x0409 int main(int argc, char **argv) { EFI_STATUS Status; EFI_HANDLE *UsbDeviceHandle; UINTN UsbDeviceHandleNumber; EFI_DEVICE_PATH_PROTOCOL *UsbDevicePath; EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *Path2Text; UINTN Index; EFI_USB_IO_PROTOCOL *UsbDevice; EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor; CHAR16 *StrManufacturer; CHAR16 *StrProduct; CHAR16 *StrSerialNumber; /* Get all usb device handles */ Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiUsbIoProtocolGuid, NULL, &UsbDeviceHandleNumber, &UsbDeviceHandle ); if (EFI_ERROR (Status)) { Print (L"Unsupported\n\r"); return 0; } /* Get device to text instance to convert device path to string */ Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **) &Path2Text); if (EFI_ERROR(Status)) { Print (L"Unsupported\n\r"); return 0; } /* List all usb info */ for (Index = 0; Index < UsbDeviceHandleNumber; Index ++) { Print (L"Usb device number: %d\n\r", Index + 1); /* Get Usb Io protocol instance */ Status = gBS->HandleProtocol (UsbDeviceHandle[Index], &gEfiUsbIoProtocolGuid, (VOID **) &UsbDevice); Status = UsbDevice->UsbGetDeviceDescriptor (UsbDevice, &DeviceDescriptor); if (!EFI_ERROR (Status)) { /* Manufacturer */ StrManufacturer = NULL; Status = UsbDevice->UsbGetStringDescriptor (UsbDevice, LANGID_ENGLISH, DeviceDescriptor.StrManufacturer, &StrManufacturer); if (!EFI_ERROR (Status)) { Print (L"Manufacturer: %s\n\r", StrManufacturer); } /* Product */ StrProduct = NULL; Status = UsbDevice->UsbGetStringDescriptor (UsbDevice, LANGID_ENGLISH, DeviceDescriptor.StrProduct, &StrProduct); if (!EFI_ERROR (Status)) { Print (L"Product: %s\n\r", StrProduct); } /* Serial number */ StrSerialNumber = NULL; Status = UsbDevice->UsbGetStringDescriptor (UsbDevice, LANGID_ENGLISH, DeviceDescriptor.StrSerialNumber, &StrSerialNumber); if (!EFI_ERROR (Status)) { Print (L"Serial number: %s\n\r", StrSerialNumber); } } /* List device path */ Status = gBS->HandleProtocol (UsbDeviceHandle[Index], &gEfiDevicePathProtocolGuid, (VOID **) &UsbDevicePath); if (!EFI_ERROR (Status)) { Print (L"Device Path: %s\n\r", Path2Text->ConvertDevicePathToText(UsbDevicePath, FALSE, TRUE)); } Print (L"\n\r"); } return EFI_SUCCESS; }
执行结果如下:
最后还有一个问题要解决的,那就是怎么去判断usb设备的类型,如怎么知道某个设备是存储设备,或是键盘鼠标等。此类信息位于USB的Interface Descriptor中,定义如下:
用于判断设备类型的只需InterfaceClass,InterfaceSubClass和InterfaceProtocol就可以了。
InterfaceClass定义(Defined Class Codes)如下:
这是一个大类型的定义,如通过这个值可以判断是存储设备还是HID等,若还要细分则要结合InterfaceSubClass和InterfaceProtocol,详细的定义可在https://www.usb.org/documents找到,搜索class可找到相应class的SPEC。本文只是做一个简单的判断,最终实现的代码如下:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Protocol/BlockIo.h> #include <Library/UefiBootServicesTableLib.h> #include <Protocol/DevicePathToText.h> #include <Protocol/UsbIo.h> #define LANGID_ENGLISH 0x0409 #define USB_INTERFACE_CLASS_UNKNOW 0x00 #define USB_INTERFACE_CLASS_HID 0x03 #define USB_INTERFACE_CLASS_STORAGE 0x08 #define USB_INTERFACE_CLASS_HUB 0x09 #define USB_INTERFACE_CLASS_VENDOR_DEF 0xFF #define USB_INTERFACE_HID_PROTOCOL_KB 0x01 #define USB_INTERFACE_HID_PROTOCOL_MS 0x02 int main(int argc, char **argv) { EFI_STATUS Status; EFI_HANDLE *UsbDeviceHandle; UINTN UsbDeviceHandleNumber; EFI_DEVICE_PATH_PROTOCOL *UsbDevicePath; EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *Path2Text; UINTN Index; EFI_USB_IO_PROTOCOL *UsbDevice; EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor; CHAR16 *StrManufacturer; CHAR16 *StrProduct; CHAR16 *StrSerialNumber; EFI_USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; /* Get all usb device handles */ Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiUsbIoProtocolGuid, NULL, &UsbDeviceHandleNumber, &UsbDeviceHandle ); if (EFI_ERROR (Status)) { Print (L"Unsupported\n\r"); return 0; } /* Get device to text instance to convert device path to string */ Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **) &Path2Text); if (EFI_ERROR(Status)) { Print (L"Unsupported\n\r"); return 0; } /* List all usb info */ for (Index = 0; Index < UsbDeviceHandleNumber; Index ++) { /* Get Usb Io protocol instance */ Status = gBS->HandleProtocol (UsbDeviceHandle[Index], &gEfiUsbIoProtocolGuid, (VOID **) &UsbDevice); /* Get usb device type */ Status = UsbDevice->UsbGetInterfaceDescriptor (UsbDevice, &InterfaceDescriptor); if (!EFI_ERROR (Status)) { Print (L"NO.%d\n\r", Index + 1); switch (InterfaceDescriptor.InterfaceClass) { case USB_INTERFACE_CLASS_UNKNOW: Print (L"Type: Unkown\n\r"); break; case USB_INTERFACE_CLASS_HID: if (InterfaceDescriptor.InterfaceProtocol == USB_INTERFACE_HID_PROTOCOL_KB) { Print (L"Type: Keyboard\n\r"); } else if (InterfaceDescriptor.InterfaceProtocol == USB_INTERFACE_HID_PROTOCOL_MS) { Print (L"Type: Mouse\n\r"); } break; case USB_INTERFACE_CLASS_STORAGE: Print (L"Type: Mass Storage\n\r"); break; case USB_INTERFACE_CLASS_HUB: Print (L"Type: Hub\n\r"); break; default: Print (L"Type: Unkown\n\r"); break; } } /* Not a device */ else continue; Status = UsbDevice->UsbGetDeviceDescriptor (UsbDevice, &DeviceDescriptor); if (!EFI_ERROR (Status)) { /* Manufacturer */ StrManufacturer = NULL; Status = UsbDevice->UsbGetStringDescriptor (UsbDevice, LANGID_ENGLISH, DeviceDescriptor.StrManufacturer, &StrManufacturer); if (!EFI_ERROR (Status)) { Print (L"Manufacturer: %s\n\r", StrManufacturer); } /* Product */ StrProduct = NULL; Status = UsbDevice->UsbGetStringDescriptor (UsbDevice, LANGID_ENGLISH, DeviceDescriptor.StrProduct, &StrProduct); if (!EFI_ERROR (Status)) { Print (L"Product: %s\n\r", StrProduct); } /* Serial number */ StrSerialNumber = NULL; Status = UsbDevice->UsbGetStringDescriptor (UsbDevice, LANGID_ENGLISH, DeviceDescriptor.StrSerialNumber, &StrSerialNumber); if (!EFI_ERROR (Status)) { Print (L"Serial number: %s\n\r", StrSerialNumber); } } /* List device path */ Status = gBS->HandleProtocol (UsbDeviceHandle[Index], &gEfiDevicePathProtocolGuid, (VOID **) &UsbDevicePath); if (!EFI_ERROR (Status)) { Print (L"Device Path: %s\n\r", Path2Text->ConvertDevicePathToText(UsbDevicePath, FALSE, TRUE)); } Print (L"\n\r"); } return EFI_SUCCESS; }
执行结果:
版权声明:
作者:bin
链接:https://ay123.net/mystudy/816/
来源:爱影博客
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论