DMA
基于 DMA 的硬件使用总线地址而非物理地址,总线地址是从设备角度上看到的内存地址,物理地址则是从 CPU 角度上看到的未经转换的内存地址(经过转换的为虚拟地址) 。内核提供了如下函数用于进行简单的虚拟地址/总线地址转换.unsigned long virt_to_bus(volatile void *address);void *bus_to_virt(unsigned long address);设备并不一定能在所有的内存地址上执行 DMA 操作,在这种情况下应该通过下列函数执行 DMA 地址掩码:int dma_set_mask(struct device *dev, u64 mask);例如,对于只能在 24 位地址上执行 DMA 操作的设备而言,就应该调用dma_set_mask (dev , 0xffff ff)。
DMA 映射包括两个方面的工作: 分配一片 DMA 缓冲区; 为这片缓冲区产生设备可访问的地址。
一致 DMA 映射
void *dma_alloc_coherent(struct device *dev, size_tsize, dma_addr_t *handle, gfp_t gfp);返回值为申请到的 DMA 缓冲区的虚拟地址.申请一片 DMA 缓冲区,进行地址映射并保证该缓冲区的Cache 一致性。dma_alloc_coherent()对应的释放函数为:void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_thandle); 以下函数用于分配一个写合并(writecombining)的 DMA 缓冲区:void * dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);
函数dma_alloc_coherent分配size字节的区域的一致内存,得到的dma_handle是指向分配的区域的地址指针,这个地址作为区域的物理基地址。dma_handle是与总线一样的位宽的无符号整数。
流式DMA
使用 dma_map_single()可实现流式 DMA 映射,该函数原型为:dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);如果映射成功,返回的是总线地址,否则,返回 NULL。第 4 个参数为 DMA 的方向 , 可 能的 值 包 括 DMA_TO_DEVICE 、 DMA_FROM_DEVICE 、DMA_BIDIRECTIONAL 和 DMA_NONE。反函数为 dma_unmap_single()。建立流式dma需要指明数据在DMA通道中的流向。关键2点,一是确保cpu侧的虚拟地址所对应的物理地址能够被设备DMA准确访问。2是,确保cache一致性问题。struct dma_map_ops对象中的sync_single_for_cpu, 在驱动访问完 DMA 缓冲区后, 应该将其所有权返还给设备sync_single_for_device, sync_sg_for_cpu, sync_sg_for_device 方法就是处理cache一致性问题。
一个使用 DMA 的设备驱动程序通常会与连接到接口总线上的硬件通讯,这些硬件使用物理地址,而程序代码使用虚拟地址。基于 DMA 的硬件使用总线地址而不是物理地址,有时,接口总线是通过将 I/O 地址映射到不同物理地址的桥接电路连接的。甚至某些系统有一个页面映射方案,能够使任意页面在外围总线上表现为连续的。 当驱动程序需要向一个 I/O 设备(例如扩展板或者DMA控制器)发送地址信息时,必须使用 virt_to_bus 转换,在接受到来自连接到总线上硬件的地址信息时,必须使用 bus_to_virt 了。
浙公网安备 33010602011771号