/* 重复了一下以前的代码,对于理解是比较有价值的 ,由于自己电脑联网的原因没有在板子上跑,这里先到这里 */
/*
* 目的:用for循环语句copy内存的数据到内存的另外一个地方;用dma实现同样的功能.
* 对比普通的copy 和 用dma功能的区别.
* 提示:
* 一次dma传输完成之后让进程休眠,然后在中断服务程序中唤醒.
* (对于睡眠理解:app->ioctl->驱动中ioctl执行dma启动,dma控制器就开始搬移数据了,然后睡眠,虽然app睡眠但dmacontroler并没有停)
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#define MEM_CPY_NO_DMA 0
#define MEM_CPY_DMA 1
static char *src;
static u32 src_phyc;
static char *dst;
static u32 dst_phyc;
#define BUFF_SIZE (512 * 1024)
static int major = 0;
static struct class * dma_class;
#define DMA0_BASE_ADDR 0x4B000000
#define DMA1_BASE_ADDR 0x4B000040
#define DMA2_BASE_ADDR 0x4B000080
#define DMA3_BASE_ADDR 0x4B0000c0
struct dma_regs {
unsigned int disrc ;
unsigned int disrcc ;
unsigned int didst ;
unsigned int didstc ;
unsigned int dcon ;
unsigned int dstat ;
unsigned int dcsrc ;
unsigned int dcdst ;
unsigned int dmasktrig;
};
static volatile struct dma_regs * dma_regp = NULL;
static wait_queue_head_t dma_wqh; /* 定义等待队列头变量 */
static volatile int ev_dma = 0 ; /* 中断处理函数将它置为1 */
static int dma_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
int i;
memset(src,0xAA,BUFF_SIZE);
memset(dst,0x55,BUFF_SIZE);
switch(cmd)
{
case MEM_CPY_NO_DMA:
{
for(i = 0 ; i < BUFF_SIZE ; i++)
{
dst[i] = src[i];
}
if(memcmp(src,dst,BUFF_SIZE) == 0)
printk("MEM_CPY_NO_DMA OK! \n");
else
printk("MEM_CPY_NO_DMA ERROR! \n");
break;
}
case MEM_CPY_DMA:
{
/* 设置dma控制寄存器 */
dma_regp->disrc = src_phyc;
dma_regp->disrcc = (0 << 1) | (0 << 0); /* 源在内存,地址递增(如果是如uart等外设需要设为地址固定) */
dma_regp->didst = dst_phyc;
dma_regp->didstc = (0 << 2) | (0 << 1) | (0 << 0); /* TC = 0时产生中断,本例子里不需要自动加载源,目的, 大小参数 */
/*dcon
* [31]: 0 软件触发,故选择Demand mode , 如果是外设如 iis就要用握手模式,req 和 ack引脚要工作;
* [30]: 本实例不用管;
* [29]: 1 使能中断;
* [28]: TSZ , 0 单个传输,其实突发传输应该也可以;
* [27]: 0 但服务方式,这样不会完全霸占总线;
* [26:24]: 本实例不管,本实验是软件实验,和外设没有关系;
* [23]: 0 software request mode;
* [22]: 0 本实验不需要重新加载;
* [21:20]: DSZ , 00 和[28]配合设置(一致);
* [19:0] : TC ,传输次数 总长度 = TC * TSZ(1或4) * DSZ;
*/
//这里修改过,[27]还要仔细思考?设为0未必就会失败, [30]ram-ram的dma操作,ram是在系统的AHB总线上的.
//dma_regp->dcon = (0 << 31) |(1 << 29) |(0 << 27) |(0 << 28) | (0 << 23) |(0 << 20 ) |(BUFF_SIZE);
dma_regp->dcon = (0 << 31) |(1<<30)|(1 << 29) |(1 << 27) |(0 << 28) | (0 << 23) |(0 << 20 ) |(BUFF_SIZE);
/* 启动dma
* 每传输一次就会进行一次dma request,知道数据传完,
* 传输完TC = 0 引起中断,所以需要注册一个中断
*/
dma_regp->dmasktrig = (1<< 1) |(1<<0);
/* dma启动之后就让应用程序休眠了,但是dma控制器还在不停第搬移数据,完成后引起中断,在中断服务程序中再次唤醒该进程 */
wait_event_interruptible(dma_wqh, ev_dma); /* ev_dma = 0则进程睡眠 */
if(memcmp(src,dst,BUFF_SIZE) == 0)
printk("MEM_CPY_DMA OK! \n");
else
printk("MEM_CPY_DMA ERROR! \n");
break;
}
default:
break;
}
return 0;
}
static const struct file_operations dma_operation = {
.owner = THIS_MODULE,
.ioctl = dma_ioctl,
};
static irqreturn_t dma_irq_handler(int irq, void *dev_id)
{
ev_dma = 1; /* ev_dma可不可以不设为1?,自己分析这要看应用程序了*/
wake_up_interruptible(&dma_wqh);
return IRQ_HANDLED;
}
static int __init dma_init(void)
{
int ret;
major = register_chrdev(0, "mydma", &dma_operation);
dma_class = class_create(THIS_MODULE, "DMA_CLASS");
device_create(dma_class, NULL,MKDEV(major,0), NULL, "DMADEV");
/* 由于内核 dma特殊的操作特性,src , src_phyc都是有内核机制给提供的 */
src = dma_alloc_writecombine(NULL, BUFF_SIZE, &src_phyc, GFP_KERNEL);
if(NULL == src)
{
printk("can't alloc buff for src !");
return -ENOMEM;
}
dst = dma_alloc_writecombine(NULL, BUFF_SIZE, &dst_phyc, GFP_KERNEL);
if(NULL == src)
{
printk("can't alloc buff for dst !");
return -ENOMEM;
}
dma_regp = ioremap(DMA3_BASE_ADDR , sizeof(struct dma_regs));
ret = request_irq(IRQ_DMA3, dma_irq_handler, IRQF_TRIGGER_NONE, "dma_irq", NULL);
if(ret)
{
printk(" can't request_irq for DMA \n");
return -EBUSY;
}
init_waitqueue_head(&dma_wqh);
return 0;
}
static void __exit dma_exit(void)
{
free_irq(IRQ_DMA3, NULL);
iounmap(dma_regp);
dma_free_writecombine(NULL,BUFF_SIZE,dst,dst_phyc);
dma_free_writecombine(NULL,BUFF_SIZE,src,src_phyc);
device_destroy(dma_class, MKDEV(major,0));
class_destroy(dma_class);
unregister_chrdev(major, "mydma");
}
module_init(dma_init);
module_exit(dma_exit);
MODULE_LICENSE("GPL");
*********************************************************************************************************************************************
测试程序:
/*
* ./dma_test nodma
* ./dam_test dma
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#define MEM_CPY_NO_DMA 0
#define MEM_CPY_DMA 1
void printt_usage(char *name)
{
printf("Usage:\n");
printf("%s <nodma | dma> <count>\n", name);
}
int main(int argc , char *argv[])
{
int cnt;
int fd;
if(argc != 2)
{
printt_usage(argv[0]);
return -1;
}
fd = open("/dev/dma",O_RDWR);
if(fd < 0)
{
printf("Can't open /dev/dma file \n");
return -1;
}
if(strcmp(argv[1] , "nodma") == 0)
{
while(1)
{
ioctl(fd,MEM_CPY_NO_DMA);
}
}
else if(strcmp(argv[1] , "dma") == 0)
{
while(1)
{
ioctl(fd,MEM_CPY_DMA);
}
}
else
printt_usage(argv[0]);
return 0;
}