4. USB 描述符体系

USB 描述符(Descriptor)是 USB 设备向主机报告自身属性、能力与配置信息的数据结构。主机通过标准设备请求(如 GET_DESCRIPTOR)逐层获取这些描述符,从而完成设备枚举与驱动匹配。描述符体系采用严格的层级结构,从设备级到功能级逐级细化。

核心原则: 描述符是 USB 协议中“自描述”机制的基石。每个描述符以 bLength 和 bDescriptorType 开头,主机据此解析后续字段。任何描述符内容的错误都可能导致枚举失败或驱动加载异常。

4.1 描述符层级结构总览

USB 描述符体系呈树形结构:一个设备只有一个设备描述符,设备描述符中指定了配置描述符的数量;每个配置描述符下包含一个或多个接口描述符;每个接口描述符下包含零个或多个端点描述符。此外,字符串描述符为其他描述符中的索引字段提供可读文本。

USB 描述符层级结构 设备描述符 (1个/设备) 配置描述符 (1个或多个/设备) 接口描述符 (1个或多个/配置) 接口描述符 (备用接口0) 端点描述符 (端点0除外) 端点描述符 (端点0除外) 字符串描述符 (可选,通过索引引用) 说明 字符串描述符通过索引被其他描述符引用 并非所有描述符都必须包含字符串索引

4.2 设备描述符 (Device Descriptor)

设备描述符是 USB 设备的第一级描述符,位于描述符树的根节点。主机通过 GET_DESCRIPTOR 请求获取设备描述符时,wValue 高字节为 0x01,低字节为 0x00。

偏移 字段 大小 说明
0 bLength 1 描述符长度,固定为 18 字节
1 bDescriptorType 1 描述符类型,设备描述符为 0x01
2-3 bcdUSB 2 USB 规范版本号(BCD 码),如 0x0200 表示 USB 2.0
4 bDeviceClass 1 设备类代码(由 USB-IF 分配),0x00 表示在接口描述符中定义
5 bDeviceSubClass 1 设备子类代码
6 bDeviceProtocol 1 设备协议代码
7 bMaxPacketSize0 1 端点 0 的最大包大小(字节)
8-9 idVendor 2 厂商 ID(由 USB-IF 分配)
10-11 idProduct 2 产品 ID(由厂商分配)
12-13 bcdDevice 2 设备版本号(BCD 码)
14 iManufacturer 1 厂商字符串描述符索引
15 iProduct 1 产品字符串描述符索引
16 iSerialNumber 1 序列号字符串描述符索引
17 bNumConfigurations 1 支持的配置描述符数量
实战要点: 在 Linux 内核中,设备描述符通过 struct usb_device_descriptor 结构体表示,定义在 <linux/usb/ch9.h> 中。驱动开发中常通过 usb_device->descriptor 直接访问。

4.3 配置描述符 (Configuration Descriptor)

配置描述符描述设备的电源管理属性和接口数量。一个设备可以有多个配置(如低功耗模式和高性能模式),但同一时刻只能有一个配置被激活。

偏移 字段 大小 说明
0 bLength 1 描述符长度,固定为 9 字节
1 bDescriptorType 1 配置描述符类型,0x02
2-3 wTotalLength 2 整个配置信息的总长度(包括配置描述符、接口描述符、端点描述符等)
4 bNumInterfaces 1 该配置下支持的接口数量
5 bConfigurationValue 1 用于 SET_CONFIGURATION 请求的配置值
6 iConfiguration 1 配置字符串描述符索引
7 bmAttributes 1 配置特性:D7=1(必须),D6=1(自供电),D5=1(支持远程唤醒)
8 bMaxPower 1 最大电流需求,以 2mA 为单位(USB 2.0 最大 500mA)
常见错误: wTotalLength 字段必须包含该配置下所有描述符的总长度。如果该值计算错误,主机在解析后续描述符时可能越界或解析失败,导致枚举中断。

4.4 接口描述符 (Interface Descriptor)

