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 条评论