Linux驱动 2026年8月5日 196 分钟

USB设备描述符完全详解

一、描述符概述 USB设备通过描述符(Descriptor)向主机报告自己的特性、能力和配置。描述符是一组结构化的数据,…

一、描述符概述

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)

读取顺序

  1. 设备描述符(18字节)
  2. 配置描述符(9字节头部,获取wTotalLength)
  3. 完整配置描述符树(包含所有接口、端点)
  4. 字符串描述符(按需读取)

1.2 描述符类型列表

名称说明
0x01DEVICE设备描述符(每个设备1个)
0x02CONFIGURATION配置描述符(1个或多个)
0x03STRING字符串描述符(可选,多个)
0x04INTERFACE接口描述符(在配置描述符内)
0x05ENDPOINT端点描述符(在接口描述符内)
0x06DEVICE_QUALIFIER设备限定符(USB2.0高速)
0x07OTHER_SPEED_CONFIGURATION其它速度配置
0x08INTERFACE_POWER接口电源(已废弃)
0x0FBOS二进制对象存储(USB3.0)
0x10DEVICE_CAPABILITY设备能力(BOS内)
0x21HIDHID描述符(HID类特定)
0x22REPORTHID报告描述符(HID类)
0x24CS_INTERFACE类特定接口(Audio、CDC等)
0x25CS_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 固定大小的描述符

描述符类型大小(字节)标识字段说明
设备描述符18bLength=0x12始终固定18字节
配置描述符9bLength=0x09仅配置本身固定9字节
接口描述符9bLength=0x09始终固定9字节
端点描述符7bLength=0x07标准端点固定7字节
HID描述符9+bLength=0x09+基本9字节,可扩展
设备限定符10bLength=0x0AUSB 2.0高速设备

关键点

  • 上述描述符的结构是固定的,但配置描述符的wTotalLength包含整棵树
  • 所有描述符第一个字节bLength告诉你实际长度
  • 即使未来USB规范扩展,旧设备仍用固定长度

2.2 可变大小的描述符

描述符类型大小范围计算公式说明
字符串描述符2 ~ 255字节2 + 字符数×2UTF-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端点伴随6bLength=0x06USB 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一致性实际长度必须等于bLengthbLength=9但实际7字节
wTotalLength准确性配置树总字节数必须匹配多算或少算类特定描述符
端点地址唯一性同配置中不能重复(除了EP0)两个端点都是0x81
接口号连续性接口编号必须从0开始连续接口0、接口2(跳过1)
电源需求合法性bMaxPower不能超过总线能力自供电设备写250(500mA)
端点方向合法性控制端点必须双向EP0设置为IN-only
字符串索引有效性非0索引必须对应实际字符串iProduct=5但只有3个字符串
类代码匹配接口类代码必须与描述符匹配bInterfaceClass=0x03(HID)但无HID描述符
BCD格式正确版本号必须用BCDbcdUSB=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描述符调试工具

工具平台用途获取方式
USBlyzerWindows捕获USB通信,解析描述符usblyzer.com
Wireshark + USBPcapWindows开源抓包工具wireshark.org
Bus HoundWindowsUSB/PCI总线分析perisoft.net
lsusb -vLinux显示设备描述符树系统自带
USB Descriptor ToolWindows微软官方描述符解析器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

十二、常见设备类型总结

设备类型ClassSubClassProtocol描述符特点
键盘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)0x010x02 (Bidirectional)1接口, 2批量端点
Hub0x09 (Hub)0x00Hub描述符(特殊)

十三、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)

特性UVCV4L2
定义USB规范Linux内核API
范围USB摄像头所有视频设备
驱动免驱动需内核驱动
控制USB控制传输ioctl系统调用
数据等时/批量传输mmap/read/write
应用层libusbv4l2-ctl, OpenCV

关系:Linux的uvcvideo内核模块将UVC设备桥接到V4L2 API,应用程序通过/dev/video0访问。


UVC摄像头描述符补充完成! 涵盖了视频控制、视频流、格式/帧描述符、控制请求和数据传输流程。

上一篇 USB子系统4-USB设备描述符

下一篇 libusb_handle_events详解

libusb_handle_events_timeout_completed() 详解 一、函数原型 二、函数作用 核心...