接口描述符描述一个功能单元(如键盘、音频流、大容量存储)。一个配置可以包含多个接口,接口可以设置备用设置(Alternate Setting),用于在运行时切换接口参数(如改变带宽)。

偏移 字段 大小 说明
0 bLength 1 描述符长度,固定为 9 字节
1 bDescriptorType 1 接口描述符类型,0x04
2 bInterfaceNumber 1 接口编号(从 0 开始)
3 bAlternateSetting 1 备用设置编号
4 bNumEndpoints 1 该接口使用的端点数量(不包括端点 0)
5 bInterfaceClass 1 接口类代码(如 HID=0x03, Mass Storage=0x08)
6 bInterfaceSubClass 1 接口子类代码
7 bInterfaceProtocol 1 接口协议代码
8 iInterface 1 接口字符串描述符索引
驱动匹配关键: Linux 内核的 usb_device_id 结构体支持按 bInterfaceClass、bInterfaceSubClass 和 bInterfaceProtocol 进行匹配。这是大多数类驱动(如 HID、UVC、Audio)的绑定依据。

4.5 端点描述符 (Endpoint Descriptor)

端点描述符描述除端点 0 以外的每个端点的属性,包括传输方向、传输类型和最大包大小。端点 0 是默认控制端点,其属性由设备描述符中的 bMaxPacketSize0 定义。

偏移 字段 大小 说明
0 bLength 1 描述符长度,固定为 7 字节
1 bDescriptorType 1 端点描述符类型,0x05
2 bEndpointAddress 1 端点地址:Bit 7 方向(0=OUT, 1=IN),Bit 3-0 端点号
3 bmAttributes 1 传输类型:Bit 1-0(0=控制, 1=等时, 2=批量, 3=中断)
4-5 wMaxPacketSize 2 最大包大小(字节),USB 2.0 高速批量端点最大 512 字节
6 bInterval 1 轮询间隔(中断/等时端点):USB 2.0 单位为 125µs(微帧)或 1ms
端点描述符关键字段解析 bEndpointAddress: D7 D6 D5 D4 D3 D2 D1 D0 D7: 方向 0 = OUT (主机→设备) 1 = IN (设备→主机) D3-D0: 端点号 范围 0-15,端点 0 为控制端点 bmAttributes (D1-D0): D1-D0 00 = 控制传输 01 = 等时传输 10 = 批量传输 11 = 中断传输

4.6 字符串描述符 (String Descriptor)

字符串描述符提供人类可读的 Unicode 文本,如厂商名称、产品名称和序列号。字符串描述符通过索引号被其他描述符引用。索引 0 是特殊的字符串描述符,它返回设备支持的语言 ID 列表(LANGID)。

偏移 字段 大小 说明
0 bLength 1 描述符长度(可变,取决于字符串长度)
1 bDescriptorType 1 字符串描述符类型,0x03
2 bString / wLANGID[0..n] 可变 索引 0 时为 LANGID 数组;其他索引时为 Unicode 字符串
实现建议: 字符串描述符使用 UTF-16LE 编码。在 Linux 内核中,字符串描述符的读取通过 usb_string() 函数完成,驱动开发者通常不需要手动解析原始描述符数据。

4.7 其他描述符

4.7.1 BOS 描述符 (Binary Object Store)

BOS 描述符是 USB 3.0 引入的扩展描述符,用于描述设备对 USB 3.0 及更高版本特性的支持情况。它位于设备描述符之后,通过 GET_DESCRIPTOR 请求(类型 0x0F)获取。

偏移 字段 大小 说明
0 bLength 1 描述符长度,固定为 5 字节
1 bDescriptorType 1 BOS 描述符类型,0x0F
2-3 wTotalLength 2 整个 BOS 描述符集的总长度
4 bNumDeviceCaps 1 设备能力描述符的数量

