以下是关于 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. 硬件工作流程
- 调度器从 ED 的
hwHeadP
读取首个 TD - DMA引擎按
hwCBP
和hwBE
访问数据缓冲区 - 状态更新:完成后写入
hwINFO
的 CC 字段 - 链表推进:跳转到
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. 性能优化技巧
- TD池缓存:预分配TD避免实时分配开销
- 批量化提交:一次性构建完整TD链
- 对齐优化:确保
hwCBP
和hwBE
按缓存行对齐(通常64字节)
10. 与其它结构的关联
完整定义可参考《OHCI Specification》5.2.7节及Linux内核源码(
drivers/usb/host/ohci.h
)