二十二、DMA驱动

时间:2022-10-10 08:11:20

一、DMA简介

DMA(Direct Memory Access,直接内存存取),DMA传输将数据从一个地址空间复制到另外一个地址空间。传输过程由DMA控制器独立完成,它并没有拖延CPU的工作,可以让CPU效率提高。

既然DMA用于传输,那么就需要具备传输三要素:源、目的、长度。在传输完成后,DMA会通过产生中断的方式汇报。

由于DMA不使用页表机制,因此必须分配连续的物理内存,这一点需要我们注意,我们可以使用dma_alloc_writecombine()或dma_alloc_coherent()。dma_alloc_coherent()分配的内存不使用缓存,也不会使用写缓冲区,性能较差,dma_alloc_writecombine()不使用缓存,使用写缓冲区。

我们来看4412数据手册Direct Memory Access Controller一章。

二十二、DMA驱动

可以看到4412上有3个DMA控制器,图中DMA是支持内存到内存的DMA控制器,DMA0和DMA1是同时支持外设到内存和内存到外设的DMA控制器。

那么为什么图中把DMA单独分类,DMA0和DMA1一类呢?

从方向上来说,DMA传输可以分为4类:memory到memory、memory到device、device到memory和device到device。从CPU的角度看,device都是slave,因此将这些有device参与的传输分为一类,称为Slave-DMA传输。而另一种memory到memory的传输,被称为Async TX。

读者若希望了解DMA传输更多信息,可参考:32.Linux-2440下的DMA驱动(详解)

二、DMA Engine介绍和DMA设备驱动步骤

DMA驱动框架定义在drivers/dma/dmaengine.c中,整体关系如下图。下面介绍DMA需要使用到的概念。

二十二、DMA驱动

1. DMA Channels:如下图,一个DMA控制器同时传输的数据个数是有限的,这个限度称为channel

二十二、DMA驱动

2. DMA Request Lines:DMA控制器和DMA传输设备之间需要有多条数据线线(称作DMA request,DRQ

3. 传输描述符:DMA传输属于异步传输,在传输前,slave驱动需要将本次传输的信息(如传输大小、方向等)提交给engine,engine返回描述符

编写DMA的设备驱动一般步骤如下:

1. 使用dma_request_channel()函数申请一个DMA通道

2. 使用dmaengine_slave_config()设置DMA通道参数

3. 使用dmaengine_prep_slave_single()或dmaengine_prep_slave_sg()或dmaengine_prep_dma_cyclic()获取传输描述符

4. 使用dmaengine_submit()将描述符提交到DMA等待队列

5. 使用dma_async_issue_pending()启动传输

6. 等待传输完成

步骤中所使用函数声明如下:

/* 1. 申请一个DMA通道 */
/* mask是使用dma_cap_sets()指定的DMA传输类型
* filter_param是slave ID
* eg:
* dma_cap_mask_t mask;
* dma_cap_zero(mask);
* dma_cap_set(DMA_MEMCPY, mask);
* dma_chan0 = dma_request_channel(mask, 0, NULL);
*/
struct dma_chan *dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param) /* 2. 设置DMA通道参数 */
/* config用于设置DMA通道宽度、数据传输宽带、源和目的等信息 */
int dmaengine_slave_config(struct dma_chan *chan, struct dma_slave_config *config) /* 3. 获取传输描述符 */
struct dma_async_tx_descriptor *dmaengine_prep_slave_single(
struct dma_chan *chan, dma_addr_t buf, size_t len,
enum dma_transfer_direction dir, unsigned long flags) 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) struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction dir) /* 4. 将描述符提交到DMA等待队列 */
dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc) /* 5. 启动传输 */
void dma_async_issue_pending(struct dma_chan *chan)

其中,

1. 传输类型具体列为:

