3. USB 描述符体系:设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符

USB 描述符(Descriptor)是 USB 设备向主机报告自身能力与属性的结构化数据。主机通过标准设备请求(如 GET_DESCRIPTOR)逐层获取这些描述符,从而构建完整的设备拓扑与功能视图。描述符体系采用严格的层级结构,每一层都包含指向下一层的长度与偏移信息。

核心要点:描述符是 USB 枚举过程的“身份证”,驱动开发者必须理解其层级关系与字段含义,才能正确解析设备能力、配置端点并提交 URB。

3.1 描述符层级结构总览

USB 描述符遵循“设备 → 配置 → 接口 → 端点”的树形层级。每个 USB 设备只有一个设备描述符,但可以有多个配置描述符;每个配置下可包含多个接口描述符;每个接口下可包含多个端点描述符。字符串描述符为可选,用于提供可读的文本信息。

USB 描述符层级结构 USB 描述符层级结构 设备描述符 配置描述符 接口描述符 接口描述符 端点描述符 端点描述符 端点描述符 端点描述符 每个描述符的首字节为 bLength,第二字节为 bDescriptorType,用于主机解析时校验与跳转

3.2 设备描述符(Device Descriptor)

设备描述符是 USB 设备的第一级描述符,位于描述符树的根节点。主机通过 GET_DESCRIPTOR(DEVICE) 请求获取,长度为 18 字节。它定义了设备的整体属性,包括 USB 规范版本、设备类、厂商与产品 ID 等。

偏移 字段 大小 说明
0 bLength 1 描述符长度,固定为 18
1 bDescriptorType 1 描述符类型,设备描述符为 0x01
2-3 bcdUSB 2 USB 规范版本号(BCD 码),如 0x0200 表示 USB 2.0
4 bDeviceClass 1 设备类代码(0x00 表示在接口描述符中定义)
5 bDeviceSubClass 1 设备子类代码
6 bDeviceProtocol 1 设备协议代码
7 bMaxPacketSize0 1 端点 0 的最大包大小(8/16/32/64)
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_get_descriptor() 或直接从 struct usb_device->descriptor 读取。

3.3 配置描述符(Configuration Descriptor)

配置描述符描述设备的供电模式、最大电流以及接口数量。一个设备可以有多个配置(如低功耗模式与高功耗模式),但同一时刻只能激活一个配置。配置描述符长度为 9 字节,其后跟随接口描述符与端点描述符。

偏移 字段 大小 说明
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 配置特性(自供电/远程唤醒等)
8 bMaxPower 1 最大电流(单位 2mA),如 50 表示 100mA
常见误区:wTotalLength 包含配置描述符本身以及其下所有接口描述符、端点描述符和其他可选描述符的总字节数。驱动在解析时必须使用该字段来界定描述符边界,不能假设固定长度。

3.4 接口描述符(Interface Descriptor)

接口描述符描述一个 USB 功能单元(如键盘、音频、存储)。一个配置下可以有多个接口,接口可以分组为接口关联(IAD)。接口描述符长度为 9 字节,定义了接口的类、子类、协议以及使用的端点数量。

偏移 字段 大小 说明
0 bLength 1 描述符长度,固定为 9
1 bDescriptorType 1 描述符类型,接口描述符为 0x04
2 bInterfaceNumber 1 接口编号(从 0 开始)
3 bAlternateSetting 1 备用设置值,用于切换接口配置
4 bNumEndpoints 1 该接口使用的端点数量(不包括端点 0)
5 bInterfaceClass 1 接口类代码(如 HID 为 0x03)
6 bInterfaceSubClass 1 接口子类代码
7 bInterfaceProtocol 1 接口协议代码
8 iInterface 1 接口字符串描述符索引
备用设置(Alternate Setting):接口可以通过 bAlternateSetting 提供多种配置变体。驱动使用 usb_set_interface() 切换备用设置,从而改变端点数量或类型。例如,UVC 视频设备在流接口中使用不同备用设置来切换带宽。

3.5 端点描述符(Endpoint Descriptor)

端点描述符是描述符体系中最底层的描述符,直接定义了 URB 传输的目标地址、传输类型、最大包大小和轮询间隔。每个非端点 0 的端点都对应一个端点描述符,长度为 7 字节(同步传输为 9 字节)。

