以下是关于 td_t(Transfer Descriptor)结构 的详细解析,结合 OHCI 规范与 Linux 驱动实现:


1. 核心定义

td_t 是 OHCI(Open Host Controller Interface)规范中的 传输描述符(Transfer Descriptor),用于描述一个 最小数据传输单元。每个 USB 传输请求(URB)会被拆分为多个 TD,由硬件按链表顺序执行。


2. 数据结构(OHCI规范)

typedef struct td {
    /* 硬件相关字段(必须严格对齐) */
    volatile uint32_t hwINFO;       // 控制/状态字段(见下方详解)

    volatile uint32_t hwCBP;       // Current Buffer Pointer(当前数据地址)

    volatile uint32_t hwNextTD;    // 下一个TD的物理地址
    volatile uint32_t hwBE;        // Buffer End(数据结束地址)

    /* 软件扩展字段(驱动私有) */
    struct ed *ed;                // 所属的端点描述符
    struct td *next_dl_td;        // 延迟处理的TD指针(用于错误恢复)
    int index;                    // 在URB中的序号
    void *data;                   // 原始数据缓冲区指针(虚拟地址)
} td_t;

3. 关键字段解析

(1) 硬件寄存器字段

字段名 位域 作用
hwINFO 0x0000001F 传输状态(CC: Condition Code)
0x00000060 数据轮询延迟(DP: Delay Phase)
0x00008000 方向位(DIR: 0=OUT, 1=IN)
0x00010000 等时传输标志(ISO)
hwCBP - 当前数据包的 物理起始地址(32位对齐)
hwNextTD - 下一个TD的 物理地址(链表结构,末尾TD必须为0)
hwBE - 当前数据包的 物理结束地址(计算方式:hwCBP + len - 1

(2) 状态码(CC字段)

#define TD_CC_NO_ERROR      0x00  // 传输成功
#define TD_CC_STALL         0x04  // 端点暂停(需清除halt)
#define TD_CC_DATA_UNDERRUN 0x0C  // 数据不足(等时传输)
#define TD_CC_DATA_OVERRUN  0x0D  // 数据溢出

4. 内存布局要求

根据 OHCI 规范 5.2.7:

  • 对齐要求:每个 TD 必须 16字节对齐hwNextTD & 0xF == 0
  • 字节序:所有字段必须为大端(Big-Endian),需用 m32_swap() 转换
  • 物理连续:TD 链表在物理内存中无需连续,但单个 TD 不可跨页

5. 与 URB 的关系

一个 URB 可能对应多个 TD(分片规则):
| 传输类型 | 分片逻辑 | 示例(4KB数据) |
|—————|———————————–|————————–|
| 控制传输 | 1 TD(SETUP) + N TD(DATA) + 1 TD(STATUS) | 3个TD(假设DATA≤4KB) |
| 批量传输 | 每4KB数据一个TD | 1个TD |
| 等时传输 | 每帧(Frame)一个TD | 8个TD(8ms间隔) |


6. 硬件工作流程

  1. 调度器从 ED 的 hwHeadP 读取首个 TD
  2. DMA引擎hwCBPhwBE 访问数据缓冲区
  3. 状态更新:完成后写入 hwINFO 的 CC 字段
  4. 链表推进:跳转到 hwNextTD 继续处理

7. Linux驱动实现示例

(1) TD分配

td_t *td_alloc(struct usb_device *dev) {
    td_t *td = dma_pool_alloc(ohci->td_cache, GFP_ATOMIC, &td_dma);
    memset(td, 0, sizeof(*td));
    td->hwINFO = m32_swap(OHCI_TD_DP_INIT);  // 初始化Delay Phase
    return td;
}

(2) TD填充(参考td_fill函数)

td->hwCBP = m32_swap(data_phys);          // 数据起始地址(物理)
td->hwBE  = m32_swap(data_phys + len - 1); // 数据结束地址
td->hwNextTD = m32_swap(next_td_phys);    // 链接下一个TD

8. 错误处理场景

错误现象 可能原因 调试方法
CC=0x04(STALL) 端点配置错误 检查设备描述符
CC=0x0E(CRC) 物理层信号干扰 缩短线缆/更换Hub
hwCBP=0xFFFFFFFF DMA映射失败 检查dma_map_single()返回值

9. 性能优化技巧

  1. TD池缓存:预分配TD避免实时分配开销
  2. 批量化提交:一次性构建完整TD链
  3. 对齐优化:确保hwCBPhwBE按缓存行对齐(通常64字节)

10. 与其它结构的关联

分解为调度引用链接URBTD链EDTDDMA缓冲区Next_TD

完整定义可参考《OHCI Specification》5.2.7节及Linux内核源码(drivers/usb/host/ohci.h