2. USB协议基础:包结构、传输类型与帧/微帧

USB(Universal Serial Bus)协议采用主机-从机(Host-Device)架构,所有通信均由主机发起。理解USB协议的基础——包结构、四种传输类型以及帧/微帧机制——是开发USB Host驱动的先决条件。本章将深入剖析这些核心概念,为后续的热插拔检测与中断传输处理奠定坚实的理论基础。

2.1 USB包结构:通信的最小单元

USB总线上的所有数据交换都以“包(Packet)”为单位进行。一个USB包由多个字段按顺序组成,其基本格式如下:

核心要点:USB包以同步字段(SYNC)开始,以包结束符(EOP)结束。包标识符(PID)决定了包的类型和用途。
字段 长度(位) 说明
SYNC 8 同步序列,用于接收器与发送器时钟同步。
PID 8 包标识符,由4位类型和4位校验组成,定义包类型(如令牌、数据、握手)。
ADDR 7 设备地址,范围0~127,地址0用于默认控制管道。
ENDP 4 端点号,每个设备最多支持16个端点(0~15)。
DATA 0~1024 数据负载,长度取决于传输类型和速度。
CRC 5或16 循环冗余校验,用于检测传输错误。
EOP — 包结束符,由单端信号(SEO)表示。

根据PID的不同,USB包主要分为以下三类:

  • 令牌包(Token Packet):由主机发出,用于启动一次传输事务。包括SETUP、IN、OUT和SOF(帧起始)包。
  • 数据包(Data Packet):携带实际数据,分为DATA0、DATA1、DATA2(高速)和MDATA(高速)包。
  • 握手包(Handshake Packet):用于确认传输状态,包括ACK(确认)、NAK(忙)、STALL(错误)和NYET(未准备好)。
USB包结构(以IN令牌包为例) SYNC PID ADDR ENDP CRC5 EOP PID = 0x69 (IN令牌) ADDR = 设备地址

2.2 四种传输类型

USB协议定义了四种传输类型,分别针对不同的应用场景。每种传输类型在带宽、延迟、错误重试等方面具有不同的特性。

传输类型 典型应用 带宽保证 错误重试 延迟要求
控制传输 设备枚举、配置、命令 10% 保留 硬件自动重试 中等
批量传输 U盘、打印机、扫描仪 无保证 硬件自动重试 低
中断传输 键盘、鼠标、游戏手柄 有保证(轮询间隔) 硬件自动重试 高
同步传输 音频、视频、摄像头 有保证(固定带宽) 无重试 极高

2.2.1 控制传输

控制传输是USB中最复杂的传输类型,用于主机与设备之间的配置、命令和状态交换。它由两个或三个阶段组成:

  • 建立阶段(Setup Stage):主机发送SETUP令牌包 + DATA0数据包(包含8字节请求描述符)。
  • 数据阶段(Data Stage,可选):主机或设备发送数据,使用DATA1/DATA0交替。
  • 状态阶段(Status Stage):用于确认整个控制传输完成,设备返回ACK/NAK/STALL。
驱动开发提示:在Linux内核中,控制传输通过 usb_control_msg() 函数实现。该函数封装了URB(USB Request Block)的构建和提交过程,驱动开发者只需指定请求类型、请求码、数据缓冲区和超时时间。

2.2.2 批量传输

批量传输用于传输大量数据,但不保证带宽和延迟。它利用总线空闲时间进行传输,当总线繁忙时会被延迟。批量传输具有硬件级别的错误重试机制,确保数据完整性。

2.2.3 中断传输

中断传输是本章的重点,用于需要定期轮询的设备(如HID设备)。主机以固定的时间间隔向设备发起IN事务,查询是否有数据需要传输。中断传输具有以下特点:

  • 轮询间隔:由设备端点描述符中的 bInterval 字段指定,单位为帧(全速/低速)或微帧(高速)。
  • 带宽保证:主机控制器会为中断传输预留带宽,确保在轮询间隔内能够完成传输。
  • 错误重试:如果传输失败,主机控制器会自动重试,直到成功或达到重试次数上限。

2.2.4 同步传输

同步传输用于对时间敏感的数据流(如音频、视频),它保证固定的带宽和低延迟,但不提供错误重试。如果传输过程中发生错误,数据将被丢弃。同步传输使用DATA0和DATA1包交替,但不需要握手包确认。

