DMA (Direct Memory Access,直接内存存取)

对DMA内存的使用有3种方式:

1,一致DMA映射

通过dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag)来直接得到一块用于dma的内存,同时得到这一段内存的虚拟地址和总线地址,分别用于CPU和device的访问。 通过这种方式得到的dma内存,开发者不用担心cache的问题,但是要注意在执行DMA操作之前flush write buffer。

2、DMA池

DMA池是一个生成小型,一致性DMA映射的机制。调用dma_alloc_coherent函数获得的映射,可能其最小大小为单个页。如果设备需要的DMA区域比这还小,就是用DMA池。在中定义了DMA池函数:

struct dma_pool *dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t allocation);void dma_pool_destroy(struct dma_pool *pool);

name是DMA池的名字,dev是device结构,size是从该池中分配的缓冲区的大小,,align是该池分配操作所必须遵守的硬件对齐原则(用字节表示),如果allocation不为零,表示内存边界不能超越allocation。比如说传入的allocation是4K,表示从该池分配的缓冲区不能跨越4KB的界限。 在销毁之前必须向DMA池返回所有分配的内存。

void * dma_pool_alloc(sturct dma_pool *pool, int mem_flags, dma_addr_t *handle);void dma_pool_free(struct dma_pool *pool, void *addr, dma_addr_t addr);3,流式DMA映射

先通过kmalloc, get_free_pages等得到一段物理连续的内存空间(注意,除非目标平台有IOMMU,否则必须要求物理地址连续,即vmalloc分配得到的内存空间不能用于DMA操作。) 然后使用dma_map_single, dma_map_pages, dma_map_sg将之前分配的内存空间映射,得到总线地址,使之能被device访问。 这种方式不保证cache的一致性,需要开发者手动处理(调用dma_sync_single_for_cpu/device函数?); 另外,必须保证内存的虚拟地址空间边界与cache line length对齐,因cache line length不确定,所以一般选择page 对齐。

在某些体系结构中,流式映射也能够拥有多个不连续的页和多个“分散/聚集”缓冲区。建立流式映射时,必须告诉内核数据流动的方向。 DMA_TO_DEVICE DEVICE_TO_DMA 如果数据被发送到设备,使用DMA_TO_DEVICE;而如果数据被发送到CPU,则使用DEVICE_TO_DMA。 DMA_BIDIRECTTONAL 如果数据可双向移动,则使用该值 DMA_NONE 该符号只是出于调试目的。 当只有一个缓冲区要被传输的时候,使用下函数映射它:

dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);

返回值是总线地址,可以把它传递给设备;如果执行错误,返回NULL。 当传输完毕后,使用下函数删除映射:

void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma-data_direction direction);

使用流式DMA的原则: 一是缓冲区只能用于这样的传送,即其传送方向匹配与映射时给定的方向值; 二是一旦缓冲区被映射,它将属于设备,不是处理器。直到缓冲区被撤销映射前,驱动程序不能以任何方式访问其中的内容。只用当dma_unmap_single函数被调用后,显示刷新处理器缓存中的数据,驱动程序才能安全访问其中的内容。 三是在DMA出于活动期间内,不能撤销对缓冲区的映射,否则会严重破坏系统的稳定性。 如果要映射的缓冲区位于设备不能访问的内存区段(高端内存),怎么办?一些体系结构只产生一个错误,但是其他一些系统结构件创建一个回弹缓冲区。回弹缓冲区就是内存中的独立区域,它可被设备访问。如果使用DMA_TO_DEVICE标志映射缓冲区,并且需要使用回弹缓冲区,则在最初缓冲区中的内容作为映射操作的一部分被拷贝。很明显,在拷贝后,最初缓冲区内容的改变对设备不可见。同样DEVICE_TO_DMA回弹缓冲区被dma_unmap_single函数拷贝回最初的缓冲区中,也就是说,直到拷贝操作完成,来自设备的数据才可用。 有时候,驱动程序需要不经过撤销映射就访问流式DMA缓冲区的内容,为此内核提供了如下调用:

void dma_sync_single_for_cpu(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_directction direction);

应该在处理器访问流式DMA缓冲区前调用该函数。一旦调用了该函数,处理器将“拥有”DMA缓冲区,并可根据需要对它进行访问。然后在设备访问缓冲区前,应该调用下面的函数将所有权交还给设备:

void dma_sync_single_for_device(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);

再次强调,处理器在调用该函数后,不能再访问DMA缓冲区了。

一致DMA映射具有更长的生命周期,它在driver的整个生命周期内都有效,且不用关心cache效应。 流式DMA映射则只在driver填充完要传输的内容到device完成传输这段时间内有效(理论上是从map到unmap,但有效时间如前所述),凡使用流式DMA映射的内存区域在map之后,就只对device有效,driver在unmap之前不能在读写这一段内存区域。或者使用dma_sync_single_for_cpu由cpu获得读写权利,然后driver可对其进行读写。

可是却依旧为对方擦去嘴角的油渍。

DMA (Direct Memory Access,直接内存存取)

相关文章:

你感兴趣的文章:

标签云: