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子系统;
intdma_async_device_register(struct dma_device *device)
2)dma_async_device_unregister

将DMA设备驱动从dma子系统注销;
voiddma_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, constchar *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,unsignedint sg_len, enum dma_data_direction direction,unsignedlong flags);
4)dmaengine_submit

把描述符添加到挂起的队列中来;
dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
5)dma_async_issue_pending

激活挂起队列中的事务,一旦一个事务完成,队列中的下一个事务就开启,如果设置了回调,还会调用回调以通知完成;
voiddma_async_issue_pending(struct dma_chan *chan)
继续阅读
阅读原文