3. UHCI 帧列表与传输描述符(TD)
UHCI 主机控制器通过 帧列表(Frame List) 和 传输描述符(Transfer Descriptor, TD) 两级数据结构来管理 USB 总线上所有周期性(等时/中断)和非周期性(控制/批量)传输。帧列表提供时间基准,TD 则描述每一次具体传输的属性和数据位置。理解这两者的格式与链接关系,是进行嵌入式平台 UHCI 驱动开发和 DMA 验证的基础。
3.1 帧列表(Frame List)结构
帧列表是一个物理地址连续的 4 字节表项数组,位于系统内存中。UHCI 基地址寄存器(BASEADDR)指向该数组的起始地址。帧列表的大小由 帧列表大小寄存器(FRNUM & SOFMOD) 控制,通常为 1024 项(每帧 1 项),也可配置为 256 或 64 项。
每个帧列表项(Frame List Entry, FLE)的格式如下:
| 位域 | 宽度(bit) | 描述 |
|---|---|---|
| Link Pointer | 31:4 | 指向 TD 或 QH 的物理地址(32 字节对齐,低 4 位为 0) |
| Reserved | 3:2 | 保留位,必须写 0 |
| QH/TD Select | 1 | 0 = 指向 TD(等时传输描述符 ITD);1 = 指向队列头(QH) |
| Terminate | 0 | 1 = 链表结束,UHCI 停止遍历该帧 |
UHCI 在每个 USB 帧(1 ms)开始时,根据当前帧号(Frame Number)索引帧列表,取出表项中的 Link Pointer,然后开始遍历该指针指向的 TD 或 QH 链。帧号寄存器(FRNUM)每帧自动递增,循环使用帧列表。
dma_alloc_coherent() 分配。
3.2 传输描述符(TD)格式详解
UHCI 使用两种 TD:等时传输描述符(Isochronous TD, ITD) 和 队列传输描述符(Queue TD, QTD)。控制传输和批量传输使用 QTD,它们通过队列头(QH)组织成单向链表。每个 TD 固定为 32 字节(4 个双字),必须 32 字节对齐。
QTD 的四个双字(DWord)格式如下:
| 双字 | 偏移 | 位域 | 宽度(bit) | 描述 |
|---|---|---|---|---|
| DWord 0 Link Pointer |
0x00 | Link Pointer (31:5) | 27 | 指向下一个 QTD 或 QH 的物理地址(32 字节对齐) |
| Depth/Breadth (4) | 1 | 0 = 深度优先(遍历完当前 TD 链再返回 QH);1 = 广度优先 | ||
| Terminate (0) | 1 | 1 = 链表结束 | ||
| DWord 1 Status & Control |
0x04 | Reserved (31:24) | 8 | 保留 |
| Error Counter (23:21) | 3 | 允许的最大错误重试次数(0-7),每次错误递减 | ||
| Low Speed (20) | 1 | 1 = 低速设备;0 = 全速设备 | ||
| IOC (19) | 1 | 1 = 完成此 TD 后产生中断 | ||
| Active (18) | 1 | 1 = TD 处于激活状态,UHCI 可执行;0 = 已完成或停止 | ||
| DWord 2 Token |
0x08 | PID (31:29) | 3 | USB 令牌包类型:000=OUT, 001=IN, 010=SETUP |
| Device Address (28:21) | 8 | 目标 USB 设备地址(0-127) | ||
| Endpoint Number (20:18) | 3 | 端点号(0-7) | ||
| DWord 3 Buffer Pointer |
0x0C | Buffer Pointer (31:0) | 32 | 数据缓冲区的物理地址(无需对齐,但建议 4 字节对齐) |
| Max Packet Length (10:0) | 11 | 此 TD 允许传输的最大字节数(1-1024) |
virt_to_phys() 或 DMA API 转换。此外,TD 结构体在内存中必须保持 32 字节对齐,否则 UHCI 会忽略该 TD。
3.3 TD 链接与队列机制
UHCI 使用 队列头(Queue Head, QH) 作为 TD 链的入口点。QH 本身不包含传输数据,只包含一个指向第一个 QTD 的指针和一个指向下一个 QH 的指针。帧列表中的每个非等时表项指向一个 QH,QH 再链接多个 QTD 形成传输队列。
典型的控制传输(SETUP + DATA + STATUS 阶段)在 UHCI 中通过 3 个 QTD 实现:
- SETUP TD: PID = SETUP (010),包含 8 字节设置包,Data Toggle = 0。
- DATA TD: PID = IN 或 OUT,包含实际数据,Data Toggle = 1。
- STATUS TD: PID = IN 或 OUT(与 DATA 方向相反),数据长度为 0,Data Toggle = 1。
这三个 TD 通过 Link Pointer 串联,最后一个 TD 的 Terminate 位设为 1。UHCI 的 DMA 引擎会按顺序遍历并执行每个 TD,直到遇到 Terminate 或 Active 位被清除。
3.4 关键字段详解与嵌入式测试要点
在嵌入式平台上验证 UHCI 控制传输时,以下 TD 字段需要特别关注:
| 字段 | 测试验证要点 | 常见错误 |
|---|---|---|
| Active (DWord1 bit18) | TD 初始化时必须置 1;UHCI 执行完成后自动清 0。轮询该位可判断传输是否完成。 | 未置 Active 位导致 UHCI 跳过 TD;或 Active 位被意外修改。 |
| Error Counter (DWord1 bit23:21) | 初始值通常设为 3。每次 USB 错误(如 CRC 校验失败)递减,减到 0 时 UHCI 停止该 TD 并置位 Stalled。 | 设为 0 导致首次错误即停止;或未检查错误计数递减情况。 |
| IOC (DWord1 bit19) | 置 1 可使 UHCI 在完成该 TD 后产生中断。控制传输通常在 STATUS TD 置 IOC。 | 每个 TD 都置 IOC 导致中断风暴;或未置 IOC 导致无法触发完成回调。 |
| Buffer Pointer (DWord3 bit31:0) | 必须指向有效的物理内存,且该内存区域不能被缓存或重映射。 | 使用虚拟地址导致 DMA 访问错误;或缓冲区跨页边界未处理。 |
| Max Packet Length (DWord3 bit10:0) | 对于全速设备,控制传输最大包长为 64 字节;低速设备为 8 字节。 | 设置超过端点能力导致 Babble 错误;或设置 0 导致传输被忽略。 |
3.5 帧列表与 TD 的协同工作流程
UHCI 在每个 USB 帧开始时执行以下步骤:
- 读取帧号: 从 FRNUM 寄存器获取当前帧号,对帧列表大小取模得到索引。
- 获取表项: 从帧列表数组中读取对应 FLE,解析 Link Pointer 和 Q/T 位。
- 遍历队列: 如果 Q/T=1,则进入 QH 链;否则处理 ITD 链。
- 执行 TD: 从 QH 指向的第一个 QTD 开始,检查 Active 位,若为 1 则执行 USB 传输。
- 更新指针: 执行完当前 TD 后,通过 Link Pointer 找到下一个 TD,直到 Terminate=1。
- 帧结束: 当前帧时间用完或链表遍历完毕,UHCI 等待下一帧 SOF 信号。
整个过程中,CPU 只需在初始化时设置帧列表和 TD 链,之后 UHCI 的 DMA 引擎自动完成所有遍历和传输操作,极大降低了 CPU 占用率。
3.6 嵌入式平台验证清单
在嵌入式平台上对 UHCI 帧列表和 TD 进行测试验证时,建议按以下清单逐项确认:
- 内存对齐: 帧列表 4 KB 对齐,TD 32 字节对齐,QH 16 字节对齐。
- 缓存一致性: 帧列表和 TD 所在内存区域必须配置为 non-cacheable,或在每次 CPU 修改后执行缓存刷新(如 ARM 的
clean_dcache_range())。 - 物理地址正确性: Link Pointer 和 Buffer Pointer 必须使用物理地址,且指向的内存区域可被 DMA 访问。
- Active 位初始化: 所有待执行的 TD 的 Active 位必须置 1,否则 UHCI 会跳过。
- Terminate 位设置: 链尾 TD 的 Terminate 位必须置 1,否则 UHCI 会继续读取无效内存地址。
- 中断处理: 仅在必要的 TD(如 STATUS TD)置 IOC 位,避免不必要的中断开销。
- 错误处理: 检查 TD 完成后的 Error Counter 和 Stalled 位,实现超时重试或错误恢复逻辑。