您的位置:首页 > 运维架构 > Linux

Linux驱动之DMA

2015-10-14 11:25 471 查看
上代码之前说一点自己的总结:不能用kmalloc,因为 kmalloc分配的物理地址有可能是不连续的,dma不能识别   DMA负责读取数据,读取的过程和CPU无关,当读取完了产生一次中断,让CPU来处理数据这样大大节省了时间,而不是让CPU去负责读取数据。DMA在产品效率开发过程中用的比较多,当你的CPU忙不过来的时候后你可以使用DMA,像读取数据,解析数据这些完全可以让DMA去做,CPU只来处理读出来的指令就行,免得CPU既要去读取
数据还得处理数据,这样效率不是很高。还有代码有可能你看不懂,但是没有关系,你结合你的Linux内核版本,不懂得函数用sourceInsight去查找。

下面有一个测试程序来教大家怎么使用DMA,由于个人能力有限,如果读者发现了那些错误,望请指正。

头文件根据自己内核版本添加,这个问题很好解决,我相信难不倒大家。

static char *src;                                         /*分配的SRC, 对应的缓冲区*/

static u32 src_phys;                                  /*物理地址*/

static char *dst;                        //分配的dst, 对应的缓冲区

static u32 dst_phys;                       //物理地址

static volatile struct s3c_dma_regs *dma_regs;

static DECLARE_WAIT_QUEUE_HEAD(dma_waitq);

/* 中断事件标志, 中断服务程序将它置1,ioctl将它清0 */

static volatile int ev_dma = 0;

static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

{
int i;

memset(src, 0xAA, BUF_SIZE);//设置缓冲区的地址
memset(dst, 0x55, BUF_SIZE);//设置缓冲区的地址

switch (cmd)
{
case MEM_CPY_NO_DMA :
{
for (i = 0; i < BUF_SIZE; i++)
dst[i] = src[i];
if (memcmp(src, dst, BUF_SIZE) == 0)
{
printk("MEM_CPY_NO_DMA OK\n");
}
else
{
printk("MEM_CPY_DMA ERROR\n");
}
break;
}

case MEM_CPY_DMA :
{
ev_dma = 0;

/* 把源,目的,长度告诉DMA */
dma_regs->disrc      = src_phys;        /* 源的物理地址 */
dma_regs->disrcc     = (0<<1) | (0<<0); /* 源位于AHB总线, 源地址递增 */
dma_regs->didst      = dst_phys;        /* 目的的物理地址 */
dma_regs->didstc     = (0<<2) | (0<<1) | (0<<0); /* 目的位于AHB总线, 目的地址递增 */
dma_regs->dcon       = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(BUF_SIZE<<0);  /* 使能中断,单个传输,软件触发, */

/* 启动DMA */
dma_regs->dmasktrig  = (1<<1) | (1<<0);

/* 如何知道DMA什么时候完成? */
/* 休眠 */
wait_event_interruptible(dma_waitq, ev_dma);

if (memcmp(src, dst, BUF_SIZE) == 0)
{
printk("MEM_CPY_DMA OK\n");
}
else
{
printk("MEM_CPY_DMA ERROR\n");
}

break;
}
}

return 0;

}

static struct file_operations dma_fops = {     /* 驱动的固定框架,给函数指针赋值,自己构造相应的函数 */
.owner  = THIS_MODULE,
.ioctl  = s3c_dma_ioctl,

};

static irqreturn_t s3c_dma_irq(int irq, void *devid)                             //DMA数据传输完成就触发这个中断

{
/* 唤醒 */
ev_dma = 1;

    wake_up_interruptible(&dma_waitq);   /* 唤醒休眠的进程 */
return IRQ_HANDLED;

}

static int s3c_dma_init(void)

{
if (request_irq(IRQ_DMA3, s3c_dma_irq, 0, "s3c_dma", 1))
{
printk("can't request_irq for DMA\n");
return -EBUSY;
}

/* 分配SRC, DST对应的缓冲区 */
src = dma_alloc_writecombine(NULL, BUF_SIZE, &src_phys, GFP_KERNEL);
if (NULL == src)
{
printk("can't alloc buffer for src\n");
free_irq(IRQ_DMA3, 1);
return -ENOMEM;
}

dst = dma_alloc_writecombine(NULL, BUF_SIZE, &dst_phys, GFP_KERNEL);
if (NULL == dst)
{
free_irq(IRQ_DMA3, 1);
dma_free_writecombine(NULL, BUF_SIZE, src, src_phys);
printk("can't alloc buffer for dst\n");
return -ENOMEM;
}

   dma_regs = ioremap(DMA3_BASE_ADDR, sizeof(struct s3c_dma_regs));

}

static void s3c_dma_exit(void)

{
iounmap(dma_regs);
unregister_chrdev(major, "s3c_dma");
dma_free_writecombine(NULL, BUF_SIZE, src, src_phys);
dma_free_writecombine(NULL, BUF_SIZE, dst, dst_phys);
free_irq(IRQ_DMA3, 1);

}

这个驱动程序不是很完整,但是DMA所使用到的功能应该基本讲解完了,具体在项目中怎么使用还得依靠你的聪明才智了,这篇文章只起到引导的作用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: