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/
来源:爱影博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
海报
UEFI开发学习6 – 枚举USB设备
USB设备的枚举还是挺简单的,几行的代码而已。我们知道,UEFI Core会为系统上的每个设备分别生成一个Handle,并在该Handle上安装其相应的Protocols。拿USB 设……
<<上一篇
下一篇>>