1. USB中断基础概念
1.1 USB中断传输的定义
USB中断传输(Interrupt Transfer)是USB规范定义的四种标准传输类型之一,用于保证有限延迟的数据传输。与字面含义不同,USB中断传输并非由设备主动发起的中断信号,而是主机以固定轮询周期主动查询设备是否有数据需要传输。
核心定义:中断传输是一种周期性、低延迟、有保证带宽的传输方式,主机控制器按预定时间间隔轮询设备,设备在收到IN令牌包后返回数据或NAK握手包。
1.2 中断传输的特点
| 特性 | 描述 | 典型值 |
|---|---|---|
| 周期性 | 主机以固定间隔(bInterval)轮询设备 | 1ms ~ 255ms(全速/低速) |
| 延迟保证 | 最大延迟由轮询间隔决定,可预测 | ≤ bInterval × 1ms |
| 带宽预留 | 每帧/微帧预留固定比例带宽 | 全速≤90%,高速≤80% |
| 错误重试 | 支持硬件级自动重试(最多3次) | 3次重试后报告错误 |
| 数据完整性 | 使用CRC16校验,保证数据正确 | 数据阶段CRC16 |
| 最大数据载荷 | 受限于端点描述符wMaxPacketSize | 低速8B,全速64B,高速1024B |
关键区别:中断传输与批量传输(Bulk)最大的不同在于延迟确定性。批量传输没有时间保证,而中断传输保证在bInterval时间内至少被轮询一次。
1.3 中断传输的适用场景
中断传输最适合以下两类设备场景:
- HID(人机交互设备):键盘、鼠标、触摸板、游戏手柄等需要及时响应用户输入的设备。
- 实时数据流:传感器数据采集、工业控制信号、MIDI音频事件等对延迟敏感但数据量较小的应用。
1.4 中断传输与轮询机制
下图展示了USB中断传输的完整轮询流程:
1.5 中断端点描述符
中断端点在USB描述符中通过以下字段定义其行为:
struct usb_endpoint_descriptor {
__u8 bLength; // 描述符长度(7字节)
__u8 bDescriptorType; // 端点描述符类型(0x05)
__u8 bEndpointAddress; // 端点地址(bit7:方向,0-3:端点号)
__u8 bmAttributes; // 传输类型(0x03 = 中断传输)
__le16 wMaxPacketSize; // 最大数据包大小
__u8 bInterval; // 轮询间隔(单位:帧/微帧)
} __attribute__ ((packed));
注意:bInterval字段在不同速度下单位不同:
- 低速/全速:单位 = 1ms(帧),取值范围 1~255
- 高速:单位 = 125µs(微帧),取值范围 1~16(实际间隔 = 2^(bInterval-1) × 125µs)
- 超高速:单位 = 125µs,取值范围 1~16
1.6 中断传输与HID设备
HID(Human Interface Device)是中断传输最典型的应用场景。以USB键盘为例,其数据流如下:
1.7 中断传输的带宽计算
在设计USB中断传输时,需要计算带宽占用以确保不超过USB规范限制:
/* 中断传输带宽计算公式 */
带宽占用 = (wMaxPacketSize + 协议开销) / bInterval
/* 全速USB示例:HID键盘 */
wMaxPacketSize = 8 bytes
协议开销 = 13 bytes (SYNC+SOF+令牌+数据+握手)
bInterval = 8 ms
每帧带宽 = (8 + 13) / 8 = 2.625 bytes/frame
带宽占比 = 2.625 / 64 = 4.1% (远低于90%限制)
/* 高速USB示例:音频设备 */
wMaxPacketSize = 64 bytes
协议开销 = 13 bytes
bInterval = 1 (2^0 × 125µs = 125µs)
每微帧带宽 = (64 + 13) = 77 bytes/microframe
带宽占比 = 77 / 1024 = 7.5% (远低于80%限制)
设计建议:
- HID设备:bInterval设为8~16ms,平衡延迟与带宽
- 实时传感器:bInterval设为1~4ms,保证数据实时性
- 避免将bInterval设为0(规范不允许)
- 高速设备利用微帧机制可实现更低的延迟
1.8 中断传输与其它传输类型的对比
| 特性 | 中断传输 | 批量传输 | 等时传输 | 控制传输 |
|---|---|---|---|---|
| 周期性 | 是(固定间隔) | 否 | 是(固定间隔) | 否 |
| 延迟保证 | 有 | 无 | 有 | 无 |
| 带宽预留 | 有 | 无 | 有 | 无 |
| 错误重试 | 硬件自动重试 | 硬件自动重试 | 无重试 | 硬件自动重试 |
| 数据完整性 | CRC16保证 | CRC16保证 | 无校验 | CRC16保证 |
| 典型应用 | HID、传感器 | 存储、文件传输 | 音频、视频 | 枚举、配置 |
1.9 中断传输在Linux内核中的实现
在Linux USB子系统中,中断传输通过URB(USB Request Block)来管理:
/* 创建中断传输URB */
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
/* 填充中断传输URB */
void usb_fill_int_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context,
int interval);
/* 提交URB开始中断传输 */
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
/* 典型中断传输回调函数 */
static void interrupt_callback(struct urb *urb)
{
struct usb_device *dev = urb->dev;
int status = urb->status;
switch (status) {
case 0: /* 成功接收数据 */
process_received_data(urb->transfer_buffer,
urb->actual_length);
break;
case -ENOENT: /* URB被取消 */
case -ECONNRESET: /* 设备断开 */
return;
case -ESHUTDOWN: /* 主机控制器关闭 */
return;
default: /* 其他错误 */
dev_err(&dev->dev, "中断传输错误: %d\n", status);
break;
}
/* 重新提交URB以继续接收 */
usb_submit_urb(urb, GFP_ATOMIC);
}
常见陷阱:
- 中断URB的回调函数中必须重新提交URB才能持续接收数据
- 回调函数运行在中断上下文,不能调用可能睡眠的函数
- transfer_buffer必须使用DMA安全的缓冲区(kmalloc,非栈内存)
- interval参数必须与端点描述符中的bInterval匹配
1.10 中断传输的故障排查要点
在开发中断传输驱动时,以下问题需要重点关注:
- 轮询间隔不匹配:驱动中设置的interval必须与设备描述符一致,否则可能导致设备无响应
- 数据包大小超限:实际接收数据超过wMaxPacketSize会导致数据截断
- URB提交失败:检查设备状态、端点号、方向是否正确
- 回调函数阻塞:中断回调中执行耗时操作会阻塞整个USB子系统
- NAK风暴:设备频繁返回NAK会浪费总线带宽,需调整轮询间隔
调试命令:使用
lsusb -v 查看设备端点描述符,usbmon 跟踪URB传输过程,perf 分析中断处理延迟。