Linux之DMA API -- 通用设备的动态DMA映射(一)

2014-11-24 10:38:54 · 作者: · 浏览: 0

by JHJ(jianghuijun211@gmail.com)


本文描述DMA API。更详细的介绍请参看Documentation/DMA-API-HOWTO.txt。


API分为两部分,第一部分描述API,第二部分描述可以支持非一致性内存机器的扩展API。你应该使用第一部分所描述的API,除非你知道你的驱动必须要支持非一致性平台。


为了可以引用DMA API,你必须 #include


1-1 使用大块DMA一致性缓冲区(dma-coherent buffers)


void *
dma_alloc_coherent
(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)


一致性内存:设备对一块内存进行写操作,处理器可以立即进行读操作,而无需担心处理器高速缓存(cache)的影响。同样的,处理器对一块内存进行些操作,设备可以立即进行读操作。(在告诉设备读内存时,你可能需要确定刷新处理器的写缓存。)


此函数申请一段大小为size字节的一致性内存,返回两个参数。一个是dma_handle,它可以用作这段内存的物理地址。 另一个是指向被分配内存的指针(处理器的虚拟地址)。


注意:由于在某些平台上,使用一致性内存代价很高,比如最小的分配长度为一个页。因此你应该尽可能合并申请一致性内存的请求。最简单的办法是使用dma_pool函数调用(详见下文)。


参数flag(仅存在于dma_alloc_coherent中)运行调用者定义申请内存时的GFP_flags(详见kmalloc)。


void *
dma_zalloc_coherent
(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)


对dma_alloc_coherent()的封装,如果内存分配成功,则返回清零的内存。


void
dma_free_coherent
(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t dma_handle)


释放之前申请的一致性内存。dev, size及dma_handle必须和申请一致性内存的函数参数相同。cpu_addr必须为申请一致性内存函数的返回虚拟地址。


注意:和其他内存分配函数不同,这些函数必须要在中断使能的情况下使用。


1-2 使用小块DMA一致性缓冲区


如果要使用这部分DMA API,必须#include


许多驱动程序需要为DMA描述符或者I/O内存申请大量小块DMA一致性内存。你可以使用DMA 内存池,而不是申请以页为单位的内存块或者调用dma_alloc_coherent()。这种机制有点像struct kmem_cache,只是它利用了DMA一致性内存分配器,而不是调用 __get_free_pages()。同样地,DMA 内存池知道通用硬件的对齐限制,比如队列头需要N字节对齐。


struct dma_pool *
dma_pool_create
(const char *name, struct device *dev,
size_t size, size_t align, size_t alloc);


create( )函数为设备初始化DMA一致性内存的内存池。它必须要在可睡眠上下文调用。


name为内存池的名字(就像struct kmem_cache name一样)。dev及size就如dma_alloc_coherent()参数一样。align为设备硬件需要的对齐大小(单位为字节,必须为2的幂次方)。如果设备没有边界限制,可以设置该参数为0。如果设置为4096,则表示从内存池分配的内存不能超过4K字节的边界。


void *
dma_pool_alloc
(struct dma_pool *pool, gfp_t gfp_flags,
dma_addr_t *dma_handle);


从内存池中分配内存。返回的内存同时满足申请的大小及对齐要求。设置GFP_ATOMIC可以确保内存分配被block,设置GFP_KERNEL(不能再中断上下文,不会保持SMP锁)允许内存分配被block。和dma_alloc_coherent()一样,这个函数会返回两个值:一个值是cpu可以使用的虚拟地址,另一个值是内存池设备可以使用的dma物理地址。


void
dma_pool_free
(struct dma_pool *pool, void *vaddr,
dma_addr_t addr);


返回内存给内存池。参数pool为传递给dma_pool_alloc()的pool,参数vaddr及addr为dma_pool_alloc()的返回值。


void
dma_pool_destroy
(struct dma_pool *pool);


内存池析构函数用于释放内存池的资源。这个函数在可睡眠上下文调用。请确认在调用此函数时,所有从该内存池申请的内存必须都要归还给内存池。


1-3 DMA寻址限制


int
dma_supported
(struct device *dev, u64 mask)


用来检测该设备是否支持掩码所表示的DMA寻址能力。比如mask为0x0FFFFFF,则检测该设备是否支持24位寻址。


返回1表示支持,0表示不支持。


注意:该函数很少用于检测是否掩码为可用的,它不会改变当前掩码设置。它是一个内部API而非供驱动者使用的外部API。


int
dma_set_mask
(struct device *dev, u64 mask)


检测该掩码是否合法,如果合法,则更新设备参数。即更新设备的寻址能力。


返回0表示成功,返回负值表示失败。


int
dma_set_coherent_mask
(struct device *dev, u64 mask)


检测该掩码是否合法,如果合法,则更新设备参数。即更新设备的寻址能力。


返回0表示成功,返回负值表示失败。


u64
dma_get_required_mask
(struct device *dev)


该函数返回平台可以高效工作的掩码。通常这意味着返回掩码是可以寻址到所有内存的最小值。检查该值可以让DMA描述符的大小尽量的小。


请求平台需要的掩码并不会改变当前掩码。如果你想利用这点,可以利用改返回值通过dma_set_mask()设置当前掩码。


1-4 流式DMA映射


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


映射一块处理器的虚拟地址,这样可以让外设访问。该函数返回内存的物理地址。


在dma_API中强烈建议使用表示DMA传输方向的枚举类型。


DMA_NONE 仅用于调试目的
DMA_TO_DEVICE 数据从内存传输到设备,可认为是写操作。
DMA_FROM_DEVICE 数据从设备传输到内存,可认为是读操作。
DMA_BIDIRECTIONAL 不清楚传输方向则可用该类型。


请注意:并非一台机器上所有的内存区域都可以用