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

USB 描述符体系是 USB 设备枚举过程中的核心数据结构,它定义了设备的身份、能力、功能接口以及数据传输方式。主机通过读取这些描述符来识别设备并加载相应的驱动程序。本章将详细解析五种标准描述符的结构、字段含义以及它们之间的层级关系。

核心概念:USB 描述符采用层级树状结构,从设备描述符(根节点)开始,向下依次包含配置描述符、接口描述符和端点描述符。字符串描述符作为可选补充,提供人类可读的文本信息。

2.1 描述符层级关系总览

USB 描述符的组织形式类似于文件系统的目录树。一个 USB 设备有且仅有一个设备描述符,它包含一个或多个配置描述符;每个配置描述符包含一个或多个接口描述符;每个接口描述符包含零个或多个端点描述符。字符串描述符通过索引被其他描述符引用。

USB 描述符层级关系图 USB 描述符层级关系 设备描述符 (1个) 配置描述符 #1 配置描述符 #2 接口 #0 接口 #1 接口 #0 端点 IN 端点 OUT 端点 IN 字符串描述符 (可选) 设备描述符 配置描述符 接口描述符 端点描述符 字符串描述符

2.2 设备描述符 (Device Descriptor)

设备描述符是 USB 设备的根描述符,位于描述符链的最顶层。主机通过控制传输的 GET_DESCRIPTOR 请求(描述符类型为 0x01)获取它。设备描述符提供了设备的全局信息,包括 USB 规范版本、设备类、厂商和产品标识等。

偏移量 字段名 大小 (字节) 说明
0 bLength 1 描述符长度,固定为 18 (0x12)
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/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 下使用 lsusb -v 可以查看设备的完整描述符信息。如果设备描述符中的 bMaxPacketSize0 设置错误(例如 USB 2.0 高速设备设为 8 而非 64),主机可能在枚举阶段直接复位设备,导致枚举失败。

2.3 配置描述符 (Configuration Descriptor)

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

偏移量 字段名 大小 (字节) 说明
0 bLength 1 描述符长度,固定为 9 (0x09)
1 bDescriptorType 1 描述符类型,配置描述符为 0x02
2-3 wTotalLength 2 整个配置的总长度(包括配置描述符本身 + 所有接口和端点描述符)
4 bNumInterfaces 1 该配置包含的接口数量
5 bConfigurationValue 1 配置值,用于 SET_CONFIGURATION 请求
6 iConfiguration 1 配置字符串描述符索引
7 bmAttributes 1 配置特性:D7=总线供电,D6=自供电,D5=远程唤醒
8 bMaxPower 1 最大电流需求,单位 2mA(如 50 表示 100mA)
常见故障:配置描述符中的 wTotalLength 必须精确等于所有子描述符的长度之和。如果该值偏小,主机可能无法完整读取后续的接口和端点描述符,导致枚举失败。在 Gadget 驱动中,常见错误是忘记更新 wTotalLength 字段。

2.4 接口描述符 (Interface Descriptor)

