V4L2视频缓冲队列开发与DMA内存管理 - 第1节
1.1 DMA 工作原理
直接存储器访问(Direct Memory Access, DMA)是一种允许硬件设备直接与系统内存进行数据交换,而无需 CPU 逐字节干预的机制。在多媒体驱动开发中,DMA 是摄像头、显示控制器、音频接口等高速外设与内存之间传输帧数据、音频样本的核心通道。
核心价值:DMA 将 CPU 从繁重的数据搬运任务中解放出来,使 CPU 可以专注于协议解析、控制逻辑和上层应用处理,从而大幅提升系统吞吐量并降低延迟。
1.1.1 无 DMA 与有 DMA 的对比
| 对比维度 | 无 DMA(PIO 模式) | 有 DMA 模式 |
|---|---|---|
| 数据搬运主体 | CPU 逐字读取/写入 | DMA 控制器硬件自动搬运 |
| CPU 占用率 | 高(100% 占用直到传输完成) | 低(仅初始化时参与,传输中可执行其他任务) |
| 传输速率 | 受限于 CPU 指令周期 | 接近总线带宽,可达数 GB/s |
| 适用场景 | 小数据量、低速设备(如 UART) | 大数据块、高速设备(如 Camera、GPU、NVMe) |
1.1.2 DMA 基本工作流程
- 设备请求:外设(如摄像头控制器)准备好数据后,向 DMA 控制器发送 DMA 请求信号(DREQ)。
- 总线仲裁:DMA 控制器向 CPU 请求总线控制权(HRQ),CPU 在完成当前总线周期后让出总线(HLDA)。
- 地址与计数加载:DMA 控制器从寄存器中读取源地址、目标地址和传输字节数。
- 数据传输:DMA 控制器在总线周期内直接在外设与内存之间搬运数据,每传输一个单元自动递增地址并递减计数。
- 传输完成:当计数归零时,DMA 控制器向 CPU 发送中断信号(DMA_IRQ),通知传输结束。
1.2 DMA 控制器
DMA 控制器(DMAC)是负责执行 DMA 传输的硬件模块。在嵌入式 SoC 中,DMAC 通常作为系统总线上的一个主设备存在,拥有独立的总线控制能力。
1.2.1 DMA 控制器内部结构
- 控制寄存器组:配置传输方向、传输模式、地址递增方式等参数。
- 源地址寄存器(SAR):存放数据源地址(外设 FIFO 或内存地址)。
- 目标地址寄存器(DAR):存放数据目标地址(内存或外设 FIFO)。
- 传输计数寄存器(TCR):记录剩余待传输的数据单元数。
- 通道仲裁逻辑:管理多个 DMA 通道的优先级和并发请求。
1.3 DMA 传输模式
根据传输过程中总线控制权的释放策略,DMA 主要分为以下三种模式:
| 传输模式 | 工作方式 | 总线占用 | 典型场景 |
|---|---|---|---|
| 单次传输(Single Transfer) | 每传输一个单元(字节/字)后释放总线,重新仲裁 | 低,每次传输后释放 | 低速外设、多设备共享总线 |
| 块传输(Block Transfer) | 一次请求完成整个数据块的传输,期间不释放总线 | 高,传输期间独占总线 | 高速数据流(Camera、Audio) |
| 请求传输(Demand Transfer) | 外设通过 DREQ 信号控制传输节奏,信号有效时持续传输 | 动态,由外设速率决定 | FIFO 半满触发传输的外设 |
驱动开发提示:在 videobuf2 框架中,DMA 传输通常采用块传输模式,以确保帧数据在指定时间内完整到达内存缓冲区。对于需要流控的场景(如音频),请求传输模式更为常见。
1.4 DMA 与 CPU 交互
DMA 与 CPU 之间的高效协作是多媒体驱动稳定运行的关键。两者通过以下机制实现同步与数据共享:
1.4.1 交互机制
- 中断通知:DMA 传输完成后触发中断,CPU 在中断服务例程(ISR)中处理数据或启动下一轮传输。
- 轮询状态寄存器:CPU 通过读取 DMA 控制器的状态寄存器判断传输是否完成,适用于低延迟或中断不可用的场景。
- 描述符链(Descriptor Chain):DMA 控制器自动从内存中读取下一个传输描述符,实现多缓冲区连续传输而无需 CPU 逐次干预。
- Cache 一致性维护:DMA 直接访问物理内存,CPU 使用 Cache 时需通过 dma_map_single / dma_sync_single_for_cpu 等 API 保证数据一致性。
1.4.2 Cache 一致性处理
常见陷阱:DMA 直接访问物理内存,而 CPU 可能将数据缓存在 Cache 中。如果 CPU 修改了缓冲区但未刷回内存,DMA 读取到的将是陈旧数据;反之,DMA 写入内存后 CPU 从 Cache 读取也可能拿到旧值。必须使用 DMA API 保证一致性。
| API 函数 | 用途 | 调用时机 |
|---|---|---|
dma_map_single() |
映射缓冲区并执行必要的 Cache 刷回/失效 | DMA 传输开始前 |
dma_unmap_single() |
解除映射并同步 Cache | DMA 传输完成后 |
dma_sync_single_for_cpu() |
将缓冲区所有权交还给 CPU(Cache 失效) | CPU 读取 DMA 写入的数据前 |
dma_sync_single_for_device() |
将缓冲区所有权交给设备(Cache 刷回) | CPU 写入数据供 DMA 读取前 |
实战要点:在 videobuf2 框架中,
vb2_dma_contig 和 vb2_dma_sg 内存分配器内部已经封装了上述 DMA API 的调用。驱动开发者只需正确实现 buf_prepare 和 buf_finish 回调,框架会自动处理 Cache 一致性。