3. USB 描述符体系:结构与层级关系

USB 描述符是设备向主机报告自身能力与配置的标准化数据结构。主机通过枚举过程逐层获取这些描述符,从而构建出完整的设备逻辑模型。理解描述符的层级关系是诊断枚举故障的基础。

3.1 描述符的层级树

USB 描述符采用严格的树形层级结构:设备描述符位于根节点,其下挂载一个或多个配置描述符;每个配置描述符下包含一个或多个接口描述符;每个接口描述符下包含一个或多个端点描述符。字符串描述符作为可选附属,被其他描述符通过索引引用。

USB 描述符层级树 设备描述符 配置描述符 #1 配置描述符 #2 配置描述符 #n 接口描述符 0 接口描述符 1 接口描述符 0 接口描述符 1 端点 0 (控制) 端点 1 (批量) 端点 2 (中断) 端点 1 (同步) 端点 3 (批量) 字符串描述符 (可选) 说明:字符串描述符通过索引被设备、配置、接口描述符引用,不直接参与层级挂载。

3.2 各描述符结构详解

3.2.1 设备描述符 (Device Descriptor)

设备描述符是主机通过默认控制端点 (Endpoint 0) 获取的第一个描述符,长度为 18 字节。它定义了设备的整体属性,包括 USB 规范版本、设备类/子类/协议、厂商 ID (VID)、产品 ID (PID) 以及设备所需的配置数量。

偏移 (字节) 字段 大小 (字节) 说明
0bLength1描述符长度 (0x12 = 18)
1bDescriptorType1描述符类型 (0x01 = 设备)
2-3bcdUSB2USB 规范版本号 (BCD 编码)
4bDeviceClass1设备类代码 (0x00 在接口描述符中定义)
5bDeviceSubClass1设备子类代码
6bDeviceProtocol1设备协议代码
7bMaxPacketSize01端点 0 最大包大小
8-9idVendor2厂商 ID (VID)
10-11idProduct2产品 ID (PID)
12-13bcdDevice2设备版本号 (BCD 编码)
14iManufacturer1厂商字符串描述符索引
15iProduct1产品字符串描述符索引
16iSerialNumber1序列号字符串描述符索引
17bNumConfigurations1支持的配置数量
排查要点: 设备描述符中的 bMaxPacketSize0 字段必须与主机期望的端点 0 包大小一致(全速设备为 8/16/32/64 字节,高速设备固定为 64 字节)。若该值错误,主机将无法正确解析后续描述符。

3.2.2 配置描述符 (Configuration Descriptor)

配置描述符长度为 9 字节,描述设备的电源管理属性和接口数量。一个设备可以有多个配置,但同一时刻只能激活一个。主机通过 Set Configuration 请求选择激活的配置。

偏移 (字节) 字段 大小 (字节) 说明
0bLength1描述符长度 (0x09 = 9)
1bDescriptorType1描述符类型 (0x02 = 配置)
2-3wTotalLength2配置描述符及其所有子描述符的总长度
4bNumInterfaces1该配置下的接口数量
5bConfigurationValue1用于 Set Configuration 的配置值
6iConfiguration1配置字符串描述符索引
7bmAttributes1配置特性 (自供电/远程唤醒)
8bMaxPower1最大功耗 (单位 2mA)
常见故障: wTotalLength 字段必须精确等于配置描述符本身加上所有接口、端点及可选描述符的总字节数。若该值偏小,主机将截断后续描述符;若偏大,主机可能等待超时或解析到无效数据。

3.2.3 接口描述符 (Interface Descriptor)

接口描述符长度为 9 字节,描述一个功能接口的属性。一个配置可以包含多个接口,接口可以包含多个设置(Alternate Setting),用于实现功能切换(如音频设备的采样率切换)。

偏移 (字节) 字段 大小 (字节) 说明
0bLength1描述符长度 (0x09 = 9)
1bDescriptorType1描述符类型 (0x04 = 接口)
2bInterfaceNumber1接口编号 (从 0 开始)
3bAlternateSetting1备用设置编号
4bNumEndpoints1该接口使用的端点数量 (不包括端点 0)
5bInterfaceClass1接口类代码
6bInterfaceSubClass1接口子类代码
7bInterfaceProtocol1接口协议代码
8iInterface1接口字符串描述符索引

