4. 异步列表调度原理:Async List 链表结构与 QH/qTD 关系

EHCI 主机控制器通过两种调度列表管理 USB 传输:周期性列表(Periodic List)用于等时与中断传输,异步列表(Async List)用于批量传输与控制传输。本章聚焦异步列表的链表结构、队列头(QH)与队列传输描述符(qTD)的层次关系,以及 EHCI 硬件如何遍历该列表完成批量传输调度。

4.1 异步列表的链表结构

异步列表是一个循环双向链表,由多个队列头(QH)通过指针串联而成。EHCI 主控制器通过异步列表寄存器(AsyncListAddr)获取链表首地址,并在每个微帧(Microframe)的空闲时间遍历该列表,执行批量传输。

链表属性 说明
结构类型 循环双向链表(Circular Doubly Linked List)
基本节点 队列头(QH)
遍历方向 从 AsyncListAddr 指向的 QH 开始,沿 Horizontal Link Pointer 前进
终止条件 当 Horizontal Link Pointer 的 T 位为 1 时停止;循环链表通常将最后一个 QH 指回首节点
调度时机 每个微帧中,当周期性列表遍历完成后,EHCI 执行异步列表遍历
关键概念:异步列表的循环特性确保所有 QH 在调度上具有公平性。EHCI 硬件不会在单次遍历中处理所有 QH,而是根据带宽预算和 qTD 状态决定每次处理的深度。

4.2 队列头(QH)内部结构

每个 QH 是 32 字节对齐的数据结构,包含三个关键区域:水平链接指针(Horizontal Link Pointer)、端点特征域(Endpoint Capabilities/Characteristics)以及当前 qTD 指针(Current qTD Pointer)。

QH 字段 偏移(字节) 作用
Horizontal Link Pointer 0x00 指向下一个 QH 或终止符;T 位控制是否终止
Endpoint Capabilities 0x04 包含设备地址、端点号、最大包长、方向、速率等
Current qTD Pointer 0x08 指向当前正在处理的 qTD;硬件执行时更新此指针
Overlay qTD(16 字节) 0x10 缓存当前 qTD 的关键执行状态,用于快速上下文切换
实现提示:Overlay qTD 是 EHCI 硬件加速的关键设计。当 QH 被调度时,硬件将 qTD 的前 16 字节加载到 Overlay 区域,避免每次访问系统内存读取完整 qTD,显著降低延迟。

4.3 qTD 结构与传输描述

队列传输描述符(qTD)是描述一次批量传输的数据结构,每个 qTD 对应一个或多个物理缓冲区。qTD 通过Next qTD Pointer 串联成单向链表,挂接在 QH 之下。

qTD 字段 偏移(字节) 说明
Next qTD Pointer 0x00 指向下一个 qTD;T 位为 1 表示链表结束
Alternate Next qTD Pointer 0x04 用于错误重试或备用传输路径(通常置为终止)
Token 0x08 包含 PID(IN/OUT/SETUP)、数据偏移、总字节数、中断标志等
Buffer Page Pointer 0–4 0x0C–0x1C 最多 5 个 4KB 页面的物理地址指针,支持跨页传输

4.4 QH 与 qTD 的层次关系

QH 与 qTD 构成两级链表:水平方向是 QH 之间的循环链表,垂直方向是每个 QH 下挂接的 qTD 单向链表。EHCI 硬件遍历时,先沿水平方向找到 QH,再沿垂直方向处理该 QH 下的 qTD 链。

异步列表:QH 与 qTD 层次关系图 QH #1 端点 1 IN QH #2 端点 2 OUT QH #3 端点 3 IN QH #4 端点 4 OUT 循环回指(T=0) qTD #1-1 64 字节 IN qTD #1-2 128 字节 IN qTD #1-3 32 字节 IN qTD #2-1 256 字节 OUT qTD #2-2 64 字节 OUT 图例说明: • 蓝色/红色/绿色/紫色矩形:队列头(QH),每个 QH 代表一个 USB 端点 • 黄色矩形:队列传输描述符(qTD),每个 qTD 描述一次批量传输 • 实线箭头:水平链表指针(Horizontal Link Pointer);虚线箭头:循环回指

4.5 硬件调度流程

EHCI 硬件在异步列表上的调度遵循以下步骤:

  1. 获取首 QH:从 AsyncListAddr 寄存器读取首 QH 物理地址。
  2. 检查 QH 状态:读取 QH 的 Current qTD Pointer,若为 NULL 或 qTD 已完成,则跳过该 QH。
  3. 加载 Overlay:将当前 qTD 的前 16 字节加载到 QH 的 Overlay 区域。
  4. 执行传输:根据 Token 字段的 PID 和缓冲区地址,启动 USB 事务。
  5. 更新状态:传输完成后,硬件更新 qTD 的 Token 状态(包括 XactErr、Babble、Halted 等标志)。
  6. 推进 qTD:若当前 qTD 完成且无错误,硬件将 Current qTD Pointer 更新为 Next qTD Pointer。
  7. 水平推进:沿 Horizontal Link Pointer 移动到下一个 QH,重复步骤 2–6。
常见陷阱:如果 QH 的 Halted 位被置位,硬件会跳过该 QH 及其所有挂接的 qTD,直到软件主动清除 Halted 位。这通常由传输错误或 STALL 握手触发,软件必须在错误恢复后重新激活 QH。

4.6 QH 与 qTD 的软件管理要点

管理操作 软件职责 硬件行为
QH 插入 分配 32 字节对齐内存,初始化端点特征,设置 Horizontal Link Pointer 遍历时自动识别新 QH
qTD 挂接 分配 qTD,设置 Token 和缓冲区指针,更新 QH 的 Current qTD Pointer 加载 Overlay 并执行传输
QH 移除 修改前驱 QH 的 Horizontal Link Pointer,绕过待移除 QH 遍历时跳过已移除节点
错误恢复 读取 qTD 状态,分析错误原因,清除 Halted 位,重新提交 qTD 恢复后继续执行 qTD 链

4.7 异步列表调度与批量传输性能

异步列表的调度效率直接影响批量传输的吞吐量。EHCI 在每个微帧中分配给异步列表的时间取决于周期性列表的负载。当周期性列表空闲时,异步列表可获得接近 90% 的 USB 总线带宽。

性能优化建议:
  • 将多个 qTD 预链接到 QH 下,减少软件中断频率。
  • 使用中断 qTD(Token 中的 IOC 位)仅在关键传输完成时触发中断。
  • 避免在 QH 链中插入空 QH(无挂接 qTD),这会浪费遍历时间。
  • 对于高速批量端点,建议 qTD 数据长度对齐 512 字节(高速批量最大包长)。