enum dma_transaction_type {
DMA_MEMCPY,
DMA_XOR,
DMA_PQ,
DMA_XOR_VAL,
DMA_PQ_VAL,
DMA_MEMSET,
DMA_INTERRUPT,
DMA_SG,
DMA_PRIVATE,
DMA_ASYNC_TX,
DMA_SLAVE,
DMA_CYCLIC,
DMA_INTERLEAVE,
/* last transaction type for creation of the capabilities mask */
DMA_TX_TYPE_END,
};

2. struct dma_slave_config含有DMA传输所需要的参数

struct dma_slave_config {
enum dma_transfer_direction direction; /* 传输方向,DMA_MEM_TO_MEM、DMA_MEM_TO_DEV等 */
dma_addr_t src_addr; /* 源,读数据的地址 */
dma_addr_t dst_addr; /* 目的,写数据的地址 */
enum dma_slave_buswidth src_addr_width; /* 最大可传输的burst size */
enum dma_slave_buswidth dst_addr_width;
u32 src_maxburst;
u32 dst_maxburst;
bool device_fc; /* 当device是flow controller时,需要设置为true */
};

3. struct dma_async_tx_descriptor是DMA的传输描述符

struct dma_async_tx_descriptor {
dma_cookie_t cookie; /* 用于追踪本次传输 */
enum dma_ctrl_flags flags; /* DMA_CTRL_开头的标志, */
dma_addr_t phys;
struct dma_chan *chan; /* 对应的通道 */
dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx); /* 控制器驱动提供的回调函数 */
dma_async_tx_callback callback; /* 传输完成的回调函数和参数 */
void *callback_param;
#ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
struct dma_async_tx_descriptor *next;
struct dma_async_tx_descriptor *parent;
spinlock_t lock;
#endif
};

4. 对于struct scatterlist,读者可参考:Linux内核scatterlist API介绍

三、DMA设备驱动程序

源代码:

 #include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h> #include <asm/io.h>
#include <asm/uaccess.h> #include <linux/amba/pl330.h>
#include <mach/dma.h> #define BUF_SIZE 512
#define PL_NO_DMA _IOW('M', 0x1, int) /* magic num */
#define PL_USE_DMA _IOW('M', 0x2, int) static char *src = NULL;
static char *dst = NULL;
static dma_addr_t dma_src;
static dma_addr_t dma_dst;
static enum dma_ctrl_flags flags;
static dma_cookie_t cookie;
static struct dma_chan *chan0 = NULL;
static struct dma_device *dev0 = NULL;
static struct dma_async_tx_descriptor *tx0 = NULL; static void dma_callback(void *dma_async_param)
{
if ( == memcmp(src, dst, BUF_SIZE))
printk("PL_USE_DMA succeed\n");
else
printk("PL_USE_DMA error\n");
} static int pl330_dma_open(struct inode *inode, struct file *filp)
{
return ;
} static long pl330_dma_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int i = ; memset(src, 0xAA, BUF_SIZE);
memset(dst, 0xBB, BUF_SIZE); switch (cmd)
{
case PL_NO_DMA:
for (i = ; i < BUF_SIZE; i++)
dst[i] = src[i]; if ( == memcmp(src, dst, BUF_SIZE))
printk("PL_NO_DMA succeed\n");
else
printk("PL_NO_DMA error\n");
break; case PL_USE_DMA:
flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
dev0 = chan0->device;
tx0 = dev0->device_prep_dma_memcpy(chan0, dma_dst, dma_src, BUF_SIZE, flags);
if (!tx0)
printk("device_prep_dma_memcpy error\n"); tx0->callback = dma_callback;
tx0->callback_param = NULL;
cookie = tx0->tx_submit(tx0);
if (dma_submit_error(cookie))
printk("tx_submit error\n"); dma_async_issue_pending(chan0);
break; default:
break;
} return ;
} static int pl330_dma_release(struct inode *inode, struct file *filp)
{
return ;
} static struct file_operations pl330_dma_fops = {
.open = pl330_dma_open,
.unlocked_ioctl = pl330_dma_ioctl,
.release = pl330_dma_release,
}; static struct miscdevice dma_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "dma_test",
.fops = &pl330_dma_fops,
}; extern bool pl330_filter(struct dma_chan *chan, void *param);
extern void msleep(unsigned int msecs); static int __init pl330_dma_init(void)
{
int ret = misc_register(&dma_misc);
if (ret < ) {
printk("misc_register error\n");
return -EINVAL;
} src = dma_alloc_writecombine(NULL, BUF_SIZE, &dma_src, GFP_KERNEL);
dst = dma_alloc_writecombine(NULL, BUF_SIZE, &dma_dst, GFP_KERNEL); dma_cap_mask_t mask;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask); chan0 = dma_request_channel(mask, pl330_filter, NULL);
if (NULL == chan0){
msleep();
chan0 = dma_request_channel(mask, NULL, NULL);
} if (NULL == chan0)
printk("dma_request_channel error\n"); return ;
} static void __exit pl330_dma_exit(void)
{
dma_release_channel(chan0);
dma_free_writecombine(NULL, BUF_SIZE, src, dma_src);
dma_free_writecombine(NULL, BUF_SIZE, dst, dma_dst);
misc_deregister(&dma_misc);
} module_init(pl330_dma_init);
module_exit(pl330_dma_exit); MODULE_LICENSE("GPL");

