一、描述符概述
USB设备通过描述符(Descriptor)向主机报告自己的特性、能力和配置。描述符是一组结构化的数据,主机在设备枚举过程中通过GET_DESCRIPTOR请求获取。
1.1 描述符层次结构
设备描述符 (Device Descriptor)
└─ 设备限定符 (Device Qualifier, USB2.0高速设备)
└─ BOS描述符 (Binary Object Store, USB3.0+)
└─ 配置描述符 (Configuration Descriptor)
├─ 接口描述符 (Interface Descriptor)
│ ├─ 端点描述符 (Endpoint Descriptor)
│ ├─ 端点描述符
│ └─ 类特定描述符 (如HID描述符)
├─ 接口描述符(替代设置)
│ └─ 端点描述符
└─ 字符串描述符 (String Descriptor)
读取顺序:
- 设备描述符(18字节)
- 配置描述符(9字节头部,获取wTotalLength)
- 完整配置描述符树(包含所有接口、端点)
- 字符串描述符(按需读取)
1.2 描述符类型列表
| 值 | 名称 | 说明 |
|---|---|---|
| 0x01 | DEVICE | 设备描述符(每个设备1个) |
| 0x02 | CONFIGURATION | 配置描述符(1个或多个) |
| 0x03 | STRING | 字符串描述符(可选,多个) |
| 0x04 | INTERFACE | 接口描述符(在配置描述符内) |
| 0x05 | ENDPOINT | 端点描述符(在接口描述符内) |
| 0x06 | DEVICE_QUALIFIER | 设备限定符(USB2.0高速) |
| 0x07 | OTHER_SPEED_CONFIGURATION | 其它速度配置 |
| 0x08 | INTERFACE_POWER | 接口电源(已废弃) |
| 0x0F | BOS | 二进制对象存储(USB3.0) |
| 0x10 | DEVICE_CAPABILITY | 设备能力(BOS内) |
| 0x21 | HID | HID描述符(HID类特定) |
| 0x22 | REPORT | HID报告描述符(HID类) |
| 0x24 | CS_INTERFACE | 类特定接口(Audio、CDC等) |
| 0x25 | CS_ENDPOINT | 类特定端点 |
二、设备描述符(Device Descriptor)
2.1 结构定义
最顶层描述符,定义设备的全局属性,固定18字节。
struct usb_device_descriptor {
uint8_t bLength; // 描述符长度 = 18
uint8_t bDescriptorType; // 类型 = 0x01 (DEVICE)
uint16_t bcdUSB; // USB版本号 (BCD码)
uint8_t bDeviceClass; // 设备类代码
uint8_t bDeviceSubClass; // 设备子类代码
uint8_t bDeviceProtocol; // 设备协议代码
uint8_t bMaxPacketSize0; // 端点0最大包大小
uint16_t idVendor; // 厂商ID (VID)
uint16_t idProduct; // 产品ID (PID)
uint16_t bcdDevice; // 设备版本号 (BCD码)
uint8_t iManufacturer; // 制造商字符串索引
uint8_t iProduct; // 产品字符串索引
uint8_t iSerialNumber; // 序列号字符串索引
uint8_t bNumConfigurations; // 配置数量
} __attribute__((packed));
2.2 字段详解
[0] bLength
值:0x12 (18字节)
说明:设备描述符固定长度
[1] bDescriptorType
值:0x01
说明:标识这是设备描述符
[2-3] bcdUSB(USB版本,BCD码)
格式:0xJJMN
JJ = 主版本号
M = 次版本号
N = 子版本号
常见值:
0x0110 → USB 1.1
0x0200 → USB 2.0
0x0201 → USB 2.01
0x0300 → USB 3.0
0x0310 → USB 3.1
0x0320 → USB 3.2
示例:
设备支持USB 2.0 → bcdUSB = 0x0200
[4] bDeviceClass(设备类代码)
定义设备的功能类别
常见值:
0x00 = 在接口描述符中定义(复合设备)
0x01 = Audio(音频设备)
0x02 = CDC (Communication Device Class,通信设备)
0x03 = HID (Human Interface Device,人机接口)
0x05 = Physical(物理设备)
0x06 = Image(图像设备,如相机)
0x07 = Printer(打印机)
0x08 = Mass Storage(大容量存储,U盘)
0x09 = Hub(USB集线器)
0x0A = CDC-Data(通信数据接口)
0x0B = Smart Card(智能卡)
0x0D = Content Security(内容安全)
0x0E = Video(视频设备)
0x0F = Personal Healthcare(个人医疗)
0x10 = Audio/Video(音视频设备)
0xDC = Diagnostic(诊断设备)
0xE0 = Wireless Controller(无线控制器)
0xEF = Miscellaneous(杂项,需看接口)
0xFE = Application Specific(应用特定)
0xFF = Vendor Specific(厂商自定义)
示例:
USB键盘 → bDeviceClass = 0x00(在接口中定义HID)
U盘 → bDeviceClass = 0x08(Mass Storage)
USB-串口 → bDeviceClass = 0x02(CDC)
[5] bDeviceSubClass
设备子类,配合bDeviceClass使用
示例:
bDeviceClass = 0x08 (Mass Storage)
bDeviceSubClass = 0x06 → SCSI透明命令集
bDeviceSubClass = 0x01 → RBC (Reduced Block Commands)
bDeviceSubClass = 0x05 → ATAPI (CD/DVD)
[6] bDeviceProtocol
设备协议,配合Class和SubClass使用
示例:
Mass Storage (0x08) + SCSI (0x06)
bDeviceProtocol = 0x50 → Bulk-Only传输
bDeviceProtocol = 0x62 → UAS (USB Attached SCSI)
[7] bMaxPacketSize0
端点0(控制端点)的最大包大小
有效值:
Low-speed: 8字节
Full-speed: 8, 16, 32, 64字节
High-speed: 64字节
SuperSpeed: 512字节(实际用2^9=512的指数形式)
常见值:
0x08 → 8字节(低速设备)
0x40 → 64字节(全速/高速设备)
[8-9] idVendor(厂商ID,VID)
USB-IF分配的厂商识别码
格式:0xVVVV(小端序传输)
示例:
0x046D → Logitech(罗技)
0x05AC → Apple
0x0781 → SanDisk
0x04E8 → Samsung
0x1A86 → QinHeng (沁恒,CH340芯片)
0x0403 → FTDI
0x067B → Prolific (PL2303芯片)
获取方式:
- 官方:向USB-IF申请(需付费)
- 测试:使用0xFFFF或申请测试VID
[10-11] idProduct(产品ID,PID)
厂商自定义的产品识别码
格式:0xPPPP(小端序)
示例:
VID=0x046D, PID=0xC077 → 罗技某款鼠标
VID=0x1A86, PID=0x7523 → CH340串口芯片
VID+PID组合唯一标识一款设备
操作系统用它匹配驱动程序
[12-13] bcdDevice(设备版本号,BCD码)
厂商定义的设备固件版本
格式:0xJJMN(同bcdUSB)
示例:
0x0100 → 版本1.00
0x0205 → 版本2.05
0x1234 → 版本12.34
用途:
- 区分同一产品的不同硬件版本
- 固件升级后版本号递增
[14] iManufacturer
制造商字符串描述符的索引
值:
0x00 → 无制造商字符串
0x01-0xFF → 字符串描述符索引
示例:
iManufacturer = 0x01 → 字符串1 = "Acme Inc"
[15] iProduct
产品字符串描述符的索引
示例:
iProduct = 0x02 → 字符串2 = "USB Keyboard"
[16] iSerialNumber
序列号字符串描述符的索引
示例:
iSerialNumber = 0x03 → 字符串3 = "123456789ABC"
iSerialNumber = 0x00 → 无序列号
作用:
唯一标识同型号的不同设备
支持多个相同设备同时接入
[17] bNumConfigurations
配置描述符的数量
常见值:
0x01 → 只有1个配置(大多数设备)
0x02 → 2个配置(如不同电源模式)
注意:
大多数设备只实现1个配置
2.3 实际示例
示例1:USB键盘
十六进制:
12 01 10 01 00 00 00 08 6D 04 77 C0 00 01 01 02 00 01
字段解析:
[0] 12 bLength = 18字节
[1] 01 bDescriptorType = DEVICE
[2-3] 10 01 bcdUSB = 0x0110 (USB 1.1)
[4] 00 bDeviceClass = 在接口定义
[5] 00 bDeviceSubClass = 0
[6] 00 bDeviceProtocol = 0
[7] 08 bMaxPacketSize0 = 8字节
[8-9] 6D 04 idVendor = 0x046D (Logitech)
[10-11] 77 C0 idProduct = 0xC077
[12-13] 00 01 bcdDevice = 1.00
[14] 01 iManufacturer = 字符串1
[15] 02 iProduct = 字符串2
[16] 00 iSerialNumber = 无序列号
[17] 01 bNumConfigurations = 1个配置
说明:
- USB 1.1低速设备(键盘)
- 类在接口中定义(HID)
- 罗技厂商
示例2:U盘(Mass Storage)
十六进制:
12 01 00 02 00 00 00 40 81 07 50 55 00 01 01 02 03 01
字段解析:
[0] 12 bLength = 18字节
[1] 01 bDescriptorType = DEVICE
[2-3] 00 02 bcdUSB = 0x0200 (USB 2.0)
[4] 00 bDeviceClass = 在接口定义
[5] 00 bDeviceSubClass = 0
[6] 00 bDeviceProtocol = 0
[7] 40 bMaxPacketSize0 = 64字节
[8-9] 81 07 idVendor = 0x0781 (SanDisk)
[10-11] 50 55 idProduct = 0x5550
[12-13] 00 01 bcdDevice = 1.00
[14] 01 iManufacturer = 字符串1 "SanDisk"
[15] 02 iProduct = 字符串2 "Cruzer"
[16] 03 iSerialNumber = 字符串3 "序列号"
[17] 01 bNumConfigurations = 1个配置
说明:
- USB 2.0全速/高速设备
- Mass Storage类在接口中定义
- 有序列号(每个U盘唯一)
示例3:USB-CDC串口(CH340)
十六进制:
12 01 10 01 FF 00 00 08 86 1A 23 75 54 02 00 00 00 01
字段解析:
[0] 12 bLength = 18字节
[1] 01 bDescriptorType = DEVICE
[2-3] 10 01 bcdUSB = 0x0110 (USB 1.1)
[4] FF bDeviceClass = 0xFF (Vendor Specific)
[5] 00 bDeviceSubClass = 0
[6] 00 bDeviceProtocol = 0
[7] 08 bMaxPacketSize0 = 8字节
[8-9] 86 1A idVendor = 0x1A86 (QinHeng)
[10-11] 23 75 idProduct = 0x7523 (CH340)
[12-13] 54 02 bcdDevice = 2.54
[14] 00 iManufacturer = 无字符串
[15] 00 iProduct = 无字符串
[16] 00 iSerialNumber = 无字符串
[17] 01 bNumConfigurations = 1个配置
说明:
- CH340使用厂商自定义类
- 无字符串描述符(成本优化)
二、描述符大小总结表
2.1 固定大小的描述符
| 描述符类型 | 大小(字节) | 标识字段 | 说明 |
|---|---|---|---|
| 设备描述符 | 18 | bLength=0x12 | 始终固定18字节 |
| 配置描述符 | 9 | bLength=0x09 | 仅配置本身固定9字节 |
| 接口描述符 | 9 | bLength=0x09 | 始终固定9字节 |
| 端点描述符 | 7 | bLength=0x07 | 标准端点固定7字节 |
| HID描述符 | 9+ | bLength=0x09+ | 基本9字节,可扩展 |
| 设备限定符 | 10 | bLength=0x0A | USB 2.0高速设备 |
关键点:
- 上述描述符的结构是固定的,但配置描述符的wTotalLength包含整棵树
- 所有描述符第一个字节
bLength告诉你实际长度 - 即使未来USB规范扩展,旧设备仍用固定长度
2.2 可变大小的描述符
| 描述符类型 | 大小范围 | 计算公式 | 说明 |
|---|---|---|---|
| 字符串描述符 | 2 ~ 255字节 | 2 + 字符数×2 | UTF-16LE编码 |
| CDC功能描述符 | 4 ~ 19字节 | 取决于子类型 | Header(5), ACM(4), Union(5) |
| Audio控制描述符 | 8 ~ 255字节 | 取决于单元数 | 输入/输出端子、混音器等 |
| Audio流格式描述符 | 11 ~ 255字节 | 取决于采样率数 | 每个采样率3字节 |
| Video格式描述符 | 27 ~ 255字节 | 取决于帧描述符数 | 每帧26字节 |
| Hub描述符 | 7 ~ 71字节 | 7 + (端口数+7)/8 × 2 | 端口电源控制掩码 |
| SuperSpeed端点伴随 | 6 | bLength=0x06 | USB 3.0特有 |
关键点:
- 字符串描述符长度完全取决于字符串内容
- 类特定描述符长度由类规范定义
- 始终读取第一个字节
bLength来确定实际长度
2.3 读取描述符的通用策略
// 安全读取任何描述符的通用代码
uint8_t first_byte[1];
usb_control_transfer(GET_DESCRIPTOR, ..., first_byte, 1);
uint8_t actual_length = first_byte[0]; // bLength字段
uint8_t *full_descriptor = malloc(actual_length);
usb_control_transfer(GET_DESCRIPTOR, ..., full_descriptor, actual_length);
// 现在可以安全解析
特殊情况:配置描述符
// 1. 先读9字节配置描述符头
uint8_t config_header[9];
usb_control_transfer(GET_DESCRIPTOR, CONFIG, 0, config_header, 9);
// 2. 提取wTotalLength(小端序)
uint16_t total_length = config_header[2] | (config_header[3] << 8);
// 3. 读取完整配置描述符树
uint8_t *full_tree = malloc(total_length);
usb_control_transfer(GET_DESCRIPTOR, CONFIG, 0, full_tree, total_length);
// 4. 遍历树中的各个描述符
uint8_t *ptr = full_tree;
while (ptr < full_tree + total_length) {
uint8_t desc_len = ptr[0]; // 当前描述符长度
uint8_t desc_type = ptr[1]; // 当前描述符类型
switch (desc_type) {
case 0x02: handle_config(ptr); break;
case 0x04: handle_interface(ptr); break;
case 0x05: handle_endpoint(ptr); break;
case 0x21: handle_hid(ptr); break;
// ...
}
ptr += desc_len; // 跳到下一个描述符
}
2.4 为什么某些描述符是可变长度?
字符串描述符(最常见的可变长度)
问题:制造商名称可能是 "HP" 或 "Logitech International"
固定长度 = 浪费空间或限制表达
解决方案:
bLength = 2 + strlen × 2 (UTF-16LE编码)
示例:
"USB" → 2 + 3×2 = 8字节
"Logitech International" → 2 + 22×2 = 46字节
Hub描述符(端口数不定)
问题:Hub可能有2个端口或127个端口
固定长度 = 为127端口预留空间(每个Hub浪费125个端口的空间)
解决方案:
bLength = 7 + (端口数+7)/8 × 2 (位掩码)
示例:
4端口Hub → 7 + 1×2 = 9字节
16端口Hub → 7 + 3×2 = 13字节
Audio格式描述符(采样率数量不定)
问题:设备可能支持1种采样率或20种
固定长度 = 浪费或限制
解决方案:
bLength = 8 + 采样率数×3
示例:
仅48kHz → 8 + 1×3 = 11字节
支持8种采样率 → 8 + 8×3 = 32字节
CDC功能描述符(功能类型不定)
问题:不同CDC子类需要不同的功能描述符
- PSTN子类需要Call Management, ACM
- Ethernet子类需要Ethernet Networking
- WHCM子类需要WHCM
解决方案:每种功能描述符独立定义长度
Header: 5字节
Call Management: 5字节
ACM: 4字节
Union: 5字节
Ethernet: 13字节
三、配置描述符(Configuration Descriptor)
3.1 结构定义
定义设备的一种工作配置,包含功耗、接口数量等信息。固定9字节。
struct usb_configuration_descriptor {
uint8_t bLength; // 描述符长度 = 9
uint8_t bDescriptorType; // 类型 = 0x02 (CONFIGURATION)
uint16_t wTotalLength; // 配置描述符树总长度
uint8_t bNumInterfaces; // 接口数量
uint8_t bConfigurationValue; // 配置值(SET_CONFIGURATION用)
uint8_t iConfiguration; // 配置字符串索引
uint8_t bmAttributes; // 配置属性
uint8_t bMaxPower; // 最大功耗
} __attribute__((packed));
3.2 字段详解
[0] bLength
值:0x09 (9字节)
说明:配置描述符固定长度
[1] bDescriptorType
值:0x02
说明:标识这是配置描述符
[2-3] wTotalLength
配置描述符树的总字节数(包含所有接口、端点描述符)
计算公式:
wTotalLength = 配置描述符(9)
+ 所有接口描述符(每个9)
+ 所有类特定描述符(长度不定)
+ 所有端点描述符(每个7)
示例计算:
1. 简单U盘(无类特定描述符)
= 配置(9) + 接口(9) + 端点IN(7) + 端点OUT(7)
= 9 + 9 + 7 + 7
= 32字节 (0x0020)
2. HID键盘(有HID类描述符)
= 配置(9) + 接口(9) + HID描述符(9) + 端点IN(7)
= 9 + 9 + 9 + 7
= 34字节 (0x0022)
为什么要加HID描述符?
- HID类要求在接口和端点之间插入HID描述符
- HID描述符告诉主机:报告描述符有多长
- 顺序:配置 → 接口 → HID → 端点
3. CDC虚拟串口(多个CDC功能描述符)
= 配置(9)
+ 接口0-控制(9)
+ CDC Header(5)
+ CDC Call Management(5)
+ CDC ACM(4)
+ CDC Union(5)
+ 端点1 IN-中断(7)
+ 接口1-数据(9)
+ 端点2 IN-批量(7)
+ 端点3 OUT-批量(7)
= 9 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 7
= 67字节 (0x0043)
4. 音频设备(更复杂)
= 配置(9)
+ 接口0-控制(9) + 音频控制类描述符(多个)
+ 接口1-流(9) + 音频流类描述符(多个) + 端点(9,音频端点更长)
= 可能100+字节
读取策略:
1. 先请求9字节获取wTotalLength
SETUP: 80 06 02 00 00 00 09 00
2. 读取wTotalLength字段:
假设返回 09 02 22 00 ...
↑↑
0x0022 = 34字节
3. 再次请求完整的34字节
SETUP: 80 06 02 00 00 00 22 00
3.2.1 为什么某些类需要额外的类特定描述符?
原因:标准描述符(配置、接口、端点)只能描述通用属性,无法描述类特有的功能。
HID类的需求
标准接口描述符能说明:
✓ 这是HID类
✓ 有1个中断IN端点
标准描述符无法说明:
✗ 报告描述符有多长?
✗ 支持哪个HID版本?
✗ 是否有国家代码?
解决方案:插入HID描述符
配置 → 接口(HID) → [HID描述符] → 端点
↑
额外的9字节
CDC类的需求
标准描述符无法说明:
✗ 控制接口和数据接口如何关联?
✗ 支持哪些AT命令能力?
✗ 控制接口能处理呼叫管理吗?
解决方案:插入多个CDC功能描述符
配置 → 接口0(CDC控制)
→ [CDC Header: 版本号]
→ [CDC Call Management: 能力位]
→ [CDC ACM: 支持的命令]
→ [CDC Union: 接口0绑定接口1]
→ 端点1 IN
→ 接口1(CDC数据) → 端点2/3
Audio类的需求
标准描述符无法说明:
✗ 音频格式(PCM/AC3/AAC)
✗ 采样率(44.1kHz/48kHz/96kHz)
✗ 位深度(16bit/24bit)
✗ 声道数(立体声/5.1环绕)
✗ 音量控制单元的ID
解决方案:插入音频类特定描述符
- Audio Control Interface Descriptor
- Input/Output Terminal Descriptor
- Feature Unit Descriptor (音量、静音)
- Audio Streaming Interface Descriptor
- Format Type Descriptor
哪些类需要额外描述符?
| 类 | 需要类特定描述符 | 原因 |
|---|---|---|
| Mass Storage | ❌ 不需要 | 标准描述符足够 |
| HID | ✅ 需要 | 报告描述符长度、版本 |
| CDC | ✅ 需要 | 接口关联、功能能力 |
| Audio | ✅ 需要 | 音频格式、控制单元 |
| Video | ✅ 需要 | 视频格式、帧率 |
| Printer | ❌ 不需要 | 标准描述符足够 |
| Hub | ✅ 需要 | Hub特定(端口数、电源) |
3.2.2 wTotalLength实战演练
例子1:U盘完整计算
配置描述符树结构:
┌─ 配置描述符 9字节
├─ 接口0描述符 9字节
│ ├─ 端点1 IN描述符 7字节
│ └─ 端点2 OUT描述符 7字节
└─ 总计 32字节
十六进制验证:
09 02 20 00 01 01 00 80 32 ← 配置描述符
↑↑
0x0020 = 32字节 ✓
完整32字节:
09 02 20 00 01 01 00 80 32 (配置 9字节)
09 04 00 00 02 08 06 50 00 (接口 9字节)
07 05 81 02 00 02 00 (端点1 IN, 7字节)
07 05 02 02 00 02 00 (端点2 OUT, 7字节)
例子2:HID键盘完整计算
配置描述符树结构:
┌─ 配置描述符 9字节
├─ 接口0描述符 9字节
│ ├─ HID描述符 9字节 ← 额外插入!
│ └─ 端点1 IN描述符 7字节
└─ 总计 34字节
十六进制验证:
09 02 22 00 01 01 00 A0 32 ← 配置描述符
↑↑
0x0022 = 34字节 ✓
完整34字节:
09 02 22 00 01 01 00 A0 32 (配置 9字节)
09 04 00 00 01 03 01 01 00 (接口 9字节, HID类)
09 21 11 01 00 01 22 3F 00 (HID描述符 9字节) ← 这里!
07 05 81 03 08 00 0A (端点1 IN, 7字节)
关键点:
- HID描述符不是端点描述符
- 它位于接口和端点之间
- 作用:告诉主机报告描述符长度(0x3F=63字节)
例子3:CDC串口完整计算
配置描述符树结构:
┌─ 配置描述符 9字节
├─ 接口0描述符(控制) 9字节
│ ├─ CDC Header功能描述符 5字节 ← CDC特定
│ ├─ CDC Call Mgmt功能描述符 5字节 ← CDC特定
│ ├─ CDC ACM功能描述符 4字节 ← CDC特定
│ ├─ CDC Union功能描述符 5字节 ← CDC特定
│ └─ 端点1 IN描述符 7字节
├─ 接口1描述符(数据) 9字节
│ ├─ 端点2 IN描述符 7字节
│ └─ 端点3 OUT描述符 7字节
└─ 总计 67字节
计算:9+9+5+5+4+5+7+9+7+7 = 67 (0x0043)
十六进制验证:
09 02 43 00 02 01 00 80 32 ← 配置描述符
↑↑
0x0043 = 67字节 ✓
关键点:
- CDC需要4个功能描述符
- 它们位于控制接口和其端点之间
- 数据接口不需要功能描述符
例子4:USB Hub计算
配置描述符树结构:
┌─ 配置描述符 9字节
├─ 接口0描述符 9字节
│ └─ 端点1 IN描述符 7字节 (状态变化中断)
└─ 总计 25字节
注意:Hub描述符(长度不定)不在配置描述符树中!
Hub描述符通过 GET_DESCRIPTOR(Hub) 单独请求
wTotalLength = 9 + 9 + 7 = 25字节
[4] bNumInterfaces
此配置包含的接口数量
示例:
0x01 → 1个接口(简单设备,如鼠标)
0x02 → 2个接口(复合设备,如USB-CDC:控制接口+数据接口)
0x03 → 3个接口(音视频设备:音频控制+音频流+视频流)
注意:
不包括替代设置(Alternate Settings)
只计算bInterfaceNumber的不同值
[5] bConfigurationValue
配置编号,SET_CONFIGURATION使用此值激活配置
值:
0x00 → 保留(表示未配置状态)
0x01-0xFF → 有效配置值
示例:
bConfigurationValue = 0x01
主机发送 SET_CONFIGURATION(wValue=0x0001) 激活此配置
[6] iConfiguration
配置字符串描述符索引
示例:
0x00 → 无配置名称
0x01 → 字符串1 = "Bus Powered Configuration"
[7] bmAttributes
配置属性位图
位布局:
D7 = 1(保留,必须为1)
D6 = Self-Powered(自供电)
0 = 总线供电
1 = 自供电(有外部电源)
D5 = Remote Wakeup(远程唤醒)
0 = 不支持远程唤醒
1 = 支持远程唤醒(可从挂起态唤醒主机)
D4:D0 = 保留(必须为0)
常见值:
0x80 = 10000000 → 总线供电,无远程唤醒
0xA0 = 10100000 → 总线供电,支持远程唤醒
0xC0 = 11000000 → 自供电,无远程唤醒
0xE0 = 11100000 → 自供电,支持远程唤醒
示例:
USB键盘 → 0x80(总线供电)
外置硬盘 → 0xC0(自供电,独立电源)
无线鼠标 → 0xA0(总线供电,支持唤醒)
[8] bMaxPower
设备从总线获取的最大电流(单位:2mA)
计算公式:
实际电流(mA) = bMaxPower × 2
示例:
0x32 (50) → 50 × 2 = 100mA
0xFA (250) → 250 × 2 = 500mA(USB 2.0最大值)
0x00 (0) → 0mA(自供电设备)
USB规范限制:
Low-speed: 最大100mA
Full-speed: 最大500mA
High-speed: 最大500mA
SuperSpeed: 最大900mA(单位变为8mA)
注意:
自供电设备也要填写从总线消耗的电流
超过500mA必须自供电
3.3 实际示例
示例1:USB鼠标配置描述符
十六进制:
09 02 22 00 01 01 00 A0 32
字段解析:
[0] 09 bLength = 9字节
[1] 02 bDescriptorType = CONFIGURATION
[2-3] 22 00 wTotalLength = 0x0022 (34字节)
[4] 01 bNumInterfaces = 1个接口
[5] 01 bConfigurationValue = 1
[6] 00 iConfiguration = 无字符串
[7] A0 bmAttributes = 10100000
D7=1 (保留)
D6=0 (总线供电)
D5=1 (远程唤醒)
[8] 32 bMaxPower = 50 → 100mA
说明:
完整描述符树34字节
1个HID接口
总线供电,支持唤醒(无线鼠标)
最大100mA
示例2:USB-CDC串口配置描述符
十六进制:
09 02 43 00 02 01 00 80 32
字段解析:
[0] 09 bLength = 9字节
[1] 02 bDescriptorType = CONFIGURATION
[2-3] 43 00 wTotalLength = 0x0043 (67字节)
[4] 02 bNumInterfaces = 2个接口
接口0: CDC控制接口
接口1: CDC数据接口
[5] 01 bConfigurationValue = 1
[6] 00 iConfiguration = 无字符串
[7] 80 bmAttributes = 10000000
总线供电,无远程唤醒
[8] 32 bMaxPower = 50 → 100mA
说明:
CDC设备需要2个接口
完整描述符树67字节
四、接口描述符(Interface Descriptor)
4.1 结构定义
定义设备的一个功能接口,比如键盘接口、鼠标接口。固定9字节。
struct usb_interface_descriptor {
uint8_t bLength; // 描述符长度 = 9
uint8_t bDescriptorType; // 类型 = 0x04 (INTERFACE)
uint8_t bInterfaceNumber; // 接口编号
uint8_t bAlternateSetting; // 替代设置编号
uint8_t bNumEndpoints; // 端点数量(不含端点0)
uint8_t bInterfaceClass; // 接口类代码
uint8_t bInterfaceSubClass; // 接口子类代码
uint8_t bInterfaceProtocol; // 接口协议代码
uint8_t iInterface; // 接口字符串索引
} __attribute__((packed));
4.2 字段详解
[0] bLength
值:0x09 (9字节)
[1] bDescriptorType
值:0x04 (INTERFACE)
[2] bInterfaceNumber
接口编号,从0开始
示例:
单接口设备 → bInterfaceNumber = 0
双接口设备 → 接口0, 接口1
CDC设备:
接口0 = 控制接口(发送AT命令)
接口1 = 数据接口(传输串口数据)
[3] bAlternateSetting
替代设置编号,同一接口的不同配置
用途:
0x00 → 默认设置
0x01, 0x02... → 替代设置(不同带宽、端点配置)
示例(音频设备):
接口0, AlternateSetting 0 → 无音频流(0端点)
接口0, AlternateSetting 1 → 48kHz采样(等时端点)
接口0, AlternateSetting 2 → 96kHz采样(更大等时端点)
切换:
SET_INTERFACE(wValue=AlternateSetting, wIndex=InterfaceNumber)
[4] bNumEndpoints
此接口使用的端点数量(不包括端点0)
示例:
0x00 → 只用端点0(控制接口)
0x01 → 1个端点(单向通信,如键盘只有IN端点)
0x02 → 2个端点(双向通信,如串口有IN+OUT端点)
0x03 → 3个端点(CDC控制接口:1个中断IN + 数据接口的2个批量端点)
[5] bInterfaceClass
接口类代码(同设备描述符的bDeviceClass)
常见值:
0x01 = Audio(音频)
0x02 = CDC (通信设备)
0x03 = HID (人机接口)
0x05 = Physical
0x06 = Image(图像)
0x07 = Printer(打印机)
0x08 = Mass Storage(大容量存储)
0x09 = Hub
0x0A = CDC-Data(CDC数据接口)
0x0B = Smart Card
0x0D = Content Security
0x0E = Video(视频)
0x0F = Personal Healthcare
0x10 = Audio/Video
0xFE = Application Specific
0xFF = Vendor Specific(厂商自定义)
[6] bInterfaceSubClass
接口子类,配合bInterfaceClass使用
HID类示例:
bInterfaceClass = 0x03 (HID)
bInterfaceSubClass = 0x00 → 无子类
bInterfaceSubClass = 0x01 → Boot Interface(BIOS兼容)
Mass Storage类示例:
bInterfaceClass = 0x08
bInterfaceSubClass = 0x06 → SCSI透明命令集
[7] bInterfaceProtocol
接口协议
HID类示例:
bInterfaceClass = 0x03, bInterfaceSubClass = 0x01 (Boot)
bInterfaceProtocol = 0x01 → Keyboard(键盘)
bInterfaceProtocol = 0x02 → Mouse(鼠标)
Mass Storage类示例:
bInterfaceClass = 0x08, bInterfaceSubClass = 0x06 (SCSI)
bInterfaceProtocol = 0x50 → Bulk-Only传输
[8] iInterface
接口字符串索引
示例:
0x00 → 无接口名称
0x01 → 字符串1 = "HID Keyboard Interface"
4.3 实际示例
示例1:HID键盘接口
十六进制:
09 04 00 00 01 03 01 01 00
字段解析:
[0] 09 bLength = 9字节
[1] 04 bDescriptorType = INTERFACE
[2] 00 bInterfaceNumber = 0
[3] 00 bAlternateSetting = 0(默认)
[4] 01 bNumEndpoints = 1个端点(中断IN)
[5] 03 bInterfaceClass = HID
[6] 01 bInterfaceSubClass = Boot Interface
[7] 01 bInterfaceProtocol = Keyboard
[8] 00 iInterface = 无字符串
说明:
HID键盘
Boot协议(BIOS可识别)
1个中断IN端点(发送按键)
示例2:Mass Storage接口(U盘)
十六进制:
09 04 00 00 02 08 06 50 00
字段解析:
[0] 09 bLength = 9字节
[1] 04 bDescriptorType = INTERFACE
[2] 00 bInterfaceNumber = 0
[3] 00 bAlternateSetting = 0
[4] 02 bNumEndpoints = 2个端点(批量IN+OUT)
[5] 08 bInterfaceClass = Mass Storage
[6] 06 bInterfaceSubClass = SCSI
[7] 50 bInterfaceProtocol = Bulk-Only传输
[8] 00 iInterface = 无字符串
说明:
SCSI U盘
Bulk-Only传输协议
双向批量端点
示例3:CDC控制接口
十六进制:
09 04 00 00 01 02 02 01 00
字段解析:
[0] 09 bLength = 9字节
[1] 04 bDescriptorType = INTERFACE
[2] 00 bInterfaceNumber = 0(控制接口)
[3] 00 bAlternateSetting = 0
[4] 01 bNumEndpoints = 1个端点(中断IN)
[5] 02 bInterfaceClass = CDC (通信)
[6] 02 bInterfaceSubClass = Abstract Control Model
[7] 01 bInterfaceProtocol = AT Commands (V.25ter)
[8] 00 iInterface = 无字符串
说明:
CDC虚拟串口控制接口
用于发送AT命令、查询状态
1个中断IN端点通知主机状态变化
五、端点描述符(Endpoint Descriptor)
5.1 结构定义
定义一个数据端点的属性,包括方向、传输类型、最大包大小等。固定7字节。
struct usb_endpoint_descriptor {
uint8_t bLength; // 描述符长度 = 7
uint8_t bDescriptorType; // 类型 = 0x05 (ENDPOINT)
uint8_t bEndpointAddress; // 端点地址(含方向)
uint8_t bmAttributes; // 端点属性(传输类型)
uint16_t wMaxPacketSize; // 最大包大小
uint8_t bInterval; // 轮询间隔
} __attribute__((packed));
5.2 字段详解
[0] bLength
值:0x07 (7字节)
[1] bDescriptorType
值:0x05 (ENDPOINT)
[2] bEndpointAddress
端点地址,包含端点号和方向
位布局:
D7 = Direction(方向)
0 = OUT(主机→设备)
1 = IN(设备→主机)
D6:D4 = 保留(必须为0)
D3:D0 = Endpoint Number(端点号,0-15)
示例:
0x81 = 10000001 → 端点1 IN
0x01 = 00000001 → 端点1 OUT
0x82 = 10000010 → 端点2 IN
0x02 = 00000010 → 端点2 OUT
0x8F = 10001111 → 端点15 IN
注意:
端点0(控制端点)不在端点描述符中描述
IN/OUT是从主机视角:
IN = 设备发送数据给主机
OUT = 主机发送数据给设备
[3] bmAttributes
端点属性,定义传输类型和同步方式
位布局:
D7:D6 = 保留(等时传输时用于同步类型)
D5:D4 = 保留(等时传输时用于用途类型)
D3:D2 = 保留(必须为0)
D1:D0 = Transfer Type(传输类型)
00 = Control(控制传输)
01 = Isochronous(等时传输)
10 = Bulk(批量传输)
11 = Interrupt(中断传输)
传输类型详解:
1. Control (0x00)
- 用于控制命令
- 有错误检测和重传
- 端点0专用(不在端点描述符中)
2. Isochronous (0x01)
- 用于音频/视频流
- 无错误重传,保证带宽
- 可能丢包但保证实时性
D5:D4 = Usage Type(用途)
00 = Data endpoint
01 = Feedback endpoint
10 = Implicit feedback data endpoint
11 = Reserved
D3:D2 = Synchronization Type(同步类型)
00 = No Synchronization
01 = Asynchronous
10 = Adaptive
11 = Synchronous
3. Bulk (0x02)
- 用于大量数据传输(U盘、打印机)
- 有错误检测和重传
- 无保证带宽,使用剩余带宽
4. Interrupt (0x03)
- 用于小量周期性数据(键盘、鼠标)
- 有错误检测和重传
- 保证最大延迟时间
常见值:
0x02 → Bulk(批量端点)
0x03 → Interrupt(中断端点)
0x01 → Isochronous(等时端点)
[4-5] wMaxPacketSize
端点最大包大小(字节)
格式(D15:D11用于高带宽):
D15:D13 = 保留(必须为0)
D12:D11 = 额外事务(高速等时/中断端点)
00 = 每微帧1个事务(wMaxPacketSize字节)
01 = 每微帧2个事务(2×wMaxPacketSize字节)
10 = 每微帧3个事务(3×wMaxPacketSize字节)
11 = 保留
D10:D0 = 最大包大小
速度限制:
Low-speed:
中断端点: 1-8字节
Full-speed:
批量端点: 8, 16, 32, 64字节
中断端点: 1-64字节
等时端点: 1-1023字节
High-speed:
批量端点: 512字节
中断端点: 1-1024字节
等时端点: 1-1024字节
高带宽等时: 最多3×1024=3072字节/微帧
示例:
0x0008 → 8字节(低速中断)
0x0040 → 64字节(全速批量)
0x0200 → 512字节(高速批量)
0x0400 → 1024字节(高速中断)
0x1400 → 1024字节×2事务=2048字节/微帧(高带宽)
[6] bInterval
轮询间隔(单位随速度和类型而变)
Low-speed / Full-speed:
中断端点: 1-255(单位:帧,1ms)
示例:bInterval=10 → 每10ms轮询一次
等时端点: 1-16(单位:帧,1ms)
示例:bInterval=1 → 每1ms传输一次
High-speed:
中断端点: 1-16(单位:微帧,125μs)
实际间隔 = 2^(bInterval-1) × 125μs
示例:
bInterval=1 → 2^0=1微帧 = 125μs
bInterval=4 → 2^3=8微帧 = 1ms
bInterval=10 → 2^9=512微帧 = 64ms
等时端点: 1-16(单位:微帧,125μs)
同中断端点
批量端点: 0-255
bInterval=0 → 每NAK后尽快重试
其它值:建议值,不强制
常见值:
0x0A (10) → Full-speed中断,每10ms(键盘常用)
0x01 (1) → Full-speed中断,每1ms(鼠标高速)
0x04 (4) → High-speed中断,每1ms(2^(4-1)=8微帧)
0x00 (0) → 批量端点,NAK后立即重试
5.3 实际示例
示例1:键盘中断IN端点
十六进制:
07 05 81 03 08 00 0A
字段解析:
[0] 07 bLength = 7字节
[1] 05 bDescriptorType = ENDPOINT
[2] 81 bEndpointAddress = 0x81
D7=1 → IN方向(设备→主机)
D3:D0=1 → 端点1
[3] 03 bmAttributes = 0x03
D1:D0=11 → Interrupt(中断传输)
[4-5] 08 00 wMaxPacketSize = 0x0008 (8字节)
[6] 0A bInterval = 10 → 每10ms轮询
说明:
端点1 IN
中断传输
每次最多8字节(键盘报告)
每10ms主机询问一次按键状态
示例2:U盘批量OUT端点
十六进制:
07 05 02 02 00 02 00
字段解析:
[0] 07 bLength = 7字节
[1] 05 bDescriptorType = ENDPOINT
[2] 02 bEndpointAddress = 0x02
D7=0 → OUT方向(主机→设备)
D3:D0=2 → 端点2
[3] 02 bmAttributes = 0x02
D1:D0=10 → Bulk(批量传输)
[4-5] 00 02 wMaxPacketSize = 0x0200 (512字节)
[6] 00 bInterval = 0(批量端点,NAK后立即重试)
说明:
端点2 OUT
批量传输(写入U盘)
高速模式512字节包
NAK后立即重试
示例3:U盘批量IN端点
十六进制:
07 05 81 02 00 02 00
字段解析:
[0] 07 bLength = 7字节
[1] 05 bDescriptorType = ENDPOINT
[2] 81 bEndpointAddress = 0x81
D7=1 → IN方向(设备→主机)
D3:D0=1 → 端点1
[3] 02 bmAttributes = 0x02 (Bulk)
[4-5] 00 02 wMaxPacketSize = 512字节
[6] 00 bInterval = 0
说明:
端点1 IN
批量传输(从U盘读取)
配合端点2 OUT实现双向通信
示例4:音频等时OUT端点
十六进制:
09 05 03 05 00 04 01
字段解析:
[0] 09 bLength = 9字节(USB 2.0音频可能带扩展)
[1] 05 bDescriptorType = ENDPOINT
[2] 03 bEndpointAddress = 0x03 (OUT)
[3] 05 bmAttributes = 0x05
D1:D0=01 → Isochronous
D3:D2=01 → Asynchronous(异步)
[4-5] 00 04 wMaxPacketSize = 1024字节
[6] 01 bInterval = 1 → 每1ms传输
说明:
端点3 OUT
等时传输(音频流)
异步音频(有反馈端点调整采样率)
每1ms传输1024字节音频数据
六、字符串描述符(String Descriptor)
6.1 结构定义
用于提供人类可读的文本信息,如厂商名、产品名、序列号。
struct usb_string_descriptor {
uint8_t bLength; // 描述符长度 = 2 + 字符串长度×2
uint8_t bDescriptorType; // 类型 = 0x03 (STRING)
uint16_t wString[]; // Unicode字符串(UTF-16LE)
} __attribute__((packed));
6.2 特殊字符串0(语言ID列表)
字符串索引0是特殊的,返回设备支持的语言ID列表。
结构:
[0] bLength = 4 + (语言数量-1)×2
[1] bDescriptorType = 0x03
[2-3] LangID 1(例如:0x0409 = 英语-美国)
[4-5] LangID 2(例如:0x0804 = 简体中文)
...
常见语言ID(LANGID):
0x0409 = English (United States)
0x0809 = English (United Kingdom)
0x0804 = Chinese (PRC, 简体中文)
0x0404 = Chinese (Taiwan, 繁体中文)
0x0411 = Japanese
0x0407 = German
0x040C = French
0x0412 = Korean
0x0419 = Russian
示例:支持英文和中文
十六进制:
04 03 09 04
字段解析:
[0] 04 bLength = 4字节(2+1×2)
[1] 03 bDescriptorType = STRING
[2-3] 09 04 LangID = 0x0409(英语-美国)
说明:
只支持1种语言:英语
多语言示例:
十六进制:
06 03 09 04 04 08
字段解析:
[0] 06 bLength = 6字节(2+2×2)
[1] 03 bDescriptorType = STRING
[2-3] 09 04 LangID 1 = 0x0409(英语)
[4-5] 04 08 LangID 2 = 0x0804(简体中文)
说明:
支持2种语言
6.3 普通字符串描述符
字符串使用UTF-16LE编码(小端序Unicode)。
示例1:制造商字符串 “Acme”
请求:
GET_DESCRIPTOR(String, Index=1, LangID=0x0409)
SETUP包:80 06 03 01 09 04 FF 00
返回:
十六进制:
0A 03 41 00 63 00 6D 00 65 00
字段解析:
[0] 0A bLength = 10字节(2+4×2)
[1] 03 bDescriptorType = STRING
[2-3] 41 00 'A' (U+0041)
[4-5] 63 00 'c' (U+0063)
[6-7] 6D 00 'm' (U+006D)
[8-9] 65 00 'e' (U+0065)
说明:
UTF-16LE编码的 "Acme"
每个ASCII字符2字节
示例2:产品字符串 “USB键盘”(中文)
请求:
GET_DESCRIPTOR(String, Index=2, LangID=0x0804)
SETUP包:80 06 03 02 04 08 FF 00
返回:
十六进制:
0E 03 55 00 53 00 42 00 2E 95 58 94 2E 95
字段解析:
[0] 0E bLength = 14字节(2+6×2)
[1] 03 bDescriptorType = STRING
[2-3] 55 00 'U' (U+0055)
[4-5] 53 00 'S' (U+0053)
[6-7] 42 00 'B' (U+0042)
[8-9] 2E 95 '键' (U+952E)
[10-11] 58 94 '盘' (U+9458)
说明:
UTF-16LE编码
中文字符每个2字节
"USB键盘"
示例3:序列号 “SN123456”
十六进制:
12 03 53 00 4E 00 31 00 32 00 33 00 34 00 35 00 36 00
字段解析:
[0] 12 bLength = 18字节(2+8×2)
[1] 03 bDescriptorType = STRING
[2-3] 53 00 'S'
[4-5] 4E 00 'N'
[6-7] 31 00 '1'
[8-9] 32 00 '2'
[10-11] 33 00 '3'
[12-13] 34 00 '4'
[14-15] 35 00 '5'
[16-17] 36 00 '6'
说明:
序列号 "SN123456"
用于唯一标识设备
七、HID描述符(HID Descriptor)
7.1 结构定义
HID类特定描述符,定义HID设备的属性。位于接口描述符之后。
struct usb_hid_descriptor {
uint8_t bLength; // 描述符长度(≥9)
uint8_t bDescriptorType; // 类型 = 0x21 (HID)
uint16_t bcdHID; // HID版本号(BCD)
uint8_t bCountryCode; // 国家代码
uint8_t bNumDescriptors; // 类描述符数量
uint8_t bDescriptorType2; // 类描述符类型(0x22=Report)
uint16_t wDescriptorLength; // 报告描述符长度
} __attribute__((packed));
7.2 字段详解
[0] bLength
值:0x09 + 额外类描述符数×3
最小:0x09(只有1个报告描述符)
[1] bDescriptorType
值:0x21 (HID)
[2-3] bcdHID
HID规范版本号(BCD)
常见值:
0x0101 → HID 1.01
0x0111 → HID 1.11
[4] bCountryCode
国家代码(键盘布局)
值:
0x00 → 不支持国家代码
0x01 → Arabic
0x02 → Belgian
0x21 → US
0x22 → International (ISO)
大多数设备:
0x00(通用)
[5] bNumDescriptors
类描述符数量
通常:
0x01 → 只有报告描述符
[6] bDescriptorType2
类描述符类型
常见值:
0x22 → Report Descriptor(报告描述符)
0x23 → Physical Descriptor(物理描述符,罕见)
[7-8] wDescriptorLength
报告描述符的字节长度
示例:
0x003F → 63字节
0x0041 → 65字节
7.3 实际示例
示例:键盘HID描述符
十六进制:
09 21 11 01 00 01 22 3F 00
字段解析:
[0] 09 bLength = 9字节
[1] 21 bDescriptorType = HID
[2-3] 11 01 bcdHID = 0x0111 (HID 1.11)
[4] 00 bCountryCode = 0(通用)
[5] 01 bNumDescriptors = 1个
[6] 22 bDescriptorType2 = Report
[7-8] 3F 00 wDescriptorLength = 0x003F (63字节)
说明:
HID 1.11规范
有1个报告描述符,长度63字节
通用国家代码
八、完整描述符树示例
8.1 USB键盘完整描述符
设备描述符 (18字节)
│
├─ 配置描述符 (9字节)
│ │
│ ├─ 接口描述符 (9字节) - HID键盘接口
│ │ │
│ │ ├─ HID描述符 (9字节)
│ │ │
│ │ └─ 端点描述符 (7字节) - 端点1 IN (中断)
│
└─ 字符串描述符
├─ 字符串0: 语言列表
├─ 字符串1: 制造商 "Logitech"
└─ 字符串2: 产品 "USB Keyboard"
完整十六进制数据
设备描述符:
12 01 10 01 00 00 00 08 6D 04 77 C0 00 01 01 02 00 01
配置描述符树(34字节):
// 配置描述符 (9字节)
09 02 22 00 01 01 00 A0 32
// 接口描述符 (9字节)
09 04 00 00 01 03 01 01 00
// HID描述符 (9字节)
09 21 11 01 00 01 22 3F 00
// 端点描述符 (7字节)
07 05 81 03 08 00 0A
字符串描述符:
// 字符串0: 语言列表
04 03 09 04
// 字符串1: "Logitech"
12 03 4C 00 6F 00 67 00 69 00 74 00 65 00 63 00 68 00
// 字符串2: "USB Keyboard"
1A 03 55 00 53 00 42 00 20 00 4B 00 65 00 79 00 62 00 6F 00 61 00 72 00 64 00
8.2 U盘完整描述符
设备描述符 (18字节)
│
├─ 配置描述符 (9字节)
│ │
│ └─ 接口描述符 (9字节) - Mass Storage接口
│ │
│ ├─ 端点描述符 (7字节) - 端点1 IN (批量)
│ │
│ └─ 端点描述符 (7字节) - 端点2 OUT (批量)
│
└─ 字符串描述符
├─ 字符串0: 语言列表
├─ 字符串1: 制造商 "SanDisk"
├─ 字符串2: 产品 "Cruzer"
└─ 字符串3: 序列号 "012345678ABCDEF"
完整十六进制数据
设备描述符:
12 01 00 02 00 00 00 40 81 07 50 55 00 01 01 02 03 01
配置描述符树(32字节):
// 配置描述符 (9字节)
09 02 20 00 01 01 00 80 32
// 接口描述符 (9字节)
09 04 00 00 02 08 06 50 00
// 端点1 IN描述符 (7字节)
07 05 81 02 00 02 00
// 端点2 OUT描述符 (7字节)
07 05 02 02 00 02 00
8.3 USB-CDC虚拟串口完整描述符
设备描述符 (18字节)
│
├─ 配置描述符 (9字节)
│ │
│ ├─ 接口0: CDC控制接口 (9字节)
│ │ │
│ │ ├─ CDC功能描述符 (5字节) - Header
│ │ ├─ CDC功能描述符 (5字节) - Call Management
│ │ ├─ CDC功能描述符 (4字节) - ACM
│ │ ├─ CDC功能描述符 (5字节) - Union
│ │ │
│ │ └─ 端点描述符 (7字节) - 端点1 IN (中断)
│ │
│ └─ 接口1: CDC数据接口 (9字节)
│ │
│ ├─ 端点描述符 (7字节) - 端点2 IN (批量)
│ │
│ └─ 端点描述符 (7字节) - 端点3 OUT (批量)
│
└─ 字符串描述符
├─ 字符串0: 语言列表
├─ 字符串1: 制造商
└─ 字符串2: 产品
CDC功能描述符说明
CDC类使用类特定描述符来描述通信能力:
Header功能描述符(5字节):
05 24 00 10 01
[0] 05 - bLength
[1] 24 - bDescriptorType = CS_INTERFACE (0x24)
[2] 00 - bDescriptorSubtype = Header
[3-4] 10 01 - bcdCDC = 1.10
Call Management功能描述符(5字节):
05 24 01 00 01
[0] 05 - bLength
[1] 24 - CS_INTERFACE
[2] 01 - Call Management
[3] 00 - bmCapabilities (不支持呼叫管理)
[4] 01 - bDataInterface (数据接口号=1)
ACM功能描述符(4字节):
04 24 02 02
[0] 04 - bLength
[1] 24 - CS_INTERFACE
[2] 02 - ACM (Abstract Control Management)
[3] 02 - bmCapabilities (支持SetLineCoding等)
Union功能描述符(5字节):
05 24 06 00 01
[0] 05 - bLength
[1] 24 - CS_INTERFACE
[2] 06 - Union
[3] 00 - bMasterInterface (控制接口=0)
[4] 01 - bSlaveInterface0 (数据接口=1)
8.4 USB鼠标完整描述符
设备描述符 (18字节)
│
├─ 配置描述符 (9字节)
│ │
│ └─ 接口描述符 (9字节) - HID鼠标接口
│ │
│ ├─ HID描述符 (9字节)
│ │
│ └─ 端点描述符 (7字节) - 端点1 IN (中断)
│
└─ 字符串描述符
├─ 字符串0: 语言列表
└─ 字符串1: 产品 "USB Mouse"
鼠标报告描述符示例(简化)
HID报告描述符定义数据格式,这是一个简单的3键鼠标:
05 01 ; Usage Page (Generic Desktop)
09 02 ; Usage (Mouse)
A1 01 ; Collection (Application)
09 01 ; Usage (Pointer)
A1 00 ; Collection (Physical)
05 09 ; Usage Page (Button)
19 01 ; Usage Minimum (Button 1)
29 03 ; Usage Maximum (Button 3)
15 00 ; Logical Minimum (0)
25 01 ; Logical Maximum (1)
75 01 ; Report Size (1 bit)
95 03 ; Report Count (3 buttons)
81 02 ; Input (Data, Variable, Absolute)
75 05 ; Report Size (5 bits padding)
95 01 ; Report Count (1)
81 01 ; Input (Constant)
05 01 ; Usage Page (Generic Desktop)
09 30 ; Usage (X)
09 31 ; Usage (Y)
09 38 ; Usage (Wheel)
15 81 ; Logical Minimum (-127)
25 7F ; Logical Maximum (127)
75 08 ; Report Size (8 bits)
95 03 ; Report Count (3 axes)
81 06 ; Input (Data, Variable, Relative)
C0 ; End Collection
C0 ; End Collection
说明:
3个按键(1 bit × 3)
5 bit填充
X轴(8 bit,相对移动,-127到+127)
Y轴(8 bit,相对移动)
滚轮(8 bit,相对移动)
报告格式(4字节):
[0] = 按键状态 (bit0=左键, bit1=右键, bit2=中键)
[1] = X轴移动量 (有符号)
[2] = Y轴移动量 (有符号)
[3] = 滚轮移动量 (有符号)
九、描述符枚举流程
9.1 设备枚举完整时序
1. 设备插入,Hub检测连接
└─ Hub通过Idle电平识别速度(Full/Low-speed)
2. Hub报告端口状态变化给主机
└─ 主机读取Hub的端口状态
3. 主机等待100ms(设备电源稳定)
4. 主机发送Reset(SE0 ≥ 10ms)
└─ 设备复位到地址0,默认状态
5. 主机读取设备描述符(前8字节)
SETUP: 80 06 01 00 00 00 08 00
└─ 获取bMaxPacketSize0,确认端点0包大小
6. 主机再次Reset设备
7. 主机分配新地址(如地址3)
SETUP: 00 05 03 00 00 00 00 00
└─ 设备切换到地址3
8. 主机读取完整设备描述符(18字节)
SETUP: 80 06 01 00 00 00 12 00
└─ 获取VID/PID, bNumConfigurations等
9. 主机读取配置描述符头(9字节)
SETUP: 80 06 02 00 00 00 09 00
└─ 获取wTotalLength
10. 主机读取完整配置描述符树
SETUP: 80 06 02 00 00 00 XX XX
└─ 根据wTotalLength读取所有接口、端点描述符
11. 主机读取字符串描述符(按需)
SETUP: 80 06 03 00 00 00 FF 00 (字符串0,语言列表)
SETUP: 80 06 03 01 09 04 FF 00 (字符串1,英文)
SETUP: 80 06 03 02 09 04 FF 00 (字符串2,英文)
12. 主机激活配置
SETUP: 00 09 01 00 00 00 00 00
└─ 设备进入Configured状态
13. HID设备:读取HID报告描述符
SETUP: 81 06 22 00 00 00 XX XX
└─ 获取报告格式
14. 加载设备驱动,设备就绪
9.2 Linux下查看描述符命令
# 列出所有USB设备
lsusb
# 详细显示某个设备的描述符
lsusb -v -d 046d:c077
# 显示所有设备的描述符树
lsusb -v
# 查看原始描述符数据
cat /sys/bus/usb/devices/1-1/descriptors | hexdump -C
# 使用usbhid-dump查看HID报告描述符
sudo usbhid-dump -d 046d:c077
9.3 Windows下查看描述符工具
USB Device Tree Viewer
- 图形化显示所有USB设备的描述符树
- 显示原始十六进制数据
USBDeview
- 列出所有USB设备
- 查看VID/PID、驱动信息
Wireshark + USBPcap
- 抓取USB通信包
- 查看SETUP事务和描述符返回
十、描述符设计建议
10.1 VID/PID选择
官方途径:
- 向USB-IF申请VID(约$6000/年)
- 可分配65535个PID
测试途径:
- 使用测试VID(需遵守测试限制)
- 借用开源VID(如OpenMoko 0x1D50)
- 使用某些芯片厂商提供的VID(如FTDI)
注意:
- 商业产品必须使用合法VID
- 相同VID+PID的设备共享驱动
- 修改产品功能时应更新PID
10.2 字符串描述符建议
必需字符串:
- 制造商(iManufacturer)
- 产品名(iProduct)
可选但推荐:
- 序列号(iSerialNumber)
- 支持多个相同设备同时使用
- Android ADB、MTP等要求必须有
多语言支持:
- 字符串0列出所有支持的语言
- 每个字符串需为每种语言准备版本
10.3 端点分配建议
端点0:
- 保留给控制传输
- 自动处理,无需描述符
端点1-15:
- IN和OUT独立编号
- 0x81和0x01是两个不同端点
推荐分配:
HID键盘:
端点1 IN (中断) - 按键报告
HID鼠标:
端点1 IN (中断) - 位置报告
Mass Storage:
端点1 IN (批量) - 读取数据
端点2 OUT (批量) - 写入数据
CDC串口:
端点1 IN (中断) - 控制接口状态通知
端点2 IN (批量) - 接收数据
端点3 OUT (批量) - 发送数据
10.4 功耗设置
bMaxPower计算:
单位:2mA
示例:
100mA → bMaxPower = 50 (0x32)
500mA → bMaxPower = 250 (0xFA)
设计建议:
- 尽量降低功耗(< 100mA)
- 高功耗设备考虑自供电(外部电源)
- USB 2.0最大500mA,USB 3.0最大900mA
- 超过限制必须使用bmAttributes.D6=1(自供电)
10.5 类代码选择
优先使用标准类:
- 兼容性好,系统自带驱动
- HID (0x03) – 键盘鼠标手柄
- Mass Storage (0x08) – U盘
- CDC (0x02) – 虚拟串口
复合设备:
- bDeviceClass = 0x00
- 每个接口定义自己的类
厂商自定义:
- bDeviceClass = 0xFF
- 需要自己提供驱动程序
十一、补充内容:USB 3.0与高级主题
11.1 USB 3.0新增描述符
USB 3.0(SuperSpeed)引入了新的描述符类型以支持5 Gbps传输速率。
BOS描述符(Binary Device Object Store)
BOS描述符是一个容器描述符,包含设备级能力信息。
结构(5字节头部):
偏移 字段 大小 值 说明
────────────────────────────────────────────────
0 bLength 1 0x05 头部长度5字节
1 bDescriptorType 1 0x0F BOS类型
2-3 wTotalLength 2 变长 包含所有能力描述符
4 bNumDeviceCaps 1 变长 能力描述符数量
示例:
05 0F 16 00 02 ← BOS头部
↑ ↑↑ ↑
│ │ └─ 2个能力描述符
│ └─ 总长度0x0016(22字节)
└─ BOS类型
后续跟随的能力描述符:
- USB 2.0 Extension (7字节)
- SuperSpeed USB (10字节)
- Container ID (20字节)
- Platform (可变)
USB 2.0扩展能力描述符
说明设备是否支持LPM(Link Power Management)。
结构(7字节):
偏移 字段 大小 值 说明
────────────────────────────────────────────────
0 bLength 1 0x07 固定7字节
1 bDescriptorType 1 0x10 DEVICE_CAPABILITY
2 bDevCapabilityType 1 0x02 USB 2.0 Extension
3-6 bmAttributes 4 位掩码 能力位
bmAttributes位定义:
D1 = LPM支持(1=支持链路电源管理)
D2 = BESL和备用HIRD支持
D3-D31 = 保留
示例:
07 10 02 06 00 00 00
↑ ↑ ↑
│ │ └─ 支持LPM (bit1=1, bit2=1)
│ └─ USB 2.0扩展能力
└─ DEVICE_CAPABILITY类型
SuperSpeed USB能力描述符
说明设备的SuperSpeed能力。
结构(10字节):
偏移 字段 大小 值 说明
────────────────────────────────────────────────
0 bLength 1 0x0A 固定10字节
1 bDescriptorType 1 0x10 DEVICE_CAPABILITY
2 bDevCapabilityType 1 0x03 SuperSpeed USB
3 bmAttributes 1 位掩码 能力位
4-5 wSpeedsSupported 2 位掩码 支持的速度
6 bFunctionalitySupport 1 索引 最低功能速度
7 bU1DevExitLat 1 微秒 U1退出延迟
8-9 wU2DevExitLat 2 微秒 U2退出延迟
bmAttributes位定义:
D1 = 支持LTM(延迟容忍消息)
wSpeedsSupported位定义:
D0 = 低速(1.5 Mbps)
D1 = 全速(12 Mbps)
D2 = 高速(480 Mbps)
D3 = SuperSpeed(5 Gbps)
示例:
0A 10 03 00 0E 00 01 0A 00 01
↑ ↑ ↑ ↑↑ ↑ ↑ ↑↑
│ │ │ │ │ │ └─ U2退出延迟=256μs
│ │ │ │ │ └─ U1退出延迟=10μs
│ │ │ │ └─ 最低需要全速(1)
│ │ │ └─ 支持全速、高速、SuperSpeed(0x0E)
│ │ └─ 无LTM支持
│ └─ SuperSpeed能力
└─ DEVICE_CAPABILITY类型
SuperSpeed端点伴随描述符
SuperSpeed端点必须在标准端点描述符后紧跟该描述符。
结构(6字节):
偏移 字段 大小 值 说明
────────────────────────────────────────────────
0 bLength 1 0x06 固定6字节
1 bDescriptorType 1 0x30 SS_ENDPOINT_COMPANION
2 bMaxBurst 1 0-15 每次传输最大包数-1
3 bmAttributes 1 位掩码 批量流、等时额外参数
4-5 wBytesPerInterval 2 字节数 等时/中断每间隔字节数
bMaxBurst说明:
批量/中断传输:0=1024字节, 15=16×1024字节
等时传输:每微帧最大包数-1
bmAttributes(批量端点,D4:D0):
0 = 不支持流
1-16 = 支持的最大流数
bmAttributes(等时端点,D1:D0):
0 = 无额外乘数
1 = 每间隔2个包
2 = 每间隔3个包
示例1:批量端点
06 30 0F 00 00 00
↑ ↑ ↑
│ │ └─ 最大16个包(15+1)
│ └─ 无流支持
└─ SS伴随描述符
示例2:等时端点
06 30 02 01 C0 00
↑ ↑ ↑ ↑↑
│ │ │ └─ 每间隔192字节
│ │ └─ 2个包乘数
│ └─ 最大3个包(2+1)
└─ SS伴随描述符
在配置描述符树中的位置:
接口描述符
├─ 端点描述符(7字节)
│ └─ SS端点伴随描述符(6字节) ← 紧跟!
└─ 端点描述符(7字节)
└─ SS端点伴随描述符(6字节)
11.2 描述符验证与常见错误
描述符一致性检查清单
| 检查项 | 说明 | 常见错误 |
|---|---|---|
| bLength一致性 | 实际长度必须等于bLength | bLength=9但实际7字节 |
| wTotalLength准确性 | 配置树总字节数必须匹配 | 多算或少算类特定描述符 |
| 端点地址唯一性 | 同配置中不能重复(除了EP0) | 两个端点都是0x81 |
| 接口号连续性 | 接口编号必须从0开始连续 | 接口0、接口2(跳过1) |
| 电源需求合法性 | bMaxPower不能超过总线能力 | 自供电设备写250(500mA) |
| 端点方向合法性 | 控制端点必须双向 | EP0设置为IN-only |
| 字符串索引有效性 | 非0索引必须对应实际字符串 | iProduct=5但只有3个字符串 |
| 类代码匹配 | 接口类代码必须与描述符匹配 | bInterfaceClass=0x03(HID)但无HID描述符 |
| BCD格式正确 | 版本号必须用BCD | bcdUSB=0x0210写成0x020A |
| 小端序正确 | 多字节字段必须小端序 | wMaxPacketSize=64写成0x4000而非0x0040 |
Windows枚举失败常见原因
| 错误现象 | 根本原因 | 解决方案 |
|---|---|---|
| 设备管理器黄色感叹号 | 描述符结构错误 | 用USB分析仪检查描述符 |
| Code 10: 设备无法启动 | 端点配置冲突 | 检查端点地址/方向唯一性 |
| Code 28: 驱动程序未安装 | VID/PID无匹配INF | 提供正确的INF文件 |
| Code 43: USB设备无法识别 | 功耗超标 | 降低bMaxPower或改自供电 |
| 枚举卡在GET_DESCRIPTOR(Device) | EP0 wMaxPacketSize错误 | LS=8, FS=8/16/32/64, HS=64 |
| SET_CONFIGURATION后Stall | 端点资源不足 | 减少端点数或降低wMaxPacketSize |
| 字符串显示乱码 | 未用UTF-16LE | 手动转换或用L”string” |
| HID设备无法识别 | 报告描述符长度错误 | HID描述符中wDescriptorLength必须正确 |
11.3 推荐的描述符设计实践
设备描述符设计
// 推荐:使用官方VID/PID
const uint8_t device_desc[] = {
0x12, // bLength
0x01, // bDescriptorType
0x00, 0x02, // bcdUSB = 2.00 (USB 2.0)
0x00, // bDeviceClass (接口定义类)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 = 64 (全速推荐)
0x83, 0x04, // idVendor = 0x0483 (ST Microelectronics示例)
0x40, 0x57, // idProduct = 0x5740 (自定义)
0x00, 0x02, // bcdDevice = 2.00 (设备版本)
0x01, // iManufacturer (字符串1)
0x02, // iProduct (字符串2)
0x03, // iSerialNumber (字符串3,推荐唯一SN)
0x01 // bNumConfigurations = 1
};
// 不推荐:盗用他人VID/PID(违法!)
// 不推荐:使用测试VID而不标注(0x0000/0xFFFF)
配置描述符设计
// 推荐:适度功耗声明
0x32, // bMaxPower = 100mA (0x32 × 2mA)
// 不推荐:虚报低功耗(实际耗电更多会导致总线过载)
0x01, // bMaxPower = 2mA(但实际用100mA!)
// 不推荐:虚报高功耗(Windows可能拒绝枚举)
0xFA, // bMaxPower = 500mA(实际只用50mA)
字符串描述符设计
// 推荐:提供有意义的字符串
wchar_t manufacturer[] = L"Acme Corporation";
wchar_t product[] = L"USB Data Logger Pro";
wchar_t serial[] = L"SN20260804-001"; // 每台设备唯一!
// 推荐:语言ID支持
const uint8_t string0[] = {
0x04, // bLength
0x03, // bDescriptorType
0x09, 0x04, // wLANGID[0] = 0x0409 (英语-美国)
};
// 不推荐:空字符串或无意义字符串
wchar_t product[] = L"Device"; // 太通用!
wchar_t serial[] = L"123456"; // 所有设备都一样!
端点分配设计
// 推荐:合理分配端点
// EP0: 控制(双向, 隐式)
// EP1 IN: 批量输入(数据上传)
// EP2 OUT: 批量输出(数据下发)
// EP3 IN: 中断输入(状态通知)
// 不推荐:浪费端点号
// EP0: 控制
// EP5 IN: 批量输入(跳过1-4)
// EP10 OUT: 批量输出(跳过6-9)
// 某些微控制器端点资源有限!
11.4 USB描述符调试工具
| 工具 | 平台 | 用途 | 获取方式 |
|---|---|---|---|
| USBlyzer | Windows | 捕获USB通信,解析描述符 | usblyzer.com |
| Wireshark + USBPcap | Windows | 开源抓包工具 | wireshark.org |
| Bus Hound | Windows | USB/PCI总线分析 | perisoft.net |
| lsusb -v | Linux | 显示设备描述符树 | 系统自带 |
| USB Descriptor Tool | Windows | 微软官方描述符解析器 | Windows SDK |
| Total Phase Beagle | 硬件 | 专业USB协议分析仪 | totalphase.com |
| Ellisys | 硬件 | 高端USB 3.x分析仪 | ellisys.com |
Linux调试示例:
# 查看所有USB设备
lsusb
# 查看详细描述符
lsusb -v -d 0483:5740
# 查看原始描述符十六进制
lsusb -v -d 0483:5740 | grep "bDescriptor"
# 导出描述符到文件
sudo cat /sys/kernel/debug/usb/devices > usb_devices.txt
十二、常见设备类型总结
| 设备类型 | Class | SubClass | Protocol | 描述符特点 |
|---|---|---|---|---|
| 键盘 | 0x03 (HID) | 0x01 (Boot) | 0x01 (Keyboard) | 1接口, 1中断IN, HID报告 |
| 鼠标 | 0x03 (HID) | 0x01 (Boot) | 0x02 (Mouse) | 1接口, 1中断IN, HID报告 |
| U盘 | 0x08 (MSC) | 0x06 (SCSI) | 0x50 (Bulk-Only) | 1接口, 2批量端点 |
| 虚拟串口 | 0x02 (CDC) | 0x02 (ACM) | 0x01 (AT Commands) | 2接口, CDC功能描述符 |
| 音频 | 0x01 (Audio) | – | – | 多接口, 等时端点, 类特定描述符 |
| 摄像头 | 0x0E (Video) | – | – | 多接口, 等时端点, UVC描述符 |
| 打印机 | 0x07 (Printer) | 0x01 | 0x02 (Bidirectional) | 1接口, 2批量端点 |
| Hub | 0x09 (Hub) | 0x00 | – | Hub描述符(特殊) |
十三、USB视频类(UVC)详解
13.1 UVC概述
UVC(USB Video Class)是USB-IF定义的视频设备标准,包括:
- 摄像头(Webcam)
- 视频采集卡
- 数字摄像机
优势:
- 免驱动(Windows/Linux/macOS内置支持)
- 标准化接口
- 支持多种格式(MJPEG、YUV、H.264)
类代码:
bInterfaceClass = 0x0E (Video)
bInterfaceSubClass:
0x01 = VideoControl (视频控制)
0x02 = VideoStreaming (视频流)
13.2 UVC描述符层次结构
设备描述符 (18字节)
│
└─ 配置描述符 (9字节)
│
├─ 视频控制接口 (接口0, bInterfaceSubClass=0x01)
│ ├─ VC接口头描述符 (CS_INTERFACE, 12+n字节)
│ ├─ 输入终端描述符 (CS_INTERFACE, 17/18字节)
│ ├─ 输出终端描述符 (CS_INTERFACE, 9字节)
│ ├─ 处理单元描述符 (CS_INTERFACE, 10+n字节)
│ └─ 中断端点描述符 (7字节) - 可选
│
└─ 视频流接口 (接口1, bInterfaceSubClass=0x02)
├─ 替代设置0 (无端点, 0带宽)
└─ 替代设置1+ (有等时端点, 实际视频流)
├─ VS接口输入头描述符 (CS_INTERFACE, 13+n字节)
├─ 格式描述符 (CS_INTERFACE, 可变)
│ ├─ MJPEG格式 (11字节)
│ ├─ 非压缩格式YUY2 (27字节)
│ └─ 帧格式H.264 (可变)
├─ 帧描述符 (CS_INTERFACE, 26+n×4字节)
│ ├─ 640×480 @ 30fps
│ ├─ 1280×720 @ 30fps
│ └─ 1920×1080 @ 30fps
├─ 颜色匹配描述符 (CS_INTERFACE, 6字节)
└─ 等时端点描述符 (7字节)
13.3 视频控制接口描述符
VC接口头描述符(Class-Specific VC Interface Header)
结构(12+n字节):
偏移 字段 大小 说明
────────────────────────────────────────────────
0 bLength 1 12+n字节
1 bDescriptorType 1 0x24 (CS_INTERFACE)
2 bDescriptorSubtype 1 0x01 (VC_HEADER)
3-4 bcdUVC 2 UVC版本 (BCD)
5-6 wTotalLength 2 所有VC描述符总长
7-10 dwClockFrequency 4 设备时钟频率(Hz)
11 bInCollection 1 视频流接口数
12.. baInterfaceNr[] n 视频流接口编号数组
示例(UVC 1.1,1个流接口):
0D 24 01 10 01 4D 00 00 6C DC 02 01 01
↑ ↑ ↑ ↑↑ ↑↑ ↑↑↑↑↑↑ ↑ ↑
│ │ │ │ │ │ │ └─ 流接口1
│ │ │ │ │ │ └─ 1个流接口
│ │ │ │ │ └─ 48MHz (0x02DC6C00)
│ │ │ │ └─ 总长77字节 (0x004D)
│ │ │ └─ UVC 1.1 (0x0110)
│ │ └─ VC_HEADER
│ └─ CS_INTERFACE
└─ 13字节
输入终端描述符(Camera Terminal)
结构(17或18字节):
偏移 字段 大小 说明
────────────────────────────────────────────────
0 bLength 1 17或18
1 bDescriptorType 1 0x24 (CS_INTERFACE)
2 bDescriptorSubtype 1 0x02 (VC_INPUT_TERMINAL)
3 bTerminalID 1 终端ID
4-5 wTerminalType 2 0x0201 (Camera)
6 bAssocTerminal 1 关联输出终端ID
7 iTerminal 1 字符串索引
8-9 wObjectiveFocalLengthMin 2 最小焦距(mm)
10-11 wObjectiveFocalLengthMax 2 最大焦距(mm)
12-13 wOcularFocalLength 2 目镜焦距(mm)
14 bControlSize 1 控制字节数(n)
15.. bmControls[] n 控制能力位
bmControls位定义(每个能力2位):
D0-D1 = 扫描模式 (0=不支持, 1=只读, 2=读写)
D2-D3 = 自动曝光模式
D4-D5 = 自动曝光优先
D6-D7 = 曝光时间(绝对)
D8-D9 = 曝光时间(相对)
D10-D11 = 焦距(绝对)
D12-D13 = 焦距(相对)
D14-D15 = 光圈(绝对)
D16-D17 = 光圈(相对)
D18-D19 = 缩放(绝对)
D20-D21 = 缩放(相对)
D22-D23 = 平移(绝对)
...
示例(3字节控制):
12 24 02 01 01 02 00 00 00 00 00 00 00 00 03 00 00 00
↑ ↑ ↑ ↑ ↑↑ ↑ ↑ ... ↑ ↑↑↑↑
│ │ │ │ │ │ │ │ └─ 支持自动曝光、焦距
│ │ │ │ │ │ └─ 无关联终端 └─ 3字节控制
│ │ │ │ │ └─ 字符串0
│ │ │ │ └─ Camera (0x0201)
│ │ │ └─ 终端ID=1
│ │ └─ INPUT_TERMINAL
│ └─ CS_INTERFACE
└─ 18字节
处理单元描述符(Processing Unit)
结构(10+n字节):
偏移 字段 大小 说明
────────────────────────────────────────────────
0 bLength 1 10+n
1 bDescriptorType 1 0x24 (CS_INTERFACE)
2 bDescriptorSubtype 1 0x05 (VC_PROCESSING_UNIT)
3 bUnitID 1 单元ID
4 bSourceID 1 源ID(输入终端)
5-6 wMaxMultiplier 2 数字缩放最大倍数×100
7 bControlSize 1 控制字节数(n)
8.. bmControls[] n 控制能力位
n+8 iProcessing 1 字符串索引
n+9 bmVideoStandards 1 视频标准(NTSC/PAL等)
bmControls位定义:
D0 = 亮度
D1 = 对比度
D2 = 色调
D3 = 饱和度
D4 = 锐度
D5 = 伽马
D6 = 白平衡温度
D7 = 白平衡成分
D8 = 背光补偿
D9 = 增益
D10 = 电源线频率 (50Hz/60Hz)
D11 = 色调自动
D12 = 白平衡温度自动
D13 = 白平衡成分自动
D14 = 数字缩放
...
示例(3字节控制):
0C 24 05 02 01 00 00 03 7F 16 00 00
↑ ↑ ↑ ↑ ↑ ↑↑ ↑ ↑↑↑↑ ↑ ↑
│ │ │ │ │ │ │ │ │ └─ 无标准限制
│ │ │ │ │ │ │ │ └─ 字符串0
│ │ │ │ │ │ │ └─ 支持亮度、对比度、色调等
│ │ │ │ │ │ └─ 3字节控制
│ │ │ │ │ └─ 无数字缩放
│ │ │ │ └─ 源ID=1(Camera终端)
│ │ │ └─ 单元ID=2
│ │ └─ PROCESSING_UNIT
│ └─ CS_INTERFACE
└─ 12字节
输出终端描述符(Output Terminal)
结构(9字节):
偏移 字段 大小 说明
────────────────────────────────────────────────
0 bLength 1 0x09
1 bDescriptorType 1 0x24 (CS_INTERFACE)
2 bDescriptorSubtype 1 0x03 (VC_OUTPUT_TERMINAL)
3 bTerminalID 1 终端ID
4-5 wTerminalType 2 0x0101 (USB流)
6 bAssocTerminal 1 关联输入终端ID
7 bSourceID 1 源ID(处理单元)
8 iTerminal 1 字符串索引
示例:
09 24 03 03 01 01 00 02 00
↑ ↑ ↑ ↑ ↑↑ ↑ ↑ ↑
│ │ │ │ │ │ │ └─ 字符串0
│ │ │ │ │ │ └─ 源ID=2(处理单元)
│ │ │ │ │ └─ 无关联终端
│ │ │ │ └─ USB流 (0x0101)
│ │ │ └─ 终端ID=3
│ │ └─ OUTPUT_TERMINAL
│ └─ CS_INTERFACE
└─ 9字节
13.4 视频流接口描述符
VS接口输入头描述符(Class-Specific VS Interface Input Header)
结构(13+p×n字节):
偏移 字段 大小 说明
────────────────────────────────────────────────
0 bLength 1 13+p×n
1 bDescriptorType 1 0x24 (CS_INTERFACE)
2 bDescriptorSubtype 1 0x01 (VS_INPUT_HEADER)
3 bNumFormats 1 格式描述符数量
4-5 wTotalLength 2 所有VS描述符总长
6 bEndpointAddress 1 等时端点地址
7 bmInfo 1 能力位
8 bTerminalLink 1 关联的输出终端ID
9 bStillCaptureMethod 1 静态图像捕获方法
10 bTriggerSupport 1 触发器支持
11 bTriggerUsage 1 触发器用途
12 bControlSize 1 每格式控制字节数(n)
13.. bmaControls[] p×n 每格式控制能力
bmaControls位定义(每格式):
D0 = 关键帧速率
D1 = P帧速率
D2 = 压缩质量
D3 = 压缩窗口大小
示例(2个格式):
0D 24 01 02 58 00 81 00 03 00 00 00 01 00 00
↑ ↑ ↑ ↑ ↑↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑↑
│ │ │ │ │ │ │ │ │ │ │ │ └─ 格式1、2控制
│ │ │ │ │ │ │ │ │ │ │ └─ 1字节/格式
│ │ │ │ │ │ │ │ │ │ └─ 触发器用途
│ │ │ │ │ │ │ │ │ └─ 无触发器
│ │ │ │ │ │ │ │ └─ 静态捕获方法0
│ │ │ │ │ │ │ └─ 终端ID=3
│ │ │ │ │ │ └─ 能力位
│ │ │ │ │ └─ 端点0x81
│ │ │ │ └─ 总长88字节
│ │ │ └─ 2个格式
│ │ └─ VS_INPUT_HEADER
│ └─ CS_INTERFACE
└─ 15字节 (13+2×1)
MJPEG格式描述符
结构(11字节):
偏移 字段 大小 说明
────────────────────────────────────────────────
0 bLength 1 0x0B
1 bDescriptorType 1 0x24 (CS_INTERFACE)
2 bDescriptorSubtype 1 0x06 (VS_FORMAT_MJPEG)
3 bFormatIndex 1 格式索引(1开始)
4 bNumFrameDescriptors 1 帧描述符数量
5 bmFlags 1 特性标志
6 bDefaultFrameIndex 1 默认帧索引
7 bAspectRatioX 1 宽高比X
8 bAspectRatioY 1 宽高比Y
9 bmInterlaceFlags 1 隔行扫描标志
10 bCopyProtect 1 复制保护
bmFlags:
D0 = 固定大小样本 (MJPEG通常为0)
D1-D7 = 保留
示例(3个帧,16:9):
0B 24 06 01 03 01 01 10 09 00 00
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
│ │ │ │ │ │ │ │ │ │ └─ 无复制保护
│ │ │ │ │ │ │ │ │ └─ 无隔行扫描
│ │ │ │ │ │ │ │ └─ Y=9
│ │ │ │ │ │ │ └─ X=16
│ │ │ │ │ │ └─ 默认帧1
│ │ │ │ │ └─ 可变大小
│ │ │ │ └─ 3个帧描述符
│ │ │ └─ 格式索引1
│ │ └─ FORMAT_MJPEG
│ └─ CS_INTERFACE
└─ 11字节
非压缩格式描述符(YUY2/NV12)
结构(27字节):
偏移 字段 大小 说明
────────────────────────────────────────────────
0 bLength 1 0x1B (27字节)
1 bDescriptorType 1 0x24 (CS_INTERFACE)
2 bDescriptorSubtype 1 0x04 (VS_FORMAT_UNCOMPRESSED)
3 bFormatIndex 1 格式索引
4 bNumFrameDescriptors 1 帧描述符数量
5-20 guidFormat 16 格式GUID
21 bBitsPerPixel 1 每像素位数
22 bDefaultFrameIndex 1 默认帧索引
23 bAspectRatioX 1 宽高比X
24 bAspectRatioY 1 宽高比Y
25 bmInterlaceFlags 1 隔行扫描标志
26 bCopyProtect 1 复制保护
常见GUID:
YUY2: 59 55 59 32 00 00 10 00 80 00 00 AA 00 38 9B 71
NV12: 4E 56 31 32 00 00 10 00 80 00 00 AA 00 38 9B 71
UYVY: 55 59 56 59 00 00 10 00 80 00 00 AA 00 38 9B 71
示例(YUY2,2个帧):
1B 24 04 02 02 59 55 59 32 00 00 10 00 80 00 00 AA 00 38 9B 71 10 01 10 09 00 00
↑ ↑ ↑ ↑ ↑ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ↑ ↑ ↑ ↑ ↑ ↑
│ │ │ │ │ │ │ │ │ │ │ └─ 无保护
│ │ │ │ │ │ │ │ │ │ └─ 无隔行
│ │ │ │ │ │ │ │ │ └─ 9
│ │ │ │ │ │ │ │ └─ 16
│ │ │ │ │ │ │ └─ 默认帧1
│ │ │ │ │ │ └─ 16bpp
│ │ │ │ │ └─ YUY2 GUID
│ │ │ │ └─ 2个帧
│ │ │ └─ 格式索引2
│ │ └─ FORMAT_UNCOMPRESSED
│ └─ CS_INTERFACE
└─ 27字节
帧描述符(Frame Descriptor)
结构(基本26字节 + 离散帧率n×4字节):
偏移 字段 大小 说明
────────────────────────────────────────────────
0 bLength 1 26 或 26+n×4
1 bDescriptorType 1 0x24 (CS_INTERFACE)
2 bDescriptorSubtype 1 0x05或0x07 (MJPEG/非压缩)
3 bFrameIndex 1 帧索引
4 bmCapabilities 1 能力位
5-6 wWidth 2 宽度(像素)
7-8 wHeight 2 高度(像素)
9-12 dwMinBitRate 4 最小比特率(bps)
13-16 dwMaxBitRate 4 最大比特率(bps)
17-20 dwMaxVideoFrameBufferSize 4 最大帧缓冲区(字节)
21-24 dwDefaultFrameInterval 4 默认帧间隔(100ns)
25 bFrameIntervalType 1 0=连续, n=离散帧率数
26.. dwFrameInterval[] n×4 离散帧间隔列表
帧间隔计算:
fps = 10,000,000 / dwFrameInterval
30fps = 10,000,000 / 333,333 = 0x00051615
15fps = 10,000,000 / 666,666 = 0x000A2C2A
10fps = 10,000,000 / 1,000,000 = 0x000F4240
示例(640×480 @ 30/15fps,MJPEG):
22 24 07 01 00 80 02 E0 01 00 00 CA 08 00 00 CA 08
00 60 09 00 15 16 05 00 02 15 16 05 00 2A 2C 0A 00
↑ ↑ ↑ ↑ ↑ ↑↑ ↑↑ ... ↑ ↑↑↑↑↑↑ ↑↑↑↑↑↑
│ │ │ │ │ │ │ │ │ └─ 666666(15fps)
│ │ │ │ │ │ │ │ └─ 333333(30fps)
│ │ │ │ │ │ │ └─ 2个离散帧率
│ │ │ │ │ │ └─ 高度480
│ │ │ │ │ └─ 宽度640
│ │ │ │ └─ 能力
│ │ │ └─ 帧索引1
│ │ └─ FRAME_MJPEG
│ └─ CS_INTERFACE
└─ 34字节 (26+2×4)
示例(1920×1080 @ 30fps,MJPEG):
1E 24 07 02 00 80 07 38 04 00 80 32 02 00 80 32 02
00 58 02 00 15 16 05 00 01 15 16 05 00
↑ ↑ ↑ ↑ ↑ ↑↑ ↑↑ ...
│ │ │ │ │ │ └─ 高度1080 (0x0438)
│ │ │ │ │ └─ 宽度1920 (0x0780)
│ │ │ │ └─ 能力
│ │ │ └─ 帧索引2
│ │ └─ FRAME_MJPEG
│ └─ CS_INTERFACE
└─ 30字节
颜色匹配描述符(Color Matching Descriptor)
结构(6字节):
偏移 字段 大小 说明
────────────────────────────────────────────────
0 bLength 1 0x06
1 bDescriptorType 1 0x24 (CS_INTERFACE)
2 bDescriptorSubtype 1 0x0D (VS_COLORFORMAT)
3 bColorPrimaries 1 色域
4 bTransferCharacteristics 1 传输特性
5 bMatrixCoefficients 1 矩阵系数
bColorPrimaries:
0x01 = BT.709 (HDTV)
0x02 = BT.470-2 (M) NTSC
0x03 = BT.470-2 (B, G) PAL
0x04 = SMPTE 170M
0x05 = SMPTE 240M
bTransferCharacteristics:
0x01 = BT.709
0x02 = BT.470-2 M
0x03 = BT.470-2 B, G
0x04 = SMPTE 170M
0x05 = SMPTE 240M
0x06 = Linear (gamma=1.0)
0x07 = sRGB
bMatrixCoefficients:
0x00 = 不使用
0x01 = BT.709
0x02 = FCC
0x04 = BT.601
0x05 = SMPTE 240M
示例(BT.709 HDTV):
06 24 0D 01 01 04 00
↑ ↑ ↑ ↑ ↑ ↑ ↑
│ │ │ │ │ │ └─ 不使用矩阵
│ │ │ │ │ └─ BT.601矩阵
│ │ │ │ └─ BT.709传输
│ │ │ └─ BT.709色域
│ │ └─ COLORFORMAT
│ └─ CS_INTERFACE
└─ 6字节
13.5 完整UVC摄像头描述符树示例
简化的640×480 MJPEG摄像头
完整配置描述符树大小:约150字节
设备描述符 (18字节):
12 01 00 02 EF 02 01 40 5E 04 58 08 00 01 01 02 03 01
↑↑ ↑↑ ↑↑ ↑↑ ↑ ↑↑↑↑ ↑↑↑↑
USB2.0 杂类 64 VID PID
配置描述符 (9字节):
09 02 9E 00 02 01 00 80 FA
↑↑ ↑ ↑ ↑
158 2接口 500mA
接口0 - 视频控制 (9字节):
09 04 00 00 01 0E 01 00 00
↑ ↑ ↑↑ ↑↑
接口0 1端 Video VC
VC接口头 (13字节):
0D 24 01 10 01 4D 00 00 6C DC 02 01 01
↑ ↑↑ ↑↑ ↑↑↑↑↑↑ ↑ ↑
头 1.1 77 48MHz 1 接口1
输入终端-Camera (18字节):
12 24 02 01 01 02 00 00 00 00 00 00 00 00 03 00 00 00
↑ ↑ ↑↑
IT ID1 Camera
处理单元 (12字节):
0C 24 05 02 01 00 00 03 7F 16 00 00
↑ ↑ ↑ ↑↑↑↑
PU ID2 源1 控制位
输出终端-USB流 (9字节):
09 24 03 03 01 01 00 02 00
↑ ↑ ↑↑ ↑
OT ID3 USB流 源2
中断端点 (7字节):
07 05 83 03 10 00 06
↑ ↑ ↑ ↑
EP3 中断 16B 6ms
接口1 - 视频流,替代设置0 (9字节):
09 04 01 00 00 0E 02 00 00
↑ ↑ ↑ ↑↑ ↑↑
接口1 无端 Video VS
接口1 - 视频流,替代设置1 (9字节):
09 04 01 01 01 0E 02 00 00
↑ ↑ ↑ ↑↑ ↑↑
接口1 1端 Video VS
VS输入头 (15字节):
0D 24 01 01 58 00 81 00 03 00 00 00 01 00
↑ ↑ ↑↑ ↑ ↑ ↑
头 1格 88 EP1 终端3 1字节/格
MJPEG格式 (11字节):
0B 24 06 01 02 01 01 10 09 00 00
↑ ↑ ↑ ↑ ↑ ↑
格式1 2帧 默认1 16:9
帧1: 640×480 @ 30fps (30字节):
1E 24 07 01 00 80 02 E0 01 00 00 CA 08 00 00 CA 08
00 60 09 00 15 16 05 00 01 15 16 05 00
帧2: 320×240 @ 30fps (30字节):
1E 24 07 02 00 40 01 F0 00 00 00 65 04 00 00 65 04
00 58 02 00 15 16 05 00 01 15 16 05 00
颜色匹配 (6字节):
06 24 0D 01 01 04 00
等时端点 (7字节):
07 05 81 05 00 0C 01
↑ ↑ ↑↑↑↑ ↑
EP1 等时 3072B 1ms
13.6 UVC控制请求
UVC使用类特定控制请求来调整摄像头参数。
常见控制请求
请求格式:
bmRequestType = 0xA1 (Device-to-Host, Class, Interface)
或 0x21 (Host-to-Device, Class, Interface)
bRequest = 0x81 (GET_CUR), 0x82 (GET_MIN), 0x83 (GET_MAX),
0x84 (GET_RES), 0x86 (GET_DEF), 0x01 (SET_CUR)
wValue = 控制选择器 << 8
wIndex = 单元ID << 8 | 接口号
wLength = 数据长度
视频控制接口请求示例
设置亮度(Processing Unit):
SETUP: 21 01 00 02 02 00 02 00
↑ ↑ ↑↑ ↑↑ ↑↑
│ │ │ │ └─ 2字节数据
│ │ │ └─ 单元2, 接口0
│ │ └─ 亮度控制(0x02)
│ └─ SET_CUR
└─ Host-to-Device
DATA: XX XX (亮度值,范围由GET_MIN/MAX确定)
获取曝光时间(Camera Terminal):
SETUP: A1 81 00 04 01 00 04 00
↑ ↑ ↑↑ ↑↑ ↑↑
│ │ │ │ └─ 4字节数据
│ │ │ └─ 终端1, 接口0
│ │ └─ 曝光时间(0x04)
│ └─ GET_CUR
└─ Device-to-Host
返回4字节曝光时间(单位100μs)
视频流接口请求示例
协商视频参数(Probe/Commit):
流程:
1. SET_CUR(PROBE) - 主机提议参数
2. GET_CUR(PROBE) - 设备返回实际能支持的参数
3. GET_MAX(PROBE) - 获取最大带宽
4. SET_CUR(COMMIT) - 确认参数,开始传输
Probe/Commit Control数据结构(26字节):
偏移 字段 大小
────────────────────────────────
0-1 bmHint 2 提示位
2 bFormatIndex 1 格式索引
3 bFrameIndex 1 帧索引
4-7 dwFrameInterval 4 帧间隔
8-9 wKeyFrameRate 2 关键帧速率
10-11 wPFrameRate 2 P帧速率
12-13 wCompQuality 2 压缩质量
14-15 wCompWindowSize 2 压缩窗口
16-17 wDelay 2 延迟(ms)
18-21 dwMaxVideoFrameSize 4 最大帧大小
22-25 dwMaxPayloadTransferSize 4 最大负载大小
示例(协商640×480 @ 30fps MJPEG):
SETUP: 21 01 00 01 01 00 1A 00
↑ ↑ ↑↑ ↑↑ ↑↑
│ │ │ │ └─ 26字节
│ │ │ └─ VS_PROBE_CONTROL, 接口1
│ │ └─ PROBE控制
│ └─ SET_CUR
└─ Host-to-Device
DATA (26字节):
00 00 01 01 15 16 05 00 00 00 00 00 00 00 00 00
00 00 00 60 09 00 00 00 0C 00
↑↑ ↑ ↑ ↑↑↑↑↑↑
Hint 格1 帧1 333333(30fps)
13.7 UVC数据传输流程
1. 设备枚举
主机读取所有描述符,解析格式/帧列表
2. 打开视频流接口
SET_INTERFACE(接口1, 替代设置0) ← 0带宽,不传输
3. 协商参数(Probe阶段)
SET_CUR(PROBE) → 提议:MJPEG, 640×480, 30fps
GET_CUR(PROBE) ← 设备返回实际参数
GET_MAX(PROBE) ← 获取最大带宽需求
4. 确认参数(Commit阶段)
SET_CUR(COMMIT) → 确认最终参数
5. 激活带宽
SET_INTERFACE(接口1, 替代设置1) ← 分配等时带宽
6. 开始传输
设备通过等时端点发送视频数据包
7. 停止传输
SET_INTERFACE(接口1, 替代设置0) ← 释放带宽
8. 关闭
设备返回Configured状态
13.8 UVC vs V4L2(Linux)
| 特性 | UVC | V4L2 |
|---|---|---|
| 定义 | USB规范 | Linux内核API |
| 范围 | USB摄像头 | 所有视频设备 |
| 驱动 | 免驱动 | 需内核驱动 |
| 控制 | USB控制传输 | ioctl系统调用 |
| 数据 | 等时/批量传输 | mmap/read/write |
| 应用层 | libusb | v4l2-ctl, OpenCV |
关系:Linux的uvcvideo内核模块将UVC设备桥接到V4L2 API,应用程序通过/dev/video0访问。
UVC摄像头描述符补充完成! 涵盖了视频控制、视频流、格式/帧描述符、控制请求和数据传输流程。