2. USB中断端点描述符:关键字段解析

中断端点(Interrupt Endpoint)是USB设备向主机主动上报事件的核心通道,其描述符中的四个字段——bEndpointAddress、bmAttributes、wMaxPacketSize、bInterval——共同决定了中断传输的行为、带宽和实时性。本章逐字段拆解其位定义、取值规则及驱动开发中的常见陷阱。

2.1 字段总览与结构

中断端点描述符在USB规范中属于标准端点描述符(Standard Endpoint Descriptor),长度为7字节。下表列出所有字段及其在驱动开发中的核心作用:

偏移 字段 长度 核心作用
0 bLength 1 描述符长度,固定为0x07
1 bDescriptorType 1 端点描述符类型,固定为0x05
2 bEndpointAddress 1 端点方向(IN/OUT)与端点号
3 bmAttributes 1 传输类型(中断=0x03)
4 wMaxPacketSize 2 每帧最大数据载荷
6 bInterval 1 轮询间隔(单位:帧/微帧)
驱动开发提示: Linux内核在 include/uapi/linux/usb/ch9.h 中定义了端点描述符结构体 struct usb_endpoint_descriptor,解析时直接使用 le16_to_cpu() 转换 wMaxPacketSize 的字节序。

2.2 bEndpointAddress:端点地址与方向

bEndpointAddress 是一个位掩码字段,低4位表示端点号,最高位(Bit 7)表示方向。驱动开发中必须严格区分IN和OUT端点:

  • Bit 7 = 1:IN端点(设备→主机),用于读取中断数据。
  • Bit 7 = 0:OUT端点(主机→设备),用于发送控制或数据。
  • Bit 6–4:保留位,必须为0。
  • Bit 3–0:端点号(0x00–0x0F),同一方向下端点号必须唯一。
常见误区: 中断端点通常只使用IN方向(设备主动上报),但部分复合设备也会定义中断OUT端点(如HID设备的输出报告)。驱动中应通过 usb_rcvintpipe() 和 usb_sndintpipe() 宏自动生成正确的pipe值。
/* Linux内核宏定义示例 */
#define USB_DIR_OUT         0
#define USB_DIR_IN          0x80

/* 解析bEndpointAddress */
unsigned int ep_addr = desc->bEndpointAddress;
unsigned int ep_num  = ep_addr & 0x0F;
unsigned int ep_dir  = ep_addr & USB_DIR_IN;

if (ep_dir) {
    printk("中断IN端点,端点号: %d\n", ep_num);
} else {
    printk("中断OUT端点,端点号: %d\n", ep_num);
}
bEndpointAddress 位结构 Bit 7 Bit 6–4 Bit 3–0 方向 + 端点号 1=IN / 0=OUT 保留(必须为0) 端点号 0x00–0x0F

2.3 bmAttributes:传输类型标识

bmAttributes 的低2位标识传输类型,中断端点的该字段值必须为 0x03。高6位在USB 2.0及之前版本中保留为0,USB 3.x中用于标识同步类型和用法。

bmAttributes 低2位 传输类型 典型应用
0x00 控制传输 枚举、标准请求
0x01 等时传输 音频、视频流
0x02 批量传输 存储、网络
0x03 中断传输 HID、中断端点
故障排查要点: 如果设备描述符中中断端点的 bmAttributes 不为0x03,主机将拒绝识别该端点为中断端点,驱动调用 usb_submit_urb() 时会返回 -EINVAL。使用 lsusb -v 可快速验证该字段。

2.4 wMaxPacketSize:最大包长与带宽预算

wMaxPacketSize 是2字节字段,定义了中断端点在单个微帧(或帧)中能够传输的最大字节数。其值直接影响中断传输的带宽占用和驱动中的缓冲区分配。

  • USB 2.0 全速(12 Mbps):最大包长 ≤ 64 字节。
  • USB 2.0 高速(480 Mbps):最大包长 ≤ 1024 字节,支持3次微帧事务(bInterval 配合使用)。
  • USB 3.x 超高速(5 Gbps+):最大包长 ≤ 1024 字节,支持突发传输(Burst)。
/* 驱动中获取wMaxPacketSize的推荐方式 */
unsigned int max_pkt = le16_to_cpu(desc->wMaxPacketSize);

/* 分配URB缓冲区 */
urb->transfer_buffer = kmalloc(max_pkt, GFP_KERNEL);
urb->transfer_buffer_length = max_pkt;
性能建议: 中断端点的实际吞吐量 = wMaxPacketSize / bInterval(单位时间)。例如:wMaxPacketSize=64字节,bInterval=1(全速1ms间隔),则最大吞吐量为64 KB/s。驱动设计时应根据实际事件频率合理设置缓冲区大小,避免溢出。
wMaxPacketSize 与速度等级 全速 (12 Mbps) ≤ 64 字节 高速 (480 Mbps) ≤ 1024 字节 超高速 (5 Gbps+) ≤ 1024 字节 驱动缓冲区大小 ≥ wMaxPacketSize,避免数据截断

2.5 bInterval:轮询间隔与实时性

bInterval 定义了主机轮询中断端点的时间间隔,是决定中断响应延迟的关键参数。不同USB速度下,bInterval 的单位和取值范围不同:

USB 速度 单位 取值范围 实际间隔
低速 (1.5 Mbps) 帧 (10 ms) 1–255 10–2550 ms
全速 (12 Mbps) 帧 (1 ms) 1–255 1–255 ms
高速 (480 Mbps) 微帧 (125 µs) 1–16 125 µs – 2 ms
超高速 (5 Gbps+) 微帧 (125 µs) 1–16 125 µs – 2 ms
常见故障: 高速设备bInterval设置为0或大于16时,主机控制器可能拒绝调度该端点,导致URB提交失败。驱动中应通过 usb_endpoint_maxp_mult() 和 usb_endpoint_interrupt_bInterval() 辅助函数验证参数合法性。
/* 驱动中计算实际轮询间隔 */
unsigned int interval = desc->bInterval;
unsigned int actual_ms;

switch (udev->speed) {
case USB_SPEED_LOW:
    actual_ms = interval * 10;  /* 10ms per frame */
    break;
case USB_SPEED_FULL:
    actual_ms = interval;       /* 1ms per frame */
    break;
case USB_SPEED_HIGH:
case USB_SPEED_SUPER:
    actual_ms = interval * 125 / 1000; /* 125µs per microframe */
    break;
default:
    actual_ms = interval;
}

printk("中断端点轮询间隔: %d ms\n", actual_ms);

2.6 四个字段的协同关系

中断端点的实际行为由四个字段共同决定,驱动开发中必须整体考虑:

  • bEndpointAddress 确定数据流向和端点标识。
  • bmAttributes 确保传输类型为中断。
  • wMaxPacketSize 限制单次传输的数据量,影响缓冲区设计。
  • bInterval 控制轮询频率,决定中断延迟和CPU占用。
中断端点描述符字段协同关系 中断端点描述符 bEndpointAddress bmAttributes wMaxPacketSize bInterval 四个字段共同决定中断传输的路径、容量、频率和实时性

2.7 驱动开发中的故障排查清单

当中断端点工作异常时,按以下顺序检查描述符字段:

  1. 验证 bEndpointAddress:确认方向正确(IN端点使用 usb_rcvintpipe),端点号未与其他端点冲突。
  2. 验证 bmAttributes:低2位必须为0x03,否则主机拒绝中断传输。
  3. 验证 wMaxPacketSize:不超过速度等级限制,驱动缓冲区大小 ≥ 该值。
  4. 验证 bInterval:高速设备取值范围1–16,低速/全速设备1–255,单位与速度匹配。
  5. 使用工具验证:lsusb -v 查看原始描述符,usbmon 跟踪URB提交状态。
调试命令示例:
lsusb -v -d 1234:5678 | grep -A 10 "Interrupt" 可快速过滤中断端点描述符信息。