AI 生成
USB 枚举过程中最核心的四个控制请求:
USB 枚举流程(就像"设备入职流程")
想象一个新员工入职公司:
公司流程 | 对应 USB 操作 |
---|---|
1. 登记基本信息(姓名、身份证) | GET_DESCRIPTOR |
2. 分配工号 | SET_ADDRESS |
3. 安排部门和岗位 | SET_CONFIGURATION |
4. 设置具体工作权限(如打印机权限) | SET_INTERFACE |
🔍 逐个解析
1. 🟦 GET_DESCRIPTOR
------ "请介绍一下你自己"
📌 目的:
让主机获取设备的基本信息,判断它是什么类型的设备。
🧩 常见描述符类型:
描述符 | 内容 |
---|---|
Device Descriptor | 厂商、产品、版本、支持配置数等 |
Configuration Descriptor | 功耗、接口数量、端点信息 |
String Descriptor | 厂商名、产品名、序列号(人类可读) |
Interface Descriptor | 接口类(如 HID、CDC、MSC) |
Endpoint Descriptor | 端点地址、传输类型、包大小 |
📥 请求格式(SETUP 包):
bmRequestType: 0x80 // Device → Host
bRequest: 0x06 // GET_DESCRIPTOR
wValue: 0x0100 // 高字节=类型(1=Device), 低字节=索引
wIndex: 0x0000
wLength: 0x0012 // 请求长度(如 18 字节)
📤 响应示例:
Device Descriptor:
idVendor = 0x2341 (QinHeng)
idProduct = 0x7523
bNumConfigurations = 1
✅ 这是主机认识设备的第一步。
2. 🟨 SET_ADDRESS
------ "你的新工号是 13"
📌 目的:
为主机分配一个唯一的地址(非 0),以便后续通信。
🧩 为什么需要?
- 所有设备刚插入时地址都是
0x00
(默认地址) - 如果多个设备同时使用
0x00
,会冲突 - 所以主机必须为每个设备分配唯一地址(
0x01 ~ 0x7F
)
📥 请求格式:
bmRequestType: 0x00 // Host → Device
bRequest: 0x05 // SET_ADDRESS
wValue: 0x0013 // 新地址 = 0x13
wIndex: 0x0000
wLength: 0x0000 // 无数据
📤 响应:
- 设备返回
ACK
- 设备内部切换地址为
0x13
- 后续通信使用
Addr=0x13
⚠️ 注意:
SET_ADDRESS
是唯一一个在地址0x00
上执行,但影响未来地址的命令。
3. 🟩 SET_CONFIGURATION
------ "你属于研发部,配置编号 1"
📌 目的:
启用设备的某个配置(Configuration),使其进入工作状态。
🧩 为什么需要?
- 一个设备可以有多个配置(如省电模式 vs 高性能模式)
- 但只能启用一个
- 主机选择最合适的配置并激活它
📥 请求格式:
bmRequestType: 0x00 // Host → Device
bRequest: 0x09 // SET_CONFIGURATION
wValue: 0x0001 // 启用配置 1
wIndex: 0x0000
wLength: 0x0000
📤 响应:
- 设备返回
ACK
- 设备初始化所有接口和端点
- 可以开始正常通信
✅ 这是设备"真正开始工作"的标志。
4. 🟪 SET_INTERFACE
------ "你现在使用调试接口"
📌 目的:
在某个配置中,切换接口的备用设置(Alternate Setting)
🧩 为什么需要?
- 一个接口可以有多个"备用设置"(Alternate Settings)
- 例如:音频设备可以有不同采样率
- 或复合设备中启用特定功能
📥 请求格式:
bmRequestType: 0x01 // Interface → Host
bRequest: 0x0B // SET_INTERFACE
wValue: 0x0000 // 使用 Alternate Setting 0
wIndex: 0x0000 // 接口编号
wLength: 0x0000
📤 响应:
- 设备返回
ACK
- 切换到指定的接口设置
- 可能重新配置端点
💡 示例:
- 音频设备:切换采样率(44.1kHz → 48kHz)
- 网卡:启用/禁用 VLAN 支持
- 复合设备:只启用串口,关闭打印机
✅ 四者的执行顺序(典型枚举流程)
1. GET_DESCRIPTOR (获取设备描述符)
2. GET_DESCRIPTOR (获取配置描述符)
3. SET_ADDRESS (分配地址,如 0x13)
4. GET_DESCRIPTOR (再次获取设备/配置描述符,验证)
5. GET_DESCRIPTOR (获取字符串描述符,如产品名)
6. SET_CONFIGURATION (启用配置 1)
7. SET_INTERFACE (可选,设置接口模式)
✅ 一旦完成,设备就可以正常通信了。
🧩 类比理解(公司入职流程)
USB 命令 | 公司对应操作 | 类比说明 |
---|---|---|
GET_DESCRIPTOR |
HR 问:"你是谁?有什么技能?" | 获取基本信息 |
SET_ADDRESS |
分配工号 | 唯一标识,避免混淆 |
SET_CONFIGURATION |
安排部门和岗位 | 决定整体工作模式 |
SET_INTERFACE |
设置具体权限(如门禁卡权限) | 微调功能,启用子模块 |
✅ 总结对比表
命令 | 方向 | 目的 | 是否必须 | 示例 |
---|---|---|---|---|
GET_DESCRIPTOR |
Host → Device | 获取设备信息 | ✅ 必须 | 读取厂商、产品、配置数 |
SET_ADDRESS |
Host → Device | 分配唯一地址 | ✅ 必须 | wValue=0x13 |
SET_CONFIGURATION |
Host → Device | 启用配置 | ✅ 必须 | wValue=0x01 |
SET_INTERFACE |
Host → Device | 切换接口设置 | ❌ 可选 | 音频采样率切换 |
💡 实际抓包中的体现
如果你用 Wireshark 抓包,你会看到类似:
SETUP: GET_DESCRIPTOR (Device)
IN: DATA0 (设备描述符)
OUT: ACK
SETUP: SET_ADDRESS (wValue=0x13)
IN: DATA1 (空包) ← STATUS 阶段
OUT: ACK
SETUP: SET_CONFIGURATION (wValue=0x01)
IN: DATA1 (空包) ← STATUS 阶段
OUT: ACK
✅ 每个
SET_*
命令后都有一个STATUS
阶段(OUT + DATA1)
✅ 最终结论
这四个命令是 USB 枚举的核心:
GET_DESCRIPTOR
:认识设备SET_ADDRESS
:分配身份SET_CONFIGURATION
:启动设备SET_INTERFACE
:微调功能
它们共同完成了从"设备插入"到"可以正常使用"的全过程。