第3章 USB 3.0协议层:包格式、令牌包、数据包、握手包详解
看USB 3.0的枚举,说白了就是看主机和设备怎么用包来“讨价还价”。协议层这些包,就像快递单、包裹和回执单。我当年刚接触时,看着一堆英文缩写头都大了,后来用逻辑分析仪抓一次波形,才真正明白它们怎么协同工作。
3.1 包的基本结构
USB 3.0的包,比USB 2.0的复杂不少。每个包前面有一串同步序列(SYNC,32位),然后是一个包帧起始(SOP),接着是12字节的包头。如果包带数据,后面跟着最多1024字节的数据载荷,最后是4字节的CRC和包结束(EOP)。嗯,这里要注意:USB 3.0的包头本身不带CRC,而是用单独的链路层CRC保护。
| 字段 | 长度(位) | 说明 |
|---|---|---|
| SYNC | 32 | 同步序列,接收器用它锁定时钟 |
| SOP | 8 | 包开始标志 |
| Packet Header | 96 | 包含包类型、地址、端点等信息 |
| Data Payload(可选) | 0–8192 | 数据载荷,按4字节对齐 |
| CRC | 32 | 对数据载荷的校验 |
| EOP | 8 | 包结束标志 |
我习惯在调试时,先看SYNC和SOP是否对齐,如果不对齐,往往就是链路层出问题了。曾经有一次,我调一块FPGA模拟设备,EOP一直发错,主机就是不认包,折腾了两天才发现是发送状态机少等了一个时钟。
3.2 令牌包( Packet)
令牌包,你可以理解成“命令包”。它告诉设备要干什么:是发数据过来,还是接收数据,或者是建立控制传输。令牌包的包头类型字段固定为,后面的子类型决定具体操作。
- OUT:主机向设备发送数据
- IN:主机向设备请求数据
- SETUP:控制传输的启动阶段,主机发设置信息
- PING:主机测试设备是否能接收数据(流量控制)
- ERDY:设备通知主机自己准备好接收/发送了
令牌包里包含设备地址和端点号。你想想看,主机怎么知道包是发给谁的?就是靠这个地址。设备收到后,先检查地址是不是自己的,不是就直接扔了。
DeviceAddress字段,结果主机发的包全部被响应,导致总线上多个设备冲突。记住:设备必须严格过滤地址,否则会死机。
令牌包不带数据载荷,所以只有12字节的包头,后面直接跟EOP。我建议你在看协议分析仪时,先抓几个令牌包,观察它们的Type和Direction,很快就能摸清传输方向。
3.3 数据包(Data Packet)
数据包是真正的“货物”。令牌包说“给我数据”,设备就回一个数据包;令牌包说“收数据”,主机就发数据包。数据包的包头里有一个Data Sequence Number(序列号),用来防丢包、去重。
- DATA0 / DATA1 / DATA2 / MP(MData Packet):序列号交替使用,保证可靠传输
- Data Payload:最大1024字节,但实际取决于端点描述符的wMaxPacketSize
我遇到过一种情况:设备枚举时,控制传输需要返回设备描述符,设备因为某些原因只返回了0长度的数据包。主机收不到数据,就一直重试。后来发现设备的状态机在“发数据”阶段跑飞了,根本没把描述符填入缓冲区。
3.4 握手包(Handshake Packet)
握手包是确认信号。它只有12字节的包头,没有数据。主机发完数据包后,设备得告诉主机“收到了,没问题”(ACK)或者“现在忙,别烦我”(NAK)或者“这个端点出错了,别发了”(STALL)。USB 3.0里还有一种叫NYET的握手包,用于流量控制场景。
| 握手类型 | 含义 | 典型场景 |
|---|---|---|
| ACK | 数据已正确接收 | 所有成功的数据交换 |
| NAK | 设备忙,稍后再试 | 中断传输时设备暂无数据 |
| STALL | 端点出现严重错误 | 不支持的请求或设备故障 |
| NYET | 设备尚未准备好接收 | 主机发PING后设备回复NYET |
我个人习惯在固件里加一个握手包调试计数器,每次收到ACK就+1,收到STALL就打印错误。这样枚举过程中一旦握手异常,能立刻定位是哪个阶段卡住了。
3.5 包类型与事务流程
三类包并不是独立的,它们串在一起构成一个“事务”。比如典型的控制写事务:
主机 → 令牌包(SETUP) → 主机 → 数据包(DATA0) → 设备 → 握手包(ACK)
然后主机再发起一个控制读事务:
主机 → 令牌包(IN) → 设备 → 数据包(DATA1) → 主机 → 握手包(ACK)
下面这张图把整个关系画清楚了:
你看,整个流程很清楚:主机先扔一个令牌包,告诉设备“我要干嘛”,然后要么主机发数据、要么设备发数据,最后收一个握手包确认。我刚开始写USB驱动时,总想着一步到位,结果枚举失败还不知道是哪个包丢了。后来老老实实抓出每个包的时间戳,对照这个顺序,才把问题找出来。
令牌指路,数据载货,握手收工——三个包缺一不可。