3.2.4 端点描述符 (Endpoint Descriptor)

端点描述符长度为 7 字节,描述一个非默认端点的传输属性。每个端点描述符定义了端点地址、传输类型、最大包大小和轮询间隔。

偏移 (字节) 字段 大小 (字节) 说明
0bLength1描述符长度 (0x07 = 7)
1bDescriptorType1描述符类型 (0x05 = 端点)
2bEndpointAddress1端点地址 (位 3-0: 编号, 位 7: 方向 0=OUT/1=IN)
3bmAttributes1传输类型 (位 1-0: 0=控制, 1=等时, 2=批量, 3=中断)
4-5wMaxPacketSize2最大包大小 (位 10-0: 大小, 位 12-11: 附加事务数)
6bInterval1轮询间隔 (单位: 帧/微帧)

3.2.5 字符串描述符 (String Descriptor)

字符串描述符提供人类可读的 Unicode 文本,用于描述厂商、产品、序列号等信息。字符串描述符不是必需的,但强烈建议包含以提升用户体验。主机通过 Get Descriptor 请求中的索引值获取对应的字符串。

// 字符串描述符结构示例 (USB 规范定义)
// 索引 0 为语言 ID 描述符
// 索引 1 为厂商字符串
// 索引 2 为产品字符串

// 语言 ID 描述符 (索引 0)
0x04,       // bLength = 4
0x03,       // bDescriptorType = 0x03 (字符串)
0x09, 0x04, // wLANGID[0] = 0x0409 (英语-美国)

// 厂商字符串 (索引 1)
0x1C,       // bLength = 28
0x03,       // bDescriptorType = 0x03
// Unicode 字符串: "Example Corp."
0x45, 0x00, 0x78, 0x00, 0x61, 0x00, 0x6D, 0x00,
0x70, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x20, 0x00,
0x43, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x70, 0x00,
0x2E, 0x00

3.3 描述符获取流程与故障点

主机在枚举过程中按固定顺序获取描述符。每个步骤都可能成为故障点:

  1. 获取设备描述符 (8 字节预览) — 主机先请求 8 字节以确认端点 0 最大包大小。
  2. 重置设备并分配地址 — 主机发送 Set Address 请求。
  3. 获取完整设备描述符 (18 字节) — 验证 VID/PID 和配置数量。
  4. 获取配置描述符 (9 字节预览) — 读取 wTotalLength 以确定总大小。
  5. 获取完整配置描述符集 — 包括所有接口、端点和可选描述符。
  6. 获取字符串描述符 (可选) — 主机根据索引请求字符串。
USB 描述符获取流程 1. 获取设备描述符 (8 字节) 2. 复位 + Set Address 3. 获取完整设备描述符 (18 字节) 4. 获取配置描述符 (9 字节预览) 5. 获取完整配置描述符集 6. 获取字符串描述符 (可选)

3.4 描述符验证清单

在排查枚举故障时,建议逐项核对以下关键字段:

  • bLength — 每个描述符的第一个字节必须等于该描述符的标准长度(设备=18,配置=9,接口=9,端点=7)。
  • bDescriptorType — 类型值必须正确(设备=0x01,配置=0x02,字符串=0x03,接口=0x04,端点=0x05)。
  • wTotalLength — 配置描述符的总长度必须包含所有子描述符。
  • bNumInterfaces 与 bNumEndpoints — 实际存在的接口/端点数量必须与声明一致。
  • wMaxPacketSize — 端点最大包大小必须符合传输类型规范(批量最大 512/1024 字节,中断最大 64/1024 字节,等时最大 1023 字节)。
  • bInterval — 中断端点的轮询间隔必须符合 USB 规范(全速 1-255ms,高速 1-16 帧)。
实战提示: 使用 USB 分析仪(如 Ellisys、LeCroy)或软件抓包工具(如 Wireshark + usbmon)捕获枚举过程,逐字节比对描述符数据。最常见的枚举失败原因包括:wTotalLength 计算错误、端点描述符缺失、以及字符串描述符索引指向不存在的索引。