4. 控制传输调试:Setup包结构解析、控制传输阶段波形分析、标准请求与厂商请求调试案例
控制传输,说白了就是USB通信的“指挥通道”。
设备枚举、配置、状态查询,全得靠它。我见过不少工程师,一上来就盯着BULK传输调,结果设备连枚举都过不去,折腾半天才发现是控制传输没处理好。
这一章,咱们就把控制传输的底裤扒干净。
4.1 Setup包结构:控制传输的“指令信封”
控制传输的第一步,是Host发一个Setup包。这个包固定8字节,结构如下:
| 偏移 | 字段 | 长度(字节) | 说明 |
|---|---|---|---|
| 0 | bmRequestType | 1 | 传输方向、请求类型、接收对象 |
| 1 | bRequest | 1 | 请求编号(标准/厂商/类) |
| 2-3 | wValue | 2 | 请求参数(小端序) |
| 4-5 | wIndex | 2 | 索引/接口/端点号 |
| 6-7 | wLength | 2 | Data阶段传输的字节数 |
这里有个坑,我一开始也踩过——wValue和wIndex是小端序。你用逻辑分析仪抓包,看到的是0x1200,实际值是0x0012。别搞反了。
bmRequestType 位域解析(我习惯这么记):
- Bit 7:方向。0=Host→Device,1=Device→Host
- Bit 6-5:类型。00=标准,01=类,10=厂商,11=保留
- Bit 4-0:接收者。00000=设备,00001=接口,00010=端点,00011=其他
举个例子:0x80 = 1000 0000,方向是Device→Host,标准请求,接收者是设备。这就是Get Descriptor的Setup包。
4.2 控制传输三阶段:Setup / Data / Status
控制传输不是一把梭,它分三个阶段。我画了张图,帮你理清流程:
我调试时最常用的方法:先看Status阶段有没有返回ACK。如果Device回了STALL,说明它不认识这个请求。这时候别急着改驱动,先查Setup包内容对不对。
4.3 波形分析:用逻辑分析仪抓控制传输
纸上谈兵没意思,咱们直接看波形。我用的是Saleae逻辑分析仪,配合USB解码插件。
抓一个标准的Get Descriptor请求,波形长这样:
Token: SETUP (ADDR=1, ENDP=0)
Data: 80 06 00 01 00 00 12 00 (8字节Setup包)
Handshake: ACK
Token: IN (ADDR=1, ENDP=0)
Data: 12 01 10 01 00 00 00 08 ... (设备描述符前8字节)
Handshake: ACK
Token: IN (ADDR=1, ENDP=0)
Data: ... (剩余描述符数据)
Handshake: ACK
Token: OUT (ADDR=1, ENDP=0)
Data: 0长度包
Handshake: ACK
我的调试习惯:
抓包时,重点关注三个地方:
- Setup包的8字节是否完整——少一个字节,Device直接STALL
- Data阶段的wLength是否匹配——Host要12字节,Device只回了8字节,会出问题
- Status阶段有没有ACK——没ACK说明Device没收到或者没处理完
有一次,我调一个U盘驱动,发现枚举到一半就卡死了。抓包一看,Setup包发出去,Device回了STALL。查了半天,原来是wValue的高低位写反了。你说冤不冤?
4.4 标准请求 vs 厂商请求:调试案例
标准请求是USB规范定死的,比如Get Descriptor、Set Address、Set Configuration。这些请求,所有USB设备都必须支持。
厂商请求就不一样了。芯片厂商可以自定义,比如读固件版本、写寄存器、启动OTA升级。
| 请求类型 | bRequest范围 | 典型用途 | 调试要点 |
|---|---|---|---|
| 标准请求 | 0x00 - 0x0F | 枚举、配置、状态查询 | 必须符合USB 2.0规范 |
| 类请求 | 0x10 - 0x1F | HID报告、Mass Storage命令 | 参考类规范文档 |
| 厂商请求 | 0x20 - 0xFF | 私有功能、调试、升级 | 需要芯片手册 |
我分享一个真实案例。之前调一个摄像头驱动,厂商提供了一个私有请求,用来设置图像参数。Host发出去,Device没反应。
抓包发现:
- Setup包:bmRequestType = 0x40 (Host→Device,厂商请求,设备)
- bRequest = 0xA0 (厂商自定义)
- wValue = 0x0100 (参数索引)
- wIndex = 0x0000
- wLength = 0x0000 (无Data阶段)
看起来没问题。但Device就是不回ACK。我查了芯片手册,发现这个请求需要Data阶段,wLength应该设为4字节。厂商文档写得不清楚,坑了我半天。
避坑指南:
我曾经因为厂商请求的wLength设错,浪费了一整天。调试厂商请求时,务必确认:
- bmRequestType的方向位是否正确(Host→Device还是Device→Host)
- wLength是否匹配实际传输的数据长度
- Device端是否在正确的端点处理了该请求
4.5 实战:用USB分析仪定位控制传输故障
最后,我分享一个通用的调试流程。遇到控制传输问题,按这个步骤来:
- 抓原始波形:用逻辑分析仪或USB分析仪抓取USB总线上的D+和D-信号
- 解析Setup包:确认8字节内容是否正确,特别是bmRequestType和wLength
- 检查Data阶段:如果有Data阶段,确认数据内容和长度是否匹配
- 看Status阶段:Device返回的是ACK还是STALL?STALL说明Device拒绝了这个请求
- 对比规范:如果是标准请求,对照USB 2.0规范检查;如果是厂商请求,对照芯片手册
嗯,这套流程我用了快十年,基本没失手过。控制传输调通了,后面的BULK和ISO传输才能跑起来。别急,一步一步来。