3. DMA传输基础:DMA控制器工作原理、总线主控与从控模式、描述符环与散聚表
在OHCI批量传输中,DMA(Direct Memory Access)是提升数据吞吐量的核心技术。本章深入剖析DMA控制器的工作机制、总线主控与从控模式的区别,以及描述符环与散聚表这两种关键数据结构的设计原理,为后续驱动调试打下坚实基础。
3.1 DMA控制器工作原理
DMA控制器允许外设与系统内存之间直接传输数据,无需CPU逐字节干预。其核心工作流程包含三个阶段:请求、传输、终止。
核心要点: DMA传输的实质是“总线控制权转移”——CPU将总线控制权交给DMA控制器,由后者完成内存与外设间的数据搬运,完成后交还控制权。
3.1.1 标准DMA传输流程
- 请求阶段: 外设(如USB主机控制器)通过DMA请求线(DREQ)向DMA控制器发出传输请求。
- 仲裁阶段: DMA控制器向CPU发出总线请求(HRQ),CPU在完成当前总线周期后释放总线控制权,通过HLDA应答。
- 传输阶段: DMA控制器接管地址总线和数据总线,执行源地址→目的地址的数据搬运,每次传输后自动递增/递减地址计数器。
- 终止阶段: 当传输计数器归零或外设撤销DREQ时,DMA控制器释放总线控制权,CPU恢复执行。
驱动调试提示: 在OHCI驱动中,DMA传输的启动通常由HcBufferStatus寄存器触发。调试时重点关注DMA完成中断是否及时到达,以及传输计数器是否与预期一致。
3.1.2 DMA传输模式对比
| 模式 | 传输粒度 | 总线占用 | 适用场景 | OHCI典型应用 |
|---|---|---|---|---|
| 单字节模式 | 1字节/次 | 低(每次释放) | 低速外设 | 控制传输 |
| 块传输模式 | 连续块(如512B) | 高(持续占用) | 高速批量数据 | 批量传输端点 |
| 请求模式 | 由外设决定 | 可变 | 可变速率外设 | 等时传输 |
| 级联模式 | 多DMA控制器协同 | 高 | 多通道系统 | 多端点并发 |
3.2 总线主控与从控模式
在USB主机控制器架构中,DMA操作分为两种总线控制模式:总线主控(Bus Master)和总线从控(Bus Slave)。这两种模式决定了谁在总线上拥有传输控制权。
3.2.1 总线主控模式
在总线主控模式下,USB主机控制器(HC)作为总线主设备,直接发起DMA传输。HC内部集成了DMA引擎,能够自主生成地址、控制信号,并在不经过CPU的情况下完成内存访问。
- 优势: 传输延迟低、CPU负载极小、适合高吞吐量批量传输。
- OHCI实现: HC通过HCCA(Host Controller Communication Area)中的描述符链表自主获取传输任务,直接读写系统内存中的缓冲区。
- 调试关注点: 总线主控传输的地址对齐、描述符链表的完整性、以及总线仲裁延迟。
3.2.2 总线从控模式
在总线从控模式下,USB主机控制器作为总线从设备,由系统DMA控制器(如Intel 8237或集成DMA控制器)代为管理传输。HC被动响应DMA控制器的读写请求。
- 劣势: 传输路径多一层DMA控制器转发,延迟增加;CPU需要参与DMA通道配置。
- 适用场景: 旧式系统或HC不支持总线主控时的兼容模式。
- 调试关注点: DMA通道分配冲突、传输计数器配置错误、以及DMA应答超时。
常见陷阱: 在OHCI驱动开发中,如果HC被错误配置为从控模式,批量传输性能会急剧下降。务必在初始化阶段检查HcControl寄存器中的Master位(位0),确保其置位以启用总线主控模式。
3.2.3 模式对比总结
| 对比维度 | 总线主控模式 | 总线从控模式 |
|---|---|---|
| 控制权归属 | USB主机控制器 | 系统DMA控制器 |
| 传输发起方 | HC自主发起 | CPU配置后由DMA控制器发起 |
| CPU参与度 | 极低(仅初始化) | 中等(配置+中断处理) |
| 传输延迟 | 低 | 中高 |
| OHCI默认模式 | 是 | 否(兼容模式) |
3.3 描述符环与散聚表
描述符环(Descriptor Ring)和散聚表(Scatter-Gather Table)是DMA传输中两种关键的数据结构,用于管理内存缓冲区的地址和长度信息。
3.3.1 描述符环(Descriptor Ring)
描述符环是一个固定大小的环形缓冲区,每个条目(Descriptor)包含一个内存缓冲区的物理地址、传输长度、控制标志和状态字段。HC通过循环遍历描述符环来获取传输任务。
- 结构特点: 环状链表,头尾指针在环内循环移动,无链表断裂风险。
- OHCI应用: HCCA中的中断端点描述符链表采用类似环结构,批量传输的通用描述符(TD)也以链表形式组织。
- 关键字段: 缓冲区地址(32位物理地址)、缓冲区长度(通常为0-8192字节)、完成状态码、链接指针。
/* OHCI通用描述符(TD)结构示例 */
struct ohci_td {
uint32_t status; /* 位[31:24]: 完成状态; 位[23:18]: 缓冲区填充 */
uint32_t cbp; /* 当前缓冲区指针(物理地址) */
uint32_t next_td; /* 下一个TD的物理地址 */
uint32_t be; /* 缓冲区结束地址 */
uint32_t flags; /* 传输方向、速度、数据切换等控制位 */
} __attribute__((packed));
调试技巧: 描述符环的“空转”问题常见于驱动初始化阶段。使用逻辑分析仪捕获HC对描述符的读取时序,检查环中是否存在无效描述符(如地址为0或长度超限)。
3.3.2 散聚表(Scatter-Gather Table)
散聚表允许将一次DMA传输映射到多个非连续的物理内存区域。表中每个条目(SG Entry)包含一个内存块的地址和长度,HC按顺序依次访问这些块,实现“分散读、聚集写”。
- 优势: 避免内存拷贝,支持零拷贝传输;适应碎片化内存分配。
- OHCI批量传输: 批量传输的TD可以链接多个缓冲区,通过TD链表实现类似散聚效果。
- 条目结构: 地址(64位或32位)、长度(通常为32位)、控制标志(如链结束标记)。
/* Linux内核散聚表条目结构 */
struct scatterlist {
unsigned long page_link; /* 页框号 + 偏移 */
unsigned int offset; /* 页内偏移 */
unsigned int length; /* 传输长度 */
dma_addr_t dma_address; /* DMA映射后的总线地址 */
unsigned int dma_length; /* DMA映射长度 */
};
3.3.3 描述符环与散聚表对比
| 对比维度 | 描述符环 | 散聚表 |
|---|---|---|
| 数据结构 | 固定大小环形数组 | 线性数组或链表 |
| 内存连续性 | 每个描述符指向连续缓冲区 | 支持非连续内存块 |
| 遍历方式 | 循环递增头指针 | 顺序遍历条目 |
| OHCI原生支持 | 是(TD链表) | 间接支持(通过TD链模拟) |
| 适用场景 | 固定大小、连续缓冲的批量传输 | 碎片化内存、零拷贝需求 |
3.4 DMA传输协同架构总览
下图展示了OHCI批量传输中DMA控制器、描述符环、散聚表与系统内存之间的协同关系:
3.5 驱动开发中的DMA调试要点
调试清单:
- 地址对齐检查: DMA缓冲区地址必须按HC要求对齐(通常为32字节或缓存行对齐),未对齐会导致传输错误。
- 描述符环完整性: 检查环中是否存在悬空指针(指向无效地址)或循环断裂。
- 散聚表边界: 确保SG条目的总长度不超过HC支持的DMA传输上限(OHCI通常为8KB)。
- 总线主控启用: 验证HcControl寄存器的Master位已置位,否则HC无法发起DMA。
- 中断延迟: 使用ftrace或perf测量DMA完成中断的响应时间,异常延迟可能表明总线竞争。