BOS 描述符后跟随一个或多个设备能力描述符 (Device Capability Descriptor),用于声明 USB 3.0 的特定能力,如:

  • USB 2.0 扩展能力:支持 LPM(链路电源管理)
  • SuperSpeed 能力:支持 USB 3.0 高速操作
  • 容器 ID 能力:提供唯一的设备标识符
  • 平台能力:用于微软等厂商的扩展属性(如 WinUSB)

4.7.2 SuperSpeed 端点伴侣描述符 (SS Endpoint Companion Descriptor)

SuperSpeed 端点伴侣描述符是 USB 3.0 为 SuperSpeed 端点增加的额外描述符,紧跟在对应的端点描述符之后。它提供 SuperSpeed 模式下的额外端点属性。

偏移 字段 大小 说明
0 bLength 1 描述符长度,固定为 6 字节
1 bDescriptorType 1 SS 端点伴侣描述符类型,0x30
2 bMaxBurst 1 最大突发传输包数(0-15)
3 bmAttributes 1 属性:Bit 4-0 为流能力(批量端点),Bit 7-5 保留
4-5 wBytesPerInterval 2 每个轮询间隔传输的字节数(等时/中断端点)
SuperSpeed 关键差异: 在 USB 3.0 中,批量端点支持突发传输(Burst),bMaxBurst 字段定义了每个微帧可以连续传输的最大包数。例如,bMaxBurst = 0 表示 1 包,bMaxBurst = 15 表示最多 16 包。这显著提高了批量传输的吞吐量。

4.8 描述符获取流程与实战代码

主机在枚举过程中按以下顺序获取描述符:

  1. 获取设备描述符(前 8 字节,用于确定 bMaxPacketSize0)
  2. 复位设备,使用新的 bMaxPacketSize0 重新获取完整设备描述符
  3. 获取配置描述符(前 9 字节,用于确定 wTotalLength)
  4. 获取完整配置描述符集(包括接口和端点描述符)
  5. 如果存在字符串索引,获取字符串描述符
  6. 对于 USB 3.0 设备,获取 BOS 描述符

以下是一个简化的 Linux 内核代码片段,展示如何从 usb_device 结构中遍历描述符:

#include <linux/usb.h>

static void print_device_descriptors(struct usb_device *udev)
{
    struct usb_device_descriptor *desc = &udev->descriptor;
    struct usb_host_config *config;
    struct usb_interface *iface;
    struct usb_host_interface *altsetting;
    struct usb_endpoint_descriptor *ep;
    int i, j, k;

    dev_info(&udev->dev, "Device: VID=0x%04x PID=0x%04x\n",
             le16_to_cpu(desc->idVendor),
             le16_to_cpu(desc->idProduct));

    for (i = 0; i < udev->descriptor.bNumConfigurations; i++) {
        config = udev->config + i;
        dev_info(&udev->dev, "  Config %d: %d interfaces\n",
                 i, config->desc.bNumInterfaces);

        for (j = 0; j < config->desc.bNumInterfaces; j++) {
            iface = config->interface[j];
            for (k = 0; k < iface->num_altsetting; k++) {
                altsetting = iface->altsetting + k;
                dev_info(&udev->dev,
                    "    Interface %d.%d: Class=0x%02x\n",
                    altsetting->desc.bInterfaceNumber,
                    altsetting->desc.bAlternateSetting,
                    altsetting->desc.bInterfaceClass);

                for (ep = altsetting->endpoint;
                     ep != altsetting->endpoint +
                          altsetting->desc.bNumEndpoints;
                     ep++) {
                    dev_info(&udev->dev,
                        "      Endpoint 0x%02x: MaxPkt=%d\n",
                        ep->desc.bEndpointAddress,
                        le16_to_cpu(ep->desc.wMaxPacketSize));
                }
            }
        }
    }
}
调试提示: 在 Linux 下,可以使用 lsusb -v 命令查看设备的完整描述符树。如果设备枚举失败,首先检查 dmesg 输出中的 USB 错误信息,通常描述符长度或校验错误会直接打印出来。

4.9 描述符体系总结

下表总结了各类描述符的核心属性与用途:

<