在Linux当中有一个专门处理DMA的框架,叫做dmaengine,它的代码实现在drivers/dma/dmaengine.c。这个文件主要是提供一套DMA使用的抽象层,但是封装的也比较简单。下面,我主要讲讲做一个Linux的dma驱动,在框架上应该注意的事项。
从使用上来讲,通常我们让DMA工作,大概都是5步,我叫做DMA 5步曲。是哪5步呢?
1、dma通道请求,对应Linux API , chan = dma_request_channel(....)
用户通过该函数,可以向DMA框架申请一个DMA通道。
点击(此处)折叠或打开
struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
dma_filter_fn fn, void *fn_param)
{
struct dma_device *device, *_d;
struct dma_chan *chan = NULL;
/* 从dma_device_list上找到一个合适的dma控制器,并从控制器上获取一个dma channel */
mutex_lock(&dma_list_mutex);
list_for_each_entry_safe(device, _d, &dma_device_list, global_node) {
chan = find_candidate(device, mask, fn, fn_param);
if (!IS_ERR(chan))
break;
chan = NULL;
}
mutex_unlock(&dma_list_mutex);
pr_debug("%s: %s (%s)\n",
__func__,
chan ? "success" : "fail",
chan ? dma_chan_name(chan) : NULL);
return chan;
}
2、dma通道配置, 对应Linux API, dmaengine_slave_config (chan....)
用户通过该函数,可以配置指定通道的参数,比如目的和源地址,位宽,传输方向等。
点击(此处)折叠或打开
static inline int dmaengine_slave_config(struct dma_chan *chan,
struct dma_slave_config *config)
{
// 直接回调dma控制器的device_config函数
if (chan->device->device_config)
return chan->device->device_config(chan, config);
return -ENOSYS;
}
3、dma通道预处理,对应Linux的API,dmaengine_prep_*。
这个预处理函数会比较多,因为DMA支持很多不同类型的处理,比如DEV TO DEV, MEM TO MEM, MEM TO DEV, DEV TO MEM等。但是都会以dmaengine_prep开头,这个API返回做好预处理的DMA TX结构,用于第4步处理。
点击(此处)折叠或打开
static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
enum dma_transfer_direction dir, unsigned long flags)
{
if (!ch