接口描述符定义了一个功能单元,它由一组端点组成,共同实现一个 USB 功能(如 HID、Mass Storage、CDC ACM)。一个配置可以包含多个接口,接口可以设置备用设置(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 接口类代码
6 bInterfaceSubClass 1 接口子类代码
7 bInterfaceProtocol 1 接口协议代码
8 iInterface 1 接口字符串描述符索引
Gadget 开发提示:在 Linux USB Gadget 框架中,接口描述符通过 usb_interface_descriptor 结构体定义。当实现复合设备(如同时支持 RNDIS 和 ACM)时,需要为每个功能分配独立的 bInterfaceNumber,并确保 bNumEndpoints 与实际注册的端点数量一致。

2.5 端点描述符 (Endpoint Descriptor)

端点描述符定义数据传输的端点属性,包括传输方向、传输类型和最大包大小。除了默认的控制端点(端点 0)外,每个非零端点都必须有对应的描述符。

偏移量 字段名 大小 (字节) 说明
0 bLength 1 描述符长度,固定为 7 (0x07)
1 bDescriptorType 1 描述符类型,端点描述符为 0x05
2 bEndpointAddress 1 端点地址:Bit 7 方向 (0=OUT, 1=IN),Bit 3-0 端点号
3 bmAttributes 1 传输类型:0=控制,1=等时,2=批量,3=中断
4-5 wMaxPacketSize 2 最大包大小(Bit 10-0 为大小,Bit 12-11 为附加事务数)
6 bInterval 1 中断/等时传输的轮询间隔(单位:帧/微帧)
端点描述符字段解码 端点描述符关键字段解码 bEndpointAddress (1 字节) Bit 7: 方向 Bit 6-4: 保留 Bit 3-0: 端点号 0x80 = IN 0x00 = OUT bmAttributes: 0=控制 1=等时 2=批量 3=中断

2.6 字符串描述符 (String Descriptor)

字符串描述符提供人类可读的 Unicode 文本信息,如厂商名称、产品名称和序列号。它是可选的,但强烈建议实现,因为操作系统通常使用字符串来识别设备并显示给用户。字符串描述符通过索引号被其他描述符引用(如 iManufacturer、iProduct)。

字符串描述符的结构如下:

偏移量 字段名 大小 (字节) 说明
0 bLength 1 描述符长度(包括头部和字符串数据)
1 bDescriptorType 1 描述符类型,字符串描述符为 0x03
2 ... bString 可变 Unicode 字符串(UTF-16LE 编码)
特殊索引:字符串描述符索引 0 用于获取支持的语言 ID 列表(LANGID 数组)。主机首先读取索引 0 的字符串描述符,获取设备支持的语言,然后再请求对应语言的字符串。在 Gadget 驱动中,必须至少实现索引 0 的字符串描述符。

2.7 描述符间的依赖关系与枚举流程

主机枚举 USB 设备时,按照固定的顺序读取描述符。理解这个顺序对于调试枚举故障至关重要。

  1. 步骤 1:主机检测到设备连接,复位设备,分配地址(SET_ADDRESS)。
  2. 步骤 2:主机发送 GET_DESCRIPTOR 请求读取设备描述符(前 8 字节,用于获取 bMaxPacketSize0)。
  3. 步骤 3:主机再次读取完整的设备描述符(18 字节)。
  4. 步骤 4:主机读取配置描述符(前 9 字节,获取 wTotalLength)。
  5. 步骤 5:主机根据 wTotalLength 读取完整的配置描述符集合(包括所有接口和端点描述符)。
  6. 步骤 6:主机解析配置集合,提取接口和端点信息。
  7. 步骤 7:主机根据 iManufacturer、iProduct 等索引读取字符串描述符(可选)。
  8. 步骤 8:主机选择配置(SET_CONFIGURATION),设备进入配置状态。
USB 枚举流程与描述符读取顺序 USB 枚举流程与描述符读取顺序 设备连接/复位 分配地址 读取设备描述符 读取配置描述符 解析接口/端点 读取字符串描述符 SET_CONFIGURATION 枚举失败/复位 控制传输阶段 描述符读取阶段 解析阶段 可选阶段 配置阶段 故障路径

2.8 描述符体系在 Linux Gadget 中的实现

在 Linux USB Gadget 框架中,描述符通过结构体数组和指针链表组织。以下是一个典型的 Gadget 描述符定义示例:

/* 设备描述符 */
static struct usb_device_descriptor gadget_device_desc = {
    .bLength            = USB_DT_DEVICE_SIZE,
    .bDescriptorType    = USB_DT_DEVICE,
    .bcdUSB             = cpu_to_le16(0x0200),
    .bDeviceClass       = 0,
    .bDeviceSubClass    = 0,
    .bDeviceProtocol    = 0,
    .bMaxPacketSize0    = 64,
    .idVendor           = cpu_to_le16(0x1D6B),  /* Linux Foundation */
    .idProduct          = cpu_to_le16(0x0104),
    .bcdDevice          = cpu_to_le16(0x0100),
    .iManufacturer