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

USB描述符是USB设备向主机报告自身能力与属性的结构化数据集合。主机通过标准设备请求(如GET_DESCRIPTOR)逐层获取这些描述符,从而完成设备枚举与驱动匹配。描述符体系采用严格的层级树状结构,每一层描述符都包含指向下一层描述符的字段。

核心要点:描述符是USB协议中“自描述”机制的基石。驱动开发者在调试端点配置与URB传输错误时,必须能够准确解析描述符中的、、等关键字段。

2.1 描述符层级结构总览

USB描述符从顶层到底层依次为:设备描述符 → 配置描述符 → 接口描述符 → 端点描述符。字符串描述符作为可选补充,为上述描述符中的索引字段提供可读文本。

USB 描述符层级树状结构 设备描述符 配置描述符 接口描述符 端点描述符 0..n 字符串描述符 说明:每个配置描述符可包含多个接口描述符;每个接口描述符可包含多个端点描述符。 字符串描述符通过索引字段被其他描述符引用,并非层级包含关系。

2.2 设备描述符(Device Descriptor)

设备描述符是USB设备的第一级描述符,位于描述符树的根节点。主机在枚举阶段首先通过GET_DESCRIPTOR(DEVICE)获取该描述符,其长度为固定的18字节。

偏移 字段 大小 说明
0bLength1描述符长度(固定0x12)
1bDescriptorType1描述符类型(设备=0x01)
2-3bcdUSB2USB规范版本号(BCD码,如0x0200表示USB 2.0)
4bDeviceClass1设备类代码(0x00表示在接口描述符中定义)
5bDeviceSubClass1设备子类代码
6bDeviceProtocol1设备协议代码
7bMaxPacketSize01端点0的最大包大小(8/16/32/64)
8-9idVendor2厂商ID(VID)
10-11idProduct2产品ID(PID)
12-13bcdDevice2设备版本号(BCD码)
14iManufacturer1厂商字符串描述符索引
15iProduct1产品字符串描述符索引
16iSerialNumber1序列号字符串描述符索引
17bNumConfigurations1支持的配置描述符数量
调试提示:在Linux内核中,设备描述符通过struct usb_device_descriptor结构体表示,定义在<linux/usb/ch9.h>中。使用lsusb -v命令可以查看实际设备的描述符内容。

2.3 配置描述符(Configuration Descriptor)

配置描述符描述设备的电源管理属性和接口数量。一个设备可以有多个配置(如低功耗模式和高性能模式),但同一时刻只能激活一个配置。配置描述符的长度为9字节,其后紧跟接口描述符和端点描述符。

偏移 字段 大小 说明
0bLength1描述符长度(固定0x09)
1bDescriptorType1描述符类型(配置=0x02)
2-3wTotalLength2配置描述符及其子描述符的总长度
4bNumInterfaces1该配置下的接口数量
5bConfigurationValue1用于SET_CONFIGURATION请求的配置值
6iConfiguration1配置字符串描述符索引
7bmAttributes1配置特性(D7:总线供电, D6:自供电, D5:远程唤醒)
8bMaxPower1最大功耗(单位2mA,如0x32表示100mA)

2.4 接口描述符(Interface Descriptor)

接口描述符描述一个功能单元(如键盘、音频、存储),一个配置可以包含多个接口。接口描述符长度为9字节,其bInterfaceClass字段决定了设备的功能类型,是驱动匹配的关键依据。

偏移 字段 大小 说明
0bLength1描述符长度(固定0x09)
1bDescriptorType1描述符类型(接口=0x04)
2bInterfaceNumber1接口编号(从0开始)
3bAlternateSetting1备用设置值(用于切换接口参数)
4bNumEndpoints1该接口使用的端点数量(不包括端点0)
5bInterfaceClass1接口类代码(如HID=0x03, Mass Storage=0x08)
6bInterfaceSubClass1接口子类代码
7bInterfaceProtocol1接口协议代码
8iInterface1接口字符串描述符索引
常见错误:接口描述符中的bAlternateSetting字段容易被忽略。当驱动未正确处理备用设置时,可能导致端点配置错误,进而引发URB传输超时或STALL错误。

2.5 端点描述符(Endpoint Descriptor)

端点描述符是描述符体系中最底层的描述符,直接定义数据传输的通道属性。每个非0端点都必须有对应的端点描述符,其长度为7字节(音频设备为9字节)。

