3. USB 描述符体系:结构与作用
USB 描述符是设备向主机报告自身能力与配置信息的标准数据结构。主机通过枚举过程逐级获取这些描述符,从而构建完整的设备驱动模型。描述符体系呈树形层级:设备描述符 → 配置描述符 → 接口描述符 → 端点描述符,字符串描述符作为可选补充提供可读文本。
核心原则: 描述符的获取顺序与层级关系必须严格遵循 USB 规范。任何一级描述符的缺失、长度错误或字段值异常,都会导致枚举失败或设备功能异常。
3.1 描述符层级总览
下图展示了 USB 描述符的完整树形结构,主机从设备描述符开始,逐层向下解析。
3.2 设备描述符 (Device Descriptor)
设备描述符是主机获取的第一个描述符,位于描述符树的根节点。它描述了设备的整体属性,包括 USB 规范版本、设备类/子类/协议、厂商和产品标识等。主机通过 GET_DESCRIPTOR (bRequest = 1, wValue = 0x0100) 请求获取。
| 偏移 | 字段 | 长度(字节) | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度,固定为 18 (0x12) |
| 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-8 | bMaxPacketSize0 | 1 | 端点 0 的最大包大小 (USB 2.0 为 8/16/32/64) |
| 9-10 | idVendor | 2 | 厂商 ID (USB-IF 分配) |
| 11-12 | idProduct | 2 | 产品 ID (厂商自定义) |
| 13-14 | bcdDevice | 2 | 设备版本号 (BCD 码) |
| 15 | iManufacturer | 1 | 厂商字符串描述符索引 (0 表示无) |
| 16 | iProduct | 1 | 产品字符串描述符索引 |
| 17 | iSerialNumber | 1 | 序列号字符串描述符索引 |
| 18 | bNumConfigurations | 1 | 支持的配置数量 |
故障排查要点: 设备描述符的 bMaxPacketSize0 字段必须与 USB 速度匹配。USB 2.0 高速设备必须设为 64,全速设备可为 8/16/32/64。若该值错误,主机将无法正确解析后续描述符。
3.3 配置描述符 (Configuration Descriptor)
配置描述符描述设备的电源管理属性和接口数量。一个设备可以有多个配置,但同一时刻只能激活一个。主机通过 GET_DESCRIPTOR (wValue = 0x0200 + 配置索引) 获取。
| 偏移 | 字段 | 长度(字节) | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度,固定为 9 (0x09) |
| 1 | bDescriptorType | 1 | 描述符类型,配置描述符为 0x02 |
| 2-3 | wTotalLength | 2 | 配置描述符及其所有子描述符的总长度 |
| 4 | bNumInterfaces | 1 | 该配置下的接口数量 |
| 5 | bConfigurationValue | 1 | 用于 SetConfiguration 请求的配置值 |
| 6 | iConfiguration | 1 | 配置字符串描述符索引 |
| 7 | bmAttributes | 1 | 电源属性:D7=总线供电,D6=自供电,D5=远程唤醒 |
| 8 | bMaxPower | 1 | 最大电流需求,单位 2mA (值×2mA) |
常见故障: wTotalLength 字段必须等于配置描述符本身加上所有接口描述符、端点描述符及其他可选描述符的长度总和。若该值偏小,主机会截断后续描述符的解析;若偏大,主机可能等待超时。
3.4 接口描述符 (Interface Descriptor)
接口描述符描述一个功能单元,包含一组端点。一个配置可以有多个接口,接口可以交替设置 (Alternate Setting)。主机通过配置描述符返回的数据块中依次解析接口描述符。
| 偏移 | 字段 | 长度(字节) | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度,固定为 9 (0x09) |
| 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 | 接口字符串描述符索引 |
交替设置 (Alternate Setting) 的作用: 允许设备在同一个接口编号下提供不同的端点配置。例如,音频设备可以通过交替设置切换采样率或通道数,而无需重新枚举整个设备。
3.5 端点描述符 (Endpoint Descriptor)
端点描述符定义数据传输的端点属性,包括传输方向、传输类型、最大包大小和轮询间隔。除端点 0 外,每个非控制端点都必须有对应的端点描述符。
| 偏移 | 字段 | 长度(字节) | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度,固定为 7 (0x07) |
| 1 | bDescriptorType | 1 | 描述符类型,端点描述符为 0x05 |
| 2 | bEndpointAddress | 1 | 端点地址:D7=方向(0 OUT/1 IN),D3-D0=端点号 |
| 3 | bmAttributes | 1 | 传输类型:D1-D0=00控制/01等时/10批量/11中断 |
| 4-5 | wMaxPacketSize | 2 | 最大包大小 (高速批量/中断还需考虑突发传输位数) |
| 6 | bInterval | 1 | 轮询间隔:中断/等时端点有效,单位因速度而异 |
wMaxPacketSize 编码规则: 对于高速批量/中断端点,位 10:0 表示最大包大小,位 12:11 表示突发传输次数 (0=1次, 1=2次, 2=3次)。例如 0x0200 表示 512 字节 × 1 次突发。
3.6 字符串描述符 (String Descriptor)
字符串描述符提供可读的 Unicode 文本,用于描述厂商名称、产品名称、序列号等。字符串描述符通过索引引用,索引 0 返回支持的语言 ID 列表。
| 偏移 | 字段 | 长度(字节) | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度,可变 (2 + 字符串长度 × 2) |
| 1 | bDescriptorType | 1 | 描述符类型,字符串描述符为 0x03 |
| 2..n | bString | 可变 | Unicode 字符串 (UTF-16LE 编码) |
字符串描述符故障: 若设备声明了字符串索引 (iManufacturer/iProduct/iSerialNumber 非 0),但主机请求时设备返回 STALL 或空数据,会导致枚举挂起。部分主机在字符串请求失败后会重试,但重试次数有限,超时后枚举失败。
3.7 描述符获取流程与故障点
下图展示了主机获取描述符的典型流程,并标注了常见故障点。
3.8 描述符联合验证要点
在故障排查中,需要将枚举日志中的描述符数据与设备实际返回的原始字节进行比对。以下为关键验证项:
- 长度一致性: 设备描述符 bLength 必须为 18,配置描述符 bLength 必须为 9,接口描述符 bLength 必须为 9,端点描述符 bLength 必须为 7。任何偏差都表明固件描述符表定义错误。
- 类型字段: bDescriptorType 必须与描述符类型匹配 (设备=0x01, 配置=0x02, 字符串=0x03, 接口=0x04, 端点=0x05)。类型错误会导致主机解析错位。
- 配置总长度: wTotalLength 必须精确等于所有子描述符长度之和。常见错误是固件中新增了描述符但未更新 wTotalLength。
- 端点数量: 接口描述符中的 bNumEndpoints 必须与实际端点描述符数量一致。若声明了 3 个端点但只提供了 2 个描述符,主机将解析到错误数据。
- 字符串索引: 若 iManufacturer/iProduct/iSerialNumber 非 0,设备必须能响应对应索引的字符串请求。返回 STALL 或空描述符会导致主机超时。
实战建议: 使用 USB 分析仪抓取枚举阶段的 GET_DESCRIPTOR 请求与响应数据,逐字节比对描述符规范。将日志中的描述符转储为十六进制,对照 USB 规范表格逐字段检查,可快速定位固件描述符定义错误。