偏移 字段 大小 说明
0 bLength 1 描述符长度,固定为 7(同步为 9)
1 bDescriptorType 1 描述符类型,端点描述符为 0x05
2 bEndpointAddress 1 端点地址:bit7 方向(0=OUT,1=IN),bit3-0 端点号
3 bmAttributes 1 传输类型:bit1-0(0=控制,1=等时,2=批量,3=中断)
4-5 wMaxPacketSize 2 最大包大小(bit10-0 为大小,bit12-11 为附加事务数)
6 bInterval 1 轮询间隔(中断/等时端点,单位:帧/微帧)
端点描述符字段与 URB 配置映射 端点描述符字段 → URB 配置映射 端点描述符 bEndpointAddress → 0x81 (IN EP1) bmAttributes → 0x02 (BULK) wMaxPacketSize → 512 bInterval → 0 URB 配置 urb->pipe = usb_rcvbulkpipe(dev, 1) urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP urb->transfer_buffer_length = 512 urb->interval = 0 驱动通过 usb_rcvbulkpipe/dev/epnum 等宏将端点描述符字段转换为 URB 管道标识

3.6 字符串描述符(String Descriptor)

字符串描述符为可选描述符,用于提供厂商名称、产品名称、序列号等可读文本。字符串使用 Unicode(UTF-16LE)编码。字符串描述符索引 0 是一个特殊的语言 ID 描述符,列出设备支持的语言代码。

偏移 字段 大小 说明
0 bLength 1 描述符长度(可变,取决于字符串长度)
1 bDescriptorType 1 描述符类型,字符串描述符为 0x03
2..N bString 可变 Unicode 字符串(UTF-16LE 编码)
内核 API:使用 usb_string() 函数从设备读取字符串描述符,该函数自动处理语言 ID 协商和 UTF-16LE 到 UTF-8 的转换。驱动中常用 dev_info() 打印设备名称,如:dev_info(&udev->dev, "Product: %s\n", udev->product);

3.7 描述符解析实战流程

在 Linux 内核中,USB 核心子系统在枚举阶段自动解析所有描述符,并将结果填充到 struct usb_device 和 struct usb_interface 中。驱动开发者通常不需要手动解析原始描述符,但理解其结构对调试和高级配置至关重要。

  1. 获取设备描述符:主机发送 GET_DESCRIPTOR(DEVICE),获取 18 字节设备描述符,校验 bcdUSB 和 bMaxPacketSize0。
  2. 获取配置描述符:主机发送 GET_DESCRIPTOR(CONFIGURATION),先读 9 字节获取 wTotalLength,再读取完整配置描述符集合。
  3. 解析接口与端点:遍历配置描述符数据,根据 bDescriptorType 区分接口描述符(0x04)和端点描述符(0x05),构建接口与端点列表。
  4. 选择配置:主机发送 SET_CONFIGURATION 激活选中的配置,设备进入可用状态。
  5. 驱动绑定:USB 核心根据接口描述符的 bInterfaceClass/bInterfaceSubClass/bInterfaceProtocol 匹配驱动,调用驱动的 probe() 函数。
实战代码片段:在驱动 probe 中获取端点描述符信息
static int my_driver_probe(struct usb_interface *intf,
                           const struct usb_device_id *id)
{
    struct usb_host_interface *iface_desc;
    struct usb_endpoint_descriptor *ep_desc;
    int i;

    iface_desc = intf->cur_altsetting;
    dev_info(&intf->dev, "Interface %d has %d endpoints\n",
             iface_desc->desc.bInterfaceNumber,
             iface_desc->desc.bNumEndpoints);

    for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
        ep_desc = &iface_desc->endpoint[i].desc;
        dev_info(&intf->dev,
                 "EP 0x%02x: type=%d, max_pkt=%d, interval=%d\n",
                 ep_desc->bEndpointAddress,
                 ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK,
                 le16_to_cpu(ep_desc->wMaxPacketSize),
                 ep_desc->bInterval);
    }
    return 0;
}

3.8 描述符类型标识速查表

bDescriptorType 值 描述符类型 固定长度 内核结构体
0x01 设备描述符 18 struct usb_device_descriptor
0x02 配置描述符 9 struct usb_config_descriptor
0x03 字符串描述符 可变 struct usb_string_descriptor
0x04 接口描述符 9 struct usb_interface_descriptor
0x05 端点描述符 7(同步为 9) struct usb_endpoint_descriptor
0x0F 接口关联描述符(IAD) 8 struct usb_interface_assoc_descriptor
调试建议:使用 lsusb -v 命令可以完整打印 USB 设备的描述符树。在驱动开发中,如果遇到 URB 提交失败或端点配置错误,首先使用 lsusb -v 确认端点描述符的 bEndpointAddress、wMaxPacketSize 和 bmAttributes 是否与驱动代码一致。