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);
}
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。驱动设计时应根据实际事件频率合理设置缓冲区大小,避免溢出。
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占用。
2.7 驱动开发中的故障排查清单
当中断端点工作异常时,按以下顺序检查描述符字段:
- 验证 bEndpointAddress:确认方向正确(IN端点使用
usb_rcvintpipe),端点号未与其他端点冲突。 - 验证 bmAttributes:低2位必须为0x03,否则主机拒绝中断传输。
- 验证 wMaxPacketSize:不超过速度等级限制,驱动缓冲区大小 ≥ 该值。
- 验证 bInterval:高速设备取值范围1–16,低速/全速设备1–255,单位与速度匹配。
- 使用工具验证:
lsusb -v查看原始描述符,usbmon跟踪URB提交状态。
调试命令示例:
lsusb -v -d 1234:5678 | grep -A 10 "Interrupt" 可快速过滤中断端点描述符信息。