偏移 字段 大小 说明
0bLength1描述符长度(固定0x07)
1bDescriptorType1描述符类型(端点=0x05)
2bEndpointAddress1端点地址(D7:方向, D3-D0:端点号)
3bmAttributes1传输类型(D1-D0: 0=控制, 1=等时, 2=批量, 3=中断)
4-5wMaxPacketSize2最大包大小(D10-D0: 大小, D12-D11: 附加事务数)
6bInterval1轮询间隔(中断/等时端点,单位ms或125μs)

端点地址与传输类型详解

bEndpointAddress字段的位定义:

  • Bit 7:方向(0=OUT/主机到设备,1=IN/设备到主机)
  • Bit 6-4:保留(必须为0)
  • Bit 3-0:端点号(0x00-0x0F)

bmAttributes字段的传输类型编码:

  • 00:控制传输(Control)——用于枚举和命令
  • 01:等时传输(Isochronous)——实时数据,无重传
  • 10:批量传输(Bulk)——大数据量,保证交付
  • 11:中断传输(Interrupt)——低延迟,轮询机制

2.6 字符串描述符(String Descriptor)

字符串描述符提供可读的Unicode文本信息,如厂商名称、产品名称和序列号。字符串描述符通过索引号被其他描述符引用,索引0为特殊语言ID描述符。字符串描述符的长度可变,前2字节为长度和类型,后续为Unicode字符串数据。

偏移 字段 大小 说明
0bLength1描述符长度(可变,包括长度和类型字节)
1bDescriptorType1描述符类型(字符串=0x03)
2..nbString可变Unicode字符串(UTF-16LE编码)
调试技巧:在Linux中,使用lsusb -v -s <bus>:<device> | grep -A 2 "iManufacturer\|iProduct\|iSerial"可以快速查看字符串描述符内容。如果字符串描述符读取失败(如设备返回STALL),通常表示设备未实现该字符串索引。

2.7 描述符解析实战:从原始字节到驱动配置

以下是一个典型的USB HID设备描述符原始数据示例(通过usbmon或逻辑分析仪捕获):

// 设备描述符(18字节)
12 01 00 02 00 00 00 40 0A 12 34 56 00 01 01 02 03 01

// 解析结果:
// bLength: 0x12 (18)
// bDescriptorType: 0x01 (DEVICE)
// bcdUSB: 0x0200 (USB 2.0)
// bDeviceClass: 0x00 (在接口中定义)
// bMaxPacketSize0: 0x40 (64字节)
// idVendor: 0x120A (示例厂商)
// idProduct: 0x5634 (示例产品)
// bNumConfigurations: 0x01 (1个配置)
// 配置描述符 + 接口描述符 + 端点描述符(组合数据)
09 02 22 00 01 01 00 A0 32
09 04 00 00 01 03 01 02 00
07 05 81 03 40 00 0A

// 解析结果:
// 配置描述符: wTotalLength=0x0022(34), bNumInterfaces=1, bMaxPower=0x32(100mA)
// 接口描述符: bInterfaceNumber=0, bNumEndpoints=1, bInterfaceClass=0x03(HID)
// 端点描述符: bEndpointAddress=0x81(IN,端点1), bmAttributes=0x03(中断传输)
//             wMaxPacketSize=0x0040(64字节), bInterval=10ms
驱动开发要点:在编写USB驱动时,通常不需要手动解析描述符原始字节。Linux内核提供了usb_get_descriptor()系列函数和struct usb_device中的缓存描述符。但理解描述符的二进制布局对于调试URB传输错误至关重要——例如,wMaxPacketSize解析错误会导致批量传输的分包边界错乱。

2.8 描述符错误排查清单

错误现象 可能原因 排查方法
枚举失败,设备不被识别 设备描述符bMaxPacketSize0与实际端点0能力不匹配 检查设备硬件配置,确认端点0 FIFO大小
驱动绑定失败 接口描述符bInterfaceClass/bInterfaceProtocol与驱动匹配表不一致 使用lsusb -v核对描述符,检查驱动id_table
URB传输返回-EPROTO 端点描述符wMaxPacketSize与实际硬件能力不符 验证端点最大包大小,检查是否超过硬件限制
中断传输无响应 端点描述符bInterval值过小导致总线拥塞,或过大导致延迟过高 调整bInterval值,参考USB规范建议范围
字符串描述符读取失败 设备未实现该索引的字符串,或语言ID不匹配 先读取索引0获取支持的语言ID列表