4. USB 描述符体系
USB 描述符(Descriptor)是 USB 设备向主机报告自身属性、能力与配置信息的数据结构。主机通过标准设备请求(如 GET_DESCRIPTOR)逐层获取这些描述符,从而完成设备枚举与驱动匹配。描述符体系采用严格的层级结构,从设备级到功能级逐级细化。
bLength 和 bDescriptorType 开头,主机据此解析后续字段。任何描述符内容的错误都可能导致枚举失败或驱动加载异常。
4.1 描述符层级结构总览
USB 描述符体系呈树形结构:一个设备只有一个设备描述符,设备描述符中指定了配置描述符的数量;每个配置描述符下包含一个或多个接口描述符;每个接口描述符下包含零个或多个端点描述符。此外,字符串描述符为其他描述符中的索引字段提供可读文本。
4.2 设备描述符 (Device Descriptor)
设备描述符是 USB 设备的第一级描述符,位于描述符树的根节点。主机通过 GET_DESCRIPTOR 请求获取设备描述符时,wValue 高字节为 0x01,低字节为 0x00。
| 偏移 | 字段 | 大小 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度,固定为 18 字节 |
| 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-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 | 支持的配置描述符数量 |
struct usb_device_descriptor 结构体表示,定义在 <linux/usb/ch9.h> 中。驱动开发中常通过 usb_device->descriptor 直接访问。
4.3 配置描述符 (Configuration Descriptor)
配置描述符描述设备的电源管理属性和接口数量。一个设备可以有多个配置(如低功耗模式和高性能模式),但同一时刻只能有一个配置被激活。
| 偏移 | 字段 | 大小 | 说明 |
|---|---|---|---|
| 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 | 配置特性:D7=1(必须),D6=1(自供电),D5=1(支持远程唤醒) |
| 8 | bMaxPower | 1 | 最大电流需求,以 2mA 为单位(USB 2.0 最大 500mA) |
wTotalLength 字段必须包含该配置下所有描述符的总长度。如果该值计算错误,主机在解析后续描述符时可能越界或解析失败,导致枚举中断。
4.4 接口描述符 (Interface Descriptor)
接口描述符描述一个功能单元(如键盘、音频流、大容量存储)。一个配置可以包含多个接口,接口可以设置备用设置(Alternate Setting),用于在运行时切换接口参数(如改变带宽)。
| 偏移 | 字段 | 大小 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度,固定为 9 字节 |
| 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 | 接口字符串描述符索引 |
usb_device_id 结构体支持按 bInterfaceClass、bInterfaceSubClass 和 bInterfaceProtocol 进行匹配。这是大多数类驱动(如 HID、UVC、Audio)的绑定依据。
4.5 端点描述符 (Endpoint Descriptor)
端点描述符描述除端点 0 以外的每个端点的属性,包括传输方向、传输类型和最大包大小。端点 0 是默认控制端点,其属性由设备描述符中的 bMaxPacketSize0 定义。
| 偏移 | 字段 | 大小 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度,固定为 7 字节 |
| 1 | bDescriptorType | 1 | 端点描述符类型,0x05 |
| 2 | bEndpointAddress | 1 | 端点地址:Bit 7 方向(0=OUT, 1=IN),Bit 3-0 端点号 |
| 3 | bmAttributes | 1 | 传输类型:Bit 1-0(0=控制, 1=等时, 2=批量, 3=中断) |
| 4-5 | wMaxPacketSize | 2 | 最大包大小(字节),USB 2.0 高速批量端点最大 512 字节 |
| 6 | bInterval | 1 | 轮询间隔(中断/等时端点):USB 2.0 单位为 125µs(微帧)或 1ms |
4.6 字符串描述符 (String Descriptor)
字符串描述符提供人类可读的 Unicode 文本,如厂商名称、产品名称和序列号。字符串描述符通过索引号被其他描述符引用。索引 0 是特殊的字符串描述符,它返回设备支持的语言 ID 列表(LANGID)。
| 偏移 | 字段 | 大小 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度(可变,取决于字符串长度) |
| 1 | bDescriptorType | 1 | 字符串描述符类型,0x03 |
| 2 | bString / wLANGID[0..n] | 可变 | 索引 0 时为 LANGID 数组;其他索引时为 Unicode 字符串 |
usb_string() 函数完成,驱动开发者通常不需要手动解析原始描述符数据。
4.7 其他描述符
4.7.1 BOS 描述符 (Binary Object Store)
BOS 描述符是 USB 3.0 引入的扩展描述符,用于描述设备对 USB 3.0 及更高版本特性的支持情况。它位于设备描述符之后,通过 GET_DESCRIPTOR 请求(类型 0x0F)获取。
| 偏移 | 字段 | 大小 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度,固定为 5 字节 |
| 1 | bDescriptorType | 1 | BOS 描述符类型,0x0F |
| 2-3 | wTotalLength | 2 | 整个 BOS 描述符集的总长度 |
| 4 | bNumDeviceCaps | 1 | 设备能力描述符的数量 |
BOS 描述符后跟随一个或多个设备能力描述符 (Device Capability Descriptor),用于声明 USB 3.0 的特定能力,如:
- USB 2.0 扩展能力:支持 LPM(链路电源管理)
- SuperSpeed 能力:支持 USB 3.0 高速操作
- 容器 ID 能力:提供唯一的设备标识符
- 平台能力:用于微软等厂商的扩展属性(如 WinUSB)
4.7.2 SuperSpeed 端点伴侣描述符 (SS Endpoint Companion Descriptor)
SuperSpeed 端点伴侣描述符是 USB 3.0 为 SuperSpeed 端点增加的额外描述符,紧跟在对应的端点描述符之后。它提供 SuperSpeed 模式下的额外端点属性。
| 偏移 | 字段 | 大小 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度,固定为 6 字节 |
| 1 | bDescriptorType | 1 | SS 端点伴侣描述符类型,0x30 |
| 2 | bMaxBurst | 1 | 最大突发传输包数(0-15) |
| 3 | bmAttributes | 1 | 属性:Bit 4-0 为流能力(批量端点),Bit 7-5 保留 |
| 4-5 | wBytesPerInterval | 2 | 每个轮询间隔传输的字节数(等时/中断端点) |
bMaxBurst 字段定义了每个微帧可以连续传输的最大包数。例如,bMaxBurst = 0 表示 1 包,bMaxBurst = 15 表示最多 16 包。这显著提高了批量传输的吞吐量。
4.8 描述符获取流程与实战代码
主机在枚举过程中按以下顺序获取描述符:
- 获取设备描述符(前 8 字节,用于确定
bMaxPacketSize0) - 复位设备,使用新的
bMaxPacketSize0重新获取完整设备描述符 - 获取配置描述符(前 9 字节,用于确定
wTotalLength) - 获取完整配置描述符集(包括接口和端点描述符)
- 如果存在字符串索引,获取字符串描述符
- 对于 USB 3.0 设备,获取 BOS 描述符
以下是一个简化的 Linux 内核代码片段,展示如何从 usb_device 结构中遍历描述符:
#include <linux/usb.h>
static void print_device_descriptors(struct usb_device *udev)
{
struct usb_device_descriptor *desc = &udev->descriptor;
struct usb_host_config *config;
struct usb_interface *iface;
struct usb_host_interface *altsetting;
struct usb_endpoint_descriptor *ep;
int i, j, k;
dev_info(&udev->dev, "Device: VID=0x%04x PID=0x%04x\n",
le16_to_cpu(desc->idVendor),
le16_to_cpu(desc->idProduct));
for (i = 0; i < udev->descriptor.bNumConfigurations; i++) {
config = udev->config + i;
dev_info(&udev->dev, " Config %d: %d interfaces\n",
i, config->desc.bNumInterfaces);
for (j = 0; j < config->desc.bNumInterfaces; j++) {
iface = config->interface[j];
for (k = 0; k < iface->num_altsetting; k++) {
altsetting = iface->altsetting + k;
dev_info(&udev->dev,
" Interface %d.%d: Class=0x%02x\n",
altsetting->desc.bInterfaceNumber,
altsetting->desc.bAlternateSetting,
altsetting->desc.bInterfaceClass);
for (ep = altsetting->endpoint;
ep != altsetting->endpoint +
altsetting->desc.bNumEndpoints;
ep++) {
dev_info(&udev->dev,
" Endpoint 0x%02x: MaxPkt=%d\n",
ep->desc.bEndpointAddress,
le16_to_cpu(ep->desc.wMaxPacketSize));
}
}
}
}
}
lsusb -v 命令查看设备的完整描述符树。如果设备枚举失败,首先检查 dmesg 输出中的 USB 错误信息,通常描述符长度或校验错误会直接打印出来。
4.9 描述符体系总结
下表总结了各类描述符的核心属性与用途: