3. 端点描述符与配置:USB 批量传输的核心参数
在 QNX 环境下进行 USB 批量传输性能调优,首先必须透彻理解 USB 端点描述符(Endpoint Descriptor)的结构。端点描述符是 USB 设备固件向主机(Host)声明其传输能力的关键数据结构,它定义了端点的传输类型、方向、最大包长以及服务间隔。对于批量端点而言,bEndpointAddress、wMaxPacketSize 和 bInterval 三个字段直接决定了数据吞吐量、延迟特性和系统资源占用。
wMaxPacketSize 或 bInterval 会导致频繁的短包中断、DMA 碎片化以及调度延迟。
3.1 端点描述符结构总览
USB 标准规定,每个非默认端点(端点 0 除外)都必须包含一个端点描述符。该描述符在设备配置(Configuration Descriptor)中紧随接口描述符(Interface Descriptor)之后。其标准结构如下表所示:
| 偏移 (字节) | 字段名 | 长度 (字节) | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度,固定为 7 字节 |
| 1 | bDescriptorType | 1 | 描述符类型,端点描述符值为 0x05 |
| 2 | bEndpointAddress | 1 | 端点地址及方向(位 7 控制方向) |
| 3 | bmAttributes | 1 | 传输类型(位 1:0 定义类型) |
| 4 | wMaxPacketSize | 2 | 端点支持的最大数据包大小(字节) |
| 6 | bInterval | 1 | 轮询间隔(单位:帧/微帧) |
对于批量传输端点,bmAttributes 字段的低两位必须为 10b(批量传输)。QNX 的 USB 栈在枚举阶段会解析这些字段,并据此分配 URBs(USB Request Blocks)和 DMA 缓冲区。
3.2 bEndpointAddress:方向与端点号解码
bEndpointAddress 是一个单字节字段,它同时编码了端点号(Endpoint Number)和传输方向(Direction)。其位定义如下:
- 位 7(方向位): 0 表示 OUT 端点(主机到设备),1 表示 IN 端点(设备到主机)。
- 位 6:4: 保留位,必须为 0。
- 位 3:0(端点号): 取值范围 0x00~0x0F,共 16 个端点号。
例如,bEndpointAddress = 0x81 表示端点号 1 的 IN 端点;0x02 表示端点号 2 的 OUT 端点。在 QNX 驱动开发中,usb_device_entry 结构体中的 endpoint 字段直接存储此值,用于 URB 路由。
io-usb-otg 驱动中,批量 IN 端点(设备到主机)的 bEndpointAddress 方向位必须与硬件 FIFO 方向一致。如果固件错误地将 IN 端点声明为 OUT,QNX 主机侧将无法收到任何数据,且不会产生错误中断——数据会静默丢失。
3.3 wMaxPacketSize:批量传输的吞吐量天花板
wMaxPacketSize 是 2 字节字段,定义了端点在一个微帧(microframe)内能够发送或接收的最大数据字节数。对于批量端点,该字段的取值与 USB 速度直接相关:
| USB 速度 | wMaxPacketSize 有效值 | 理论最大吞吐量(每微帧) | QNX 典型配置 |
|---|---|---|---|
| Low-Speed (1.5 Mbps) | 不支持批量传输 | — | — |
| Full-Speed (12 Mbps) | 8, 16, 32, 64 | 64 字节 | 64 字节(推荐) |
| High-Speed (480 Mbps) | 512 | 512 字节 | 512 字节(必须) |
| SuperSpeed (5 Gbps) | 1024 | 1024 字节 | 1024 字节 |
在 High-Speed 模式下,wMaxPacketSize 必须为 512 字节。如果固件设置小于 512,QNX 主机栈会拒绝该配置,导致设备枚举失败。Full-Speed 模式下,建议使用 64 字节以获得最佳总线利用率。
3.4 bInterval:批量端点的轮询间隔
bInterval 字段在批量端点中的含义与等时或中断端点不同。对于批量传输,该字段在 USB 2.0 规范中定义为“最大 NAK 速率限制”,即端点可以连续返回 NAK 的最大微帧数。但在实际 QNX 驱动实现中,bInterval 的行为如下:
- Full-Speed 批量端点:
bInterval表示主机轮询该端点的时间间隔,单位为帧(1 帧 = 1ms)。有效值范围 1~255。例如bInterval = 1表示每帧轮询一次。 - High-Speed 批量端点:
bInterval必须为 0。HS 批量传输由主机控制器硬件自动调度,不需要软件轮询间隔。 - SuperSpeed 批量端点:
bInterval同样必须为 0。
bInterval 设置为非零值(如 1),io-usb-otg 驱动可能会进入错误处理路径,导致批量传输被降级为轮询模式,吞吐量骤降至 FS 水平。务必检查设备固件中的描述符定义。
3.5 批量端点配置的 QNX 实践检查清单
在 QNX 环境下验证端点描述符配置时,建议按以下步骤逐一核对:
- 使用
usb -v命令 查看设备枚举日志,确认bEndpointAddress方向位与预期一致。 - 检查
wMaxPacketSize是否匹配 USB 速度等级(HS 必须为 512)。 - 确认
bInterval在 HS/SS 下为 0,FS 下根据应用延迟需求设置(通常为 1)。 - 验证
bmAttributes低两位是否为 0x02(批量传输)。 - 在 QNX 驱动中打印
usb_endpoint_descriptor结构体,确认内核解析后的值与固件声明一致。
3.6 批量端点描述符的 QNX 内核解析示例
在 QNX 驱动代码中,端点描述符通常通过 usb_endpoint_descriptor 结构体访问。以下是一个典型的解析片段:
#include <usb/usb.h>
void parse_bulk_endpoint(struct usb_endpoint_descriptor *ep) {
uint8_t bEndpointAddress = ep->bEndpointAddress;
uint16_t wMaxPacketSize = ep->wMaxPacketSize;
uint8_t bInterval = ep->bInterval;
uint8_t bmAttributes = ep->bmAttributes;
// 提取方向
uint8_t direction = (bEndpointAddress & 0x80) >> 7;
uint8_t ep_num = bEndpointAddress & 0x0F;
// 检查是否为批量端点
if ((bmAttributes & 0x03) != USB_ENDPOINT_XFER_BULK) {
printf("错误:非批量端点\n");
return;
}
// 验证 wMaxPacketSize
if (wMaxPacketSize != 512 && wMaxPacketSize != 64) {
printf("警告:wMaxPacketSize 非标准值 %d\n", wMaxPacketSize);
}
// 验证 bInterval
if (bInterval != 0) {
printf("注意:HS 批量端点 bInterval 应为 0,当前为 %d\n", bInterval);
}
printf("批量端点: EP 0x%02x, 方向 %s, 最大包长 %d, 间隔 %d\n",
ep_num,
direction ? "IN" : "OUT",
wMaxPacketSize,
bInterval);
}
io-usb-otg 驱动中,可以在 usb_device_add 函数入口处添加类似的打印,实时观察设备枚举时的端点描述符内容。这比事后抓取 USB 分析仪数据更快捷。