4. 中断端点特性:中断传输的时序保证、轮询间隔(bInterval)、最大包大小与适用场景(HID设备)
中断传输(Interrupt Transfer)是USB规范中为有限延迟、周期性、小批量数据传输而设计的传输类型。与批量传输尽力而为的语义不同,中断传输提供了明确的时序保证,确保主机在约定的时间窗口内轮询设备并完成数据交换。本章深入剖析中断端点的核心参数:轮询间隔(bInterval)、最大包大小,以及它们在HID(Human Interface Device)类设备中的典型应用。
4.1 中断传输的时序保证机制
USB主机控制器(Host Controller)在每帧(Frame)或微帧(Microframe)的调度中,为每个中断端点预留了固定的服务时间槽。这种调度机制保证了中断传输的最大延迟(Maximum Latency)和最小轮询频率。
- 全速/低速总线: 帧周期为1ms,主机在每个帧的SOF(Start of Frame)之后,按照中断端点的bInterval值,在特定帧中发起事务。
- 高速总线: 微帧周期为125µs,主机在每个微帧中检查调度表,决定是否轮询该中断端点。
- 时序保证的本质: 主机控制器硬件保证在bInterval指定的时间窗口内,至少有一次针对该端点的IN或OUT事务。如果总线带宽充足,延迟可以小于bInterval;但不会超过bInterval。
4.2 轮询间隔(bInterval)详解
bInterval是中断端点描述符中的关键字段,位于端点描述符的偏移量7处。其编码方式与总线速度密切相关,直接决定了中断传输的轮询频率。
| 总线速度 | bInterval 编码方式 | 轮询周期计算公式 | 有效范围 |
|---|---|---|---|
| 低速 (Low Speed) | 直接表示帧数 | 周期 = bInterval × 1ms | 10 ~ 255 (10ms ~ 255ms) |
| 全速 (Full Speed) | 直接表示帧数 | 周期 = bInterval × 1ms | 1 ~ 255 (1ms ~ 255ms) |
| 高速 (High Speed) | 指数编码 (2^(bInterval-1)) | 周期 = 2^(bInterval-1) × 125µs | 1 ~ 16 (125µs ~ 4.096s) |
| 超高速 (SuperSpeed) | 直接表示微帧数 | 周期 = bInterval × 125µs | 1 ~ 16 (125µs ~ 2ms) |
4.2.1 bInterval 对URB提交的影响
在Linux内核中,中断URB的提交周期必须与bInterval匹配。驱动程序通过usb_fill_int_urb()函数初始化URB时,内核会根据端点描述符自动计算合适的URB间隔。
/* Linux内核源码: drivers/usb/core/urb.c */
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->dev = dev;
urb->pipe = pipe;
urb->transfer_buffer = transfer_buffer;
urb->transfer_buffer_length = buffer_length;
urb->complete = complete_fn;
urb->context = context;
if (unlikely(interval < 1))
interval = 1;
urb->interval = interval;
/* 内核会根据端点bInterval对interval进行修正 */
usb_urb_interval_check(urb);
}
驱动程序在提交URB时,urb->interval字段必须与端点描述符的bInterval一致。如果驱动程序设置的interval小于bInterval,主机控制器调度器会以bInterval为准,忽略驱动程序的设置。
4.3 最大包大小(MaxPacketSize)约束
中断端点的最大包大小受总线速度和端点类型的限制。USB规范为中断端点定义了明确的包大小上限,超过该上限的配置将被主机拒绝。
| 总线速度 | 中断端点最大包大小 | 备注 |
|---|---|---|
| 低速 (Low Speed) | 8 字节 | 仅支持控制传输和中断传输 |
| 全速 (Full Speed) | 64 字节 | 与批量端点最大包大小相同 |
| 高速 (High Speed) | 1024 字节 | 允许在微帧内进行突发传输(最多3笔事务) |
| 超高速 (SuperSpeed) | 1024 字节 | 支持突发传输,最大突发长度由端点伴侣描述符定义 |
4.4 适用场景:HID设备深度分析
HID(Human Interface Device)类设备是中断传输最典型的应用场景。键盘、鼠标、游戏手柄、触摸屏、条形码扫描仪等设备都通过中断端点与主机通信。
4.4.1 HID设备的中断端点配置
一个标准的HID设备通常包含一个中断IN端点,用于向主机发送输入报告(Input Report)。部分HID设备还包含一个中断OUT端点,用于接收主机的输出报告(Output Report),例如键盘的LED状态指示。
- 中断IN端点: 设备向主机报告按键、鼠标移动、触摸事件等。bInterval通常设置为8~32(全速下对应8ms~32ms),兼顾响应速度和总线带宽。
- 中断OUT端点: 主机向设备发送配置指令或状态反馈,例如键盘CapsLock灯控制。bInterval通常与IN端点一致或略大。
- 报告描述符(Report Descriptor): HID设备通过报告描述符定义数据的格式和含义,中断端点传输的原始数据由HID驱动解析为具体的输入事件。
4.4.2 HID设备中断URB处理流程
在Linux内核中,HID设备的中断URB处理遵循以下典型流程:
- 设备枚举阶段: USB核心读取HID设备的接口描述符和端点描述符,获取中断IN端点的bInterval和MaxPacketSize。
- HID驱动绑定: hid-core驱动为中断IN端点分配并初始化一个中断URB,设置回调函数为
hid_irq_in()。 - URB提交: 驱动调用
usb_submit_urb()提交URB,主机控制器开始按bInterval周期轮询设备。 - 数据到达: 设备有输入事件时,在主机轮询时返回数据;无事件时返回NAK。主机在收到数据后调用回调函数。
- 报告解析: 回调函数将原始数据传递给HID核心,HID核心根据报告描述符解析出具体的输入事件(按键、移动等)。
- URB重提交: 回调函数处理完成后,驱动重新提交URB,维持连续轮询。
/* Linux内核源码: drivers/hid/usbhid/hid-core.c */
static void hid_irq_in(struct urb *urb)
{
struct hid_device *hid = urb->context;
int ret;
switch (urb->status) {
case 0: /* 成功 */
/* 将原始报告传递给HID核心层解析 */
hid_input_report(hid, HID_INPUT_REPORT,
urb->transfer_buffer,
urb->actual_length, 1);
break;
case -ECONNRESET: /* 设备断开 */
case -ENOENT:
case -ESHUTDOWN:
return; /* 不再重提交URB */
default: /* 其他错误 */
break;
}
/* 重新提交URB,维持连续轮询 */
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret && ret != -EPERM && ret != -ENODEV)
dev_err(&hid->dev, "can't resubmit intr, %s-%d\n",
hid->name, urb->interval);
}
4.5 中断传输与其他传输类型的对比
| 特性 | 中断传输 | 批量传输 | 等时传输 | 控制传输 |
|---|---|---|---|---|
| 时序保证 | 有(bInterval保证最大延迟) | 无(尽力而为) | 有(固定带宽预留) | 无(尽力而为) |
| 错误重传 | 支持(硬件重试) | 支持(硬件重试) | 不支持(无重传) | 支持(硬件重试) |
| 数据大小 | 小(≤1024字节/事务) | 大(≤1024字节/事务) | 中(≤1024字节/事务) | 小(≤64字节/全速) |
| 典型延迟 | 1ms ~ 几十ms | 不确定(可能数百ms) | 125µs ~ 1ms | 不确定 |
| 适用场景 | HID、少量周期性数据 | 存储、大块数据 | 音频、视频流 | 枚举、配置、命令 |
4.6 中断端点配置实战:bInterval选择指南
在实际的嵌入式系统或USB设备固件开发中,选择合适的bInterval值需要在响应速度和总线带宽之间取得平衡。
- 高响应需求(游戏鼠标、触控笔): 选择bInterval=1~4(全速下1ms~4ms,高速下125µs~500µs)。注意高速设备bInterval=1时,每微帧轮询一次,对主机控制器和USB总线带宽消耗较大。
- 中等响应需求(办公鼠标、键盘): 选择bInterval=8~16(全速下8ms~16ms)。这是大多数HID设备的默认配置,兼顾用户体验和总线效率。
- 低频率数据(温度传感器、环境监测): 选择bInterval=32~255(全速下32ms~255ms)。对于变化缓慢的物理量,过高的轮询频率只会浪费总线带宽和CPU资源。
- 电池供电设备: 适当增大bInterval(如50ms~100ms),减少主机轮询频率,降低设备端唤醒和响应功耗。
lsusb -v 查看设备端点描述符中的bInterval值。使用 usbmon 或 ftrace 跟踪中断URB的提交和完成时间,验证实际轮询间隔是否与bInterval一致。如果发现延迟异常,检查主机控制器的调度策略和总线带宽占用情况。