USB 异步执行与完成交付
crab-usb 把主机管理、设备枚举和控制器传输分开。异步接口表示调用可以等待设备事件后继续,不表示每个 TRB 都创建一个任务,也不表示所有共享结构无锁。软件包源码位于 drivers/usb/usb-host/,内核后端位于 src/backend/kmod/。
该实现对应公共运行时与完成中的异步请求模型,设备停止条件由生命周期限定。
1. 主机执行模型
USBHost 持有后端对象,公开主机初始化、设备变化查询、设备打开及事件处理入口。Core 管理跨控制器复用的 hub 拓扑,xHCI、DWC2 和 EHCI 后端分别解释硬件请求。
1.1 平台与核心
drivers/ax-driver/src/usb/mod.rs 的 PlatformUsbHost 将 USBHost 与 BindingInfo 关联。UsbKernel 转发 DMA 分配、映射、同步及内核服务,USB 核心不直接从 FDT 取得这些能力。
USBHost::create_event_handler() 取得独立事件处理对象。平台包装提供 take_event_handler() 和带绑定来源的 handler 接管入口,避免把整个可变主机对象塞进 IRQ 回调。
1.2 异步调用范围
src/host.rs 的 init() 和 open_device() 等方法等待后端完成;src/backend/kmod/kcore.rs 的 Core 负责初始化根 hub、处理端口变化和创建已寻址设备。执行器由系统集成提供,USB future 本身不创建操作系统任务。
一个控制请求可以包含多个 TRB,一个 transfer descriptor 也可以由多条 TRB 组成。任务、请求、TD 和 TRB 不是一一对应关系,不能按 TRB 数量估计调度器任务数。
2. xHCI 请求发布
命令环、传输环和事件环具有不同生产者。ring.rs 保存 DMA TRB 存储和 cycle 状态,cmd.rs 组织命令,endpoint.rs 保存端点请求状态。
2.1 环与可见性
Ring 使用 CoherentArray 保存 TRB。带 Link TRB 的发送环将末槽保留给链接,回绕时改变 cycle 状态;SendRing::usable_capacity() 表达实际可 提交容量,不能用分配槽数直接代替。
SendRing::enqueue_transfer_td() 组织同一 TD 的发布。数据内容、cycle bit 和 doorbell 的顺序决定硬件何时看到请求;只有填入软件数组而未满足发布顺序,不构成硬件接受的证据。
该方法先以不可见 cycle 写入首条 TRB,再填写其余 TRB 并清理各地址对应的旧完成槽;执行 mb() 后才将首条 TRB 改为可见 cycle。首条描述符承担整个 TD 的发布入口,不能先向硬件发布首条,再补写后续内容。空 TRB 切片直接返回空地址集合。
2.2 命令与数据完成
SendRing 按 TRB 总线地址关联 Finished<R>,命令完成返回到命令等待者,传输完成由 TransferResultHandler 按设备 slot 和端点路由。端点更换队列时使用 replace_queues() 更新完成路由,不能继续向旧队列发布新完成。
事件处理只交付硬件结果;请求任务继续执行描述符解析、枚举或上层数据操作。一次唤醒不保证每个请求都完成,任务仍须检查自己等待的对象。
3. 完成槽与唤醒
src/backend/kmod/queue.rs 预先建立 BusAddr 到完成槽的映射。每个 FinishedData 保存原子状态、结果和 AtomicWaker,避免把完成值和任务通知当作同一件事。