1. USB协议基础回顾:USB 2.0与USB 3.x架构差异、四种传输类型、描述符体系

做USB驱动开发,绕不开协议基础。很多新手一上来就翻芯片手册、调寄存器,结果被各种描述符搞晕,或者枚举死活过不去。我个人习惯是,先把协议层面的骨架搭清楚,再动手写代码。这一章我们就来梳理USB协议最核心的三个板块:架构差异、传输类型、描述符体系。

1.1 USB 2.0 与 USB 3.x 架构差异

先说结论:USB 3.x 不是 USB 2.0 的简单升级,它几乎是重新设计了一套物理层和链路层。两者最大的区别在于——通信模式从半双工变成了全双工。

对比项 USB 2.0 (High-Speed) USB 3.x (SuperSpeed)
信号线 D+/D- 差分对 SSTX+/SSTX-、SSRX+/SSRX- 两对差分线
通信模式 半双工(轮询式) 全双工(异步传输)
传输速率 480 Mbps 5 Gbps (USB 3.0) / 10 Gbps (USB 3.1 Gen2)
编码方式 NRZI + 位填充 8b/10b (USB 3.0) / 128b/132b (USB 3.1)
电源管理 挂起/恢复 U0~U3 多级链路电源状态
数据包结构 SOF、令牌包、数据包、握手包 链路层数据包 (LUP/LDN)+ 协议层数据包

我在项目中遇到过一个问题:把USB 3.0的摄像头插到USB 2.0的Hub上,结果枚举失败。后来排查发现,是设备端的SS Detect电路没有正确降级到USB 2.0模式。说白了,USB 3.x 的物理层多了一对独立的接收通道,这让它能够同时收发数据。但这也意味着,兼容性设计比USB 2.0复杂得多。

核心差异速记:

  • USB 2.0:一根线分时复用,主机轮询设备。
  • USB 3.x:两根线各走各的,设备可以主动发起异步通知。
  • USB 3.x 的链路层引入了 流控 (Flow Control) 和 链路训练 (Link Training),这是调试时最容易出问题的地方。

1.2 四种传输类型

USB协议定义了四种传输类型,每种都有固定的应用场景。你想想看,如果拿控制传输去传视频流,那肯定卡死。我建议你从带宽保证和错误重传两个维度去理解它们。

1. 控制传输 (Control Transfer)

这是USB的“命令通道”。所有设备必须支持端点0,用来做枚举、配置、状态查询。控制传输分三个阶段:Setup → Data (可选) → Status。我曾经在调试一个HID设备时,发现设备对Set Configuration请求没响应,最后定位到是描述符长度字段写错了。嗯,这里要注意:控制传输的Data阶段最大包长在USB 2.0 High-Speed下是64字节,在USB 3.x下是512字节。

2. 批量传输 (Bulk Transfer)

批量传输没有带宽保证,但保证数据完整性。适合U盘、打印机这类对延迟不敏感、但数据不能出错的设备。批量传输在USB 2.0下理论带宽约53 MB/s,实际能跑到40 MB/s就不错了。我见过不少驱动工程师把批量传输的缓冲区设得太大,结果导致URB提交失败。我的经验是:单个URB的缓冲区不要超过64 KB,否则在嵌入式Linux上容易触发DMA映射失败。

3. 中断传输 (Interrupt Transfer)

名字叫“中断”,其实也是轮询。主机每隔一个固定的时间间隔(bInterval)去查询设备是否有数据。适合鼠标、键盘、游戏手柄。USB 2.0下中断传输的最小间隔是125 µs (微帧),USB 3.x下可以做到更小。注意:中断传输的延迟是确定的,但带宽上限很低。

4. 同步传输 (Isochronous Transfer)

同步传输没有重传机制,数据丢了就丢了。但它的带宽是预留的,延迟也低。适合音频、视频、USB摄像头。同步传输的端点描述符里有一个字段,用来指定同步类型。我调试过一个USB麦克风,声音断断续续,最后发现是主机控制器没有正确分配微帧带宽。说白了,同步传输对主机控制器的调度能力要求很高。

传输类型 带宽保证 错误重传 典型应用 端点类型
控制 低 是 枚举、配置 双向
批量 无 是 U盘、打印机 单向
中断 有(固定间隔) 是 HID设备 单向
同步 有(预留带宽) 否 音频、视频 单向

1.3 描述符体系

描述符是USB设备的“身份证”。主机通过读取描述符来了解设备是什么、能干什么、怎么通信。描述符是树形结构,从设备描述符往下,一层套一层。我刚开始学USB时,被这个嵌套关系绕晕过。后来画了一张图,一下就清楚了。

调试技巧: 在Linux下用 lsusb -v 可以打印出完整的描述符树。如果枚举失败,先用这个命令看看设备返回了什么。我曾经靠这个命令发现一个设备返回的配置描述符总长度比实际数据短了2个字节,导致主机拒绝接受。

1. 设备描述符 (Device Descriptor)

每个USB设备只有一个设备描述符。它包含了USB规范版本号、设备类、厂商ID (VID)、产品ID (PID)、以及设备可以支持的配置数量。注意:设备描述符中的bMaxPacketSize0字段,在USB 2.0下必须是8、16、32或64,在USB 3.x下固定为512。

2. 配置描述符 (Configuration Descriptor)

一个设备可以有多个配置,但同一时刻只能激活一个。配置描述符里包含了该配置的总功耗(bMaxPower,单位2mA)、接口数量、以及该配置是否支持远程唤醒。我建议你在设计设备固件时,尽量只提供一个配置,多配置会增加主机驱动的复杂度。

3. 接口描述符 (Interface Descriptor)

接口描述符对应一个“功能”。比如一个USB摄像头可能有视频接口和音频接口。接口描述符里包含了接口编号、备用设置 (bAlternateSetting)、以及端点数量。备用设置是同步传输的常用技巧:通过切换备用设置来改变带宽需求。

4. 端点描述符 (Endpoint Descriptor)

端点是USB通信的终点。每个端点描述符定义了传输类型、方向、最大包长、轮询间隔。注意:端点0没有描述符,它是默认的控制端点。端点描述符中的wMaxPacketSize字段,在USB 2.0 High-Speed下,批量端点固定为512字节,中断端点最大1024字节,同步端点最大1024字节。

常见踩坑点:

  • 描述符的bLength字段必须等于该描述符类型的标准长度,否则主机直接拒绝。
  • 接口描述符的bInterfaceNumber必须从0开始连续编号,不能跳号。
  • 端点描述符的bEndpointAddress,bit7表示方向(1=IN,0=OUT),bit3~0是端点号。端点号不能重复。

1.4 描述符树形结构图

下面这张图展示了描述符的层级关系。我建议你把它打印出来贴在工位上,调试时对照着看。

USB描述符树形结构 设备描述符 配置描述符 (bNumInterfaces=N) 接口描述符 0 (bNumEndpoints=M) 接口描述符 1 (bNumEndpoints=K) 端点 1 (IN) 端点 2 (OUT) 端点 3 (IN) 端点 4 (OUT) 说明 • 设备描述符是根,一个设备可以有多个配置描述符 • 每个配置描述符下可以有多个接口描述符(功能) • 每个接口描述符下可以有多个端点描述符(通信管道)

1.5 小结

这一章我们回顾了USB协议最基础的三个部分。USB 2.0和USB 3.x的架构差异,决定了你在调试时看信号的方向完全不同——USB 2.0盯着D+/D-的包冲突,USB 3.x要关注链路训练状态机。四种传输类型的选择,说白了就是在带宽、延迟、可靠性之间做权衡。描述符体系是USB设备的“自我介绍”,枚举失败90%的问题都出在描述符上。