1. 概述
DMA(Direct Memory Access)是计算机系统中除了CPU之外可以主动访问计算机系统内存的硬件部件,DMA的主要功能搬移内存数据而不用通过CPU核。
有些CPU会设计通用DMA设备给各外设使用,例如嵌入式CPU的一些低速外设,自身没有设计DMA,为了数据传输的高效,则需要借用通用DMA设备。
对于大多数的高速外设,因其业务特性,是会自己实现相应的DMA组件的,以追求业务最佳性能。
针对通用DMA设备,Linux系统中提供了统一的DMA驱动框架——DMA子系统。
2. DMA类型
DMA从不同的角度可以进行不同的分类
2.1 DMA mapping方式分类
从DMA mapping方式可以分为2类
- Coherent DMA(一致性DMA)
Coherent DMA访问内存地址时不过cache,是cache-coherence设备,采用Consistent mapping的API进行内存申请
- Streaming DMA(流式DMA)
Streaming DMA在访问内存地址时经过cache,是non-coherence设备,通常采用streaming mapping的API进行内存申请,在单次DMA传输时进行map,在传输完成后进行unmap
2.2 DMA工作方式分类
从DMA工作方式可以分为2类
- Block DMA
硬件DMA设计为一次访问操作需要连续内存地址空间
- Scatter-Gather DMA
硬件DMA设计为一次访问操作可以访问多个离散的、不连续的内存地址空间,并将不连续地址空间的进行数据组合
两种类型的DMA明显差异是软件配置给DMA的地址格式不同,Block DMA只需要在寄存器中实现地址寄存器和数据长度寄存器即可;但Scatter-Gather DMA需要采用描述符表的方式,描述符表中每一个条目是各离散地址和数据长度,寄存器只需要实现描述符表的基址寄存器。
3. Linux DMA子系统
3.1 DMA子系统框架
DMA子系统是一个相对比较简单的子系统,整个框架比较薄,Linux下DMA子系统的框架图见下图蓝色部分
Linux DMA子系统主要有有5部分组成
- dmaengine:是DMA子系统的核心,为DMA Device Driver提供DMA设备注册的API,为DMA调用者(Device Driver)提供屏蔽DMA设备实现细节的统一接口API
- virt-dma:为DMA子系统提供虚拟DMA channel的支持
- of-dma:为DMA子系统提供设备树描述DMA信息传入的支持
- DMA Device Driver:为DMA设备的驱动程序
- dmatest:提供dmatest模块测试DMA memcpy, memset, XOR和RAID6 P+Q操作各种长度和各种偏移量进入源和目标缓冲区
3.2 DMA子系统重要API
此处主要介绍DMA系统为上、下游提供的API,dma-mapping的API此处不做介绍
3.2.1 Register API
1)dma_async_device_register
将DMA设备驱动注册到dma子系统
int dma_async_device_register(struct dma_device *device)
2)dma_async_device_unregister
将DMA设备驱动从dma子系统注销
void dma_async_device_unregister(struct dma_device *device)
3.2.2 DMA调用者(Device Driver)API
1)dma_request_chan
查找并返回与设备关联的设备名为name的DMA通道,这种关联是通过设备树描述实现的。
通过该接口申请的channel是独占的,直到使用dma_release_channel()释放为止。
struct dma_chan *dma_request_chan(struct device *dev, const char *name)
2)dmaengine_slave_config
传递指定的信息给DMA驱动,这些信息大部分已经集成在struct dma_slave_config中赋值
如果需要传递更多的信息,可以将struct dma_slave_config内嵌到DMA设备的指定结构体中,这种就可以传递更多参数了
int dmaengine_slave_config(struct dma_chan *chan, struct dma_slave_config *config)
3)dmaengine_prep_slave_sg
以散列表的形式传输数据,在调用dmaengine_prep_slave_sg()之前需要使用散列表的映射,并且必须在DMA操作完成之前不能释放。如果需要同步,请调用dma_sync_*_for_*()
struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_data_direction direction,
unsigned long flags);
4)dmaengine_submit
把描述符添加到挂起的队列中来
dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
5)dma_async_issue_pending
激活挂起队列中的事务,一旦一个事务完成,队列中的下一个事务就开启,如果设置了回调,还会调用回调以通知完成
void dma_async_issue_pending(struct dma_chan *chan)