四种USB传输类型对比 控制传输 配置/命令/状态 有重试 | 中等延迟 带宽保留10% 批量传输 U盘/打印机 有重试 | 低延迟 无带宽保证 中断传输 键盘/鼠标 有重试 | 高延迟 轮询间隔保证 同步传输 音频/视频 无重试 | 极低延迟 固定带宽保证 传输类型选择指南 • 设备枚举/配置 → 控制传输 • 大块数据(U盘) → 批量传输 • 人机交互设备(HID) → 中断传输

2.3 帧与微帧

USB总线的时间被划分为固定长度的“帧(Frame)”或“微帧(Microframe)”,用于同步和带宽分配。

2.3.1 帧(全速/低速模式)

  • 帧长度:1毫秒(ms)。
  • 帧起始包(SOF):主机在每个帧开始时发送SOF包,用于同步所有设备。
  • 帧号:SOF包中包含一个11位的帧号(0~2047),用于设备同步和错误检测。
  • 带宽分配:每个帧的总带宽为1ms,主机控制器在帧内分配时间片给不同的传输事务。

2.3.2 微帧(高速模式)

  • 微帧长度:125微秒(μs)。
  • 微帧数量:每个帧包含8个微帧(8 × 125μs = 1ms)。
  • 微帧起始包:每个微帧开始时发送SOF包,帧号每8个微帧递增一次。
  • 优势:微帧机制允许更精细的带宽分配和更低的延迟,特别适合高速中断传输和同步传输。
驱动开发注意事项:在高速模式下,中断传输的轮询间隔以微帧为单位。例如,bInterval = 4 表示每4个微帧(即500μs)轮询一次。驱动开发者在解析端点描述符时,必须根据设备速度正确解释 bInterval 字段。
帧与微帧结构 帧 (Frame) — 1ms MF 0 MF 1 MF 2 MF 3 MF 4 MF 5 MF 6 MF 7 帧与微帧关系 1 帧 = 8 微帧 = 1ms | 每个微帧 = 125μs 高速设备使用微帧调度,全速/低速设备使用帧调度

2.4 传输事务与URB

在USB协议中,一次“传输事务(Transaction)”由令牌包、数据包(可选)和握手包(可选)组成。而Linux内核使用URB(USB Request Block)来抽象一次完整的USB传输请求。

URB是USB驱动开发的核心数据结构,它封装了以下信息:

  • 目标设备:设备地址和端点号。
  • 传输类型:控制、批量、中断或同步。
  • 数据缓冲区:指向数据内存的指针和长度。
  • 回调函数:传输完成或出错时调用的函数。
  • 标志位:如是否允许DMA、是否使用中断等。
关键概念:URB是异步的。驱动提交URB后立即返回,当传输完成时,内核会调用注册的回调函数。这种机制使得USB驱动能够高效地处理并发传输,而不会阻塞整个系统。

以下是一个典型的中断传输URB提交代码片段:

/* 分配中断传输URB */
struct urb *urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
    dev_err(&udev->dev, "Failed to allocate URB\n");
    return -ENOMEM;
}

/* 填充URB */
usb_fill_int_urb(urb, udev, 
                 usb_rcvintpipe(udev, endpoint->bEndpointAddress),
                 buffer, buffer_size,
                 complete_callback,   /* 回调函数 */
                 context,             /* 回调上下文 */
                 endpoint->bInterval); /* 轮询间隔 */

/* 提交URB */
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) {
    dev_err(&udev->dev, "Failed to submit URB: %d\n", retval);
    usb_free_urb(urb);
    return retval;
}

2.5 本章小结

本章详细介绍了USB协议的基础知识,包括:

  • 包结构:SYNC、PID、ADDR、ENDP、DATA、CRC和EOP字段,以及令牌包、数据包和握手包三种类型。
  • 四种传输类型:控制传输(配置/命令)、批量传输(大块数据)、中断传输(HID设备)和同步传输(音视频),每种类型在带宽、延迟和错误重试方面具有不同特性。
  • 帧与微帧:全速/低速模式使用1ms帧,高速模式使用125μs微帧,微帧机制提供了更精细的带宽调度能力。
  • URB:Linux内核中用于抽象USB传输请求的核心数据结构,支持异步提交和回调。