Makefile:

 KERN_DIR = /work/itop4412/tools/linux-3.5

 all:
make -C $(KERN_DIR) M=`pwd` modules clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order obj-m += pl330dma.o

测试文件:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h> #define PL_NO_DMA _IOW('M', 0x1, int) /* magic num */
#define PL_USE_DMA _IOW('M', 0x2, int) int main(void)
{
int fd;
fd = open("/dev/dma_test", O_RDWR);
if (fd < ) {
printf("can't open /dev/dma_test\n");
return -;
} ioctl(fd, PL_NO_DMA, );
sleep(); ioctl(fd, PL_USE_DMA, );
sleep(); return ;
}

在测试之前,我们需要确定内核中是否配置了DMA框架:

Device Drivers --->
[*] DMA Engine support --->
[*] DMA Engine debugging
<*> DMA API Driver for PL330
[*] Async_tx: Offload support for the async_tx api

测试:

在编译并在开发板上insmod后执行测试文件,可以看到设备驱动printk()输出。

下一章  二十三、uevnet机制和U盘自动挂载

二十二、DMA驱动的更多相关文章

  1. &lbrack;分享&rsqb; IT天空的二十二条军规

    Una 发表于 2014-9-19 20:25:06 https://www.itsk.com/thread-335975-1-1.html IT天空的二十二条军规 第一条.你不是什么都会,也不是什么 ...

  2. Bootstrap &lt&semi;基础二十二&gt&semi;超大屏幕(Jumbotron)

    Bootstrap 支持的另一个特性,超大屏幕(Jumbotron).顾名思义该组件可以增加标题的大小,并为登陆页面内容添加更多的外边距(margin).使用超大屏幕(Jumbotron)的步骤如下: ...

  3. Web 前端开发精华文章推荐(HTML5、CSS3、jQuery)【系列二十二】

    <Web 前端开发精华文章推荐>2014年第一期(总第二十二期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML ...

  4. 二十二、OGNL的一些其他操作

    二十二.OGNL的一些其他操作 投影 ?判断满足条件 动作类代码: ^ $   public class Demo2Action extends ActionSupport {     public ...

  5. WCF技术剖析之二十二&colon; 深入剖析WCF底层异常处理框架实现原理&lbrack;中篇&rsqb;

    原文:WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇] 在[上篇]中,我们分别站在消息交换和编程的角度介绍了SOAP Fault和FaultException异常.在服务执行过 ...

  6. VMware vSphere 服务器虚拟化之二十二桌面虚拟化之创建View Composer链接克隆的虚拟桌面池

    VMware vSphere 服务器虚拟化之二十二桌面虚拟化之创建View Composer链接克隆的虚拟桌面池 在上一节我们创建了完整克隆的自动专有桌面池,在创建过程比较缓慢,这次我们将学习创建Vi ...

  7. Bootstrap入门(二十二)组件16:列表组

    Bootstrap入门(二十二)组件16:列表组 列表组是灵活又强大的组件,不仅能用于显示一组简单的元素,还能用于复杂的定制的内容. 1.默认样式列表组 2.加入徽章 3.链接 4.禁用的列表组 5. ...

  8. JAVA之旅(二十二)——Map概述&comma;子类对象特点,共性方法,keySet,entrySet,Map小练习

    JAVA之旅(二十二)--Map概述,子类对象特点,共性方法,keySet,entrySet,Map小练习 继续坚持下去吧,各位骚年们! 事实上,我们的数据结构,只剩下这个Map的知识点了,平时开发中 ...

  9. 备忘录模式 Memento 快照模式 标记Token模式 行为型 设计模式(二十二)

    备忘录模式 Memento   沿着脚印,走过你来时的路,回到原点.     苦海翻起爱恨   在世间难逃避命运   相亲竟不可接近   或我应该相信是缘份   一首<一生所爱>触动了多少 ...

  10. 二十二&period; Python基础&lpar;22&rpar;--继承

    二十二. Python基础(22)--继承 ● 知识框架   ● 继承关系中self的指向 当一个对象调用一个方法时,这个方法的self形参会指向这个对象 class A:     def get(s ...

随机推荐

  1. tornado template

    若果使用Tornado进行web开发可能会用到模板功能,页面继承,嵌套... 多页应用模板的处理多半依赖后端(SPA就可以动态加载局部视图),就算是RESTfull的API设计,也不妨碍同时提供部分模 ...

  2. 配置 L2 Population - 每天5分钟玩转 OpenStack(114)

    前面我们学习了L2 Population 的原理,今天讨论如何在 Neutron 中配置和启用此特性. 目前 L2 Population 支持 VXLAN with Linux bridge 和 VX ...

  3. iOS同一项目多个Target的快速实现方法

    之前写过这种场景下的项目,比如类似滴滴的司机端和乘客端,学生端和教师端等等.. 最近有人问我,就整理记录如下: 1.正常新建一个项目,完成后  如下: 2.这一步就是添加一个新的target 3.添加 ...

  4. 8天学通MongoDB——第二天 细说增删查改

    原文地址:http://www.cnblogs.com/huangxincheng/archive/2012/02/19/2357846.html 看过上一篇,相信大家都会知道如何开启mongodb了 ...

  5. cocos2dx json数据解析

    转自:http://blog.csdn.net/wangbin_jxust/article/details/9707873 cocos2dx本身没有json解析类库,我们这里引入libjson进行解析 ...

  6. JavaScript中的Date

    Date 对象用于处理日期和时间. var myDate=new Date() Date 对象会自动把当前日期和时间保存为其初始值. Date常用方法有: myDate.getYear(); //获取 ...

  7. 汉企C&num;面向对象——继承Practice

    class Dianqi //创建电器类:父类 { private string _Dianqimingzi; public string Dianqimingzi { get { return _D ...

  8. C&num;设置IP地址,启用禁用适配器

    界面效果图如下: 报表界面 说下关键代码 需要开启 Windows Management Instrumentation服务(默认已经开启),在程序中需要增加 Management引用. 主要有Net ...

  9. AJAX同步和异步的区别

    function paginationGo(page){ sendata = {"page":page}; $.ajax({ type:"POST", url: ...

  10. Charles抓取https请求详解

    大家好,我是TT,互联网测试行业多年,没有牛逼的背景,也没有什么可炫耀的,唯独比他人更努力,在职场打拼.遇到过的坑,走过的弯路,愿意与大家分享,分享自己的经验,少走弯路.首发于个人公众号[测试架构师] ...