mini2440 简单的dma工作原理实验

以驱动方式测试dma的工作原理,用户层程序不停调用ioctl触发驱动里面完成的dma操作,把内存的数据块从一个连续地址搬到另一个连续地址,然后用memcmp测试搬移后的数据和搬移前是不是完全一样。

简单的驱动代码:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/input.h>
#include <linux/dma-mapping.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <mach/regs-gpio.h>


#define		MEM_CPY_NO_DMA		0
#define		MEM_CPY_USE_DMA		1
#define		DMA_BUF_SIZE		(512*1024)
#define		GRH_MODULE_NAME		"grh_dma"

#define		DMA_REG_BASE_ADDR_0		0x4B000000
#define		DMA_REG_BASE_ADDR_1		0x4B000040
#define		DMA_REG_BASE_ADDR_2		0x4B000080
#define		DMA_REG_BASE_ADDR_3		0x4B0000C0


static struct dma_regs{
	volatile unsigned long *disrc;
	volatile unsigned long *disrcc;
	volatile unsigned long *didst;
	volatile unsigned long *didstc;
	volatile unsigned long *dcon;
	volatile unsigned long *dstat;
	volatile unsigned long *dcsrc;
	volatile unsigned long *dcdst;
	volatile unsigned long *dmasktrig;
};

static struct dma_regs dma_regs_ins;


static int major = 0;

static char *src;
static char *dst;
static unsigned int src_phys; //数据源的物理地址
static unsigned int dst_phys; //目标位置的物理地址


static struct class *dma_class;
static struct class_device *dma_class_device; 

//dma中断处理的相关数据
static DECLARE_WAIT_QUEUE_HEAD(dma_waitq); //休眠的进程队列头
static volatile int sleep_for_dma; //这个变量为0的时候ioctl函数会休眠,中断里面将其置1,read函数末尾将其设置为0


static int dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){
	int i;
	memset(src, 0xaa, DMA_BUF_SIZE);
	memset(dst, 0x55, DMA_BUF_SIZE);

	switch(cmd){
		case MEM_CPY_NO_DMA:{
			for(i=0; i<DMA_BUF_SIZE; i++)
				dst[i] = src[i];
			if(0 == memcmp(src, dst, DMA_BUF_SIZE))
				printk(KERN_EMERG"dma success!\n");
			else
				printk(KERN_EMERG"dma fail!\n");
			break;
		}
		case MEM_CPY_USE_DMA:{
			*(dma_regs_ins.disrc) = src_phys; //将数据源的物理地址存入disrc寄存器
			*(dma_regs_ins.disrcc) = (0<<0) | (0<<1); //源数据和目的位置是位于主存上的,设置dma的地址递增
			*(dma_regs_ins.didst) = dst_phys; 将目标物理地址存入didst寄存器
			*(dma_regs_ins.didstc) = (0<<0) | (0<<1) | (0<<2); //不使用自动加载,地址递增,目的地址是位于主存中的
			*(dma_regs_ins.dcon) = (1<<30) | (1<<29) | (0<<28) | (1<<27) | (0<<23) | (0<<20) | (DMA_BUF_SIZE<<0); //使能中断,不使用突发传输,dma为软件触发,传输的最小单位是1字节
			//启动dma
			*(dma_regs_ins.dmasktrig) = (1<<1) | (1<<0);

			//休眠进程
			wait_event_interruptible(dma_waitq, sleep_for_dma);
			sleep_for_dma = 0;

			if(0 == memcmp(src, dst, DMA_BUF_SIZE))
				printk(KERN_EMERG"dma success!\n");
			else
				printk(KERN_EMERG"dma fail!\n");

			break;
		}
	}
	return 0;
}


static struct file_operations dma_fops = {
	.owner = THIS_MODULE,
	.ioctl = dma_ioctl,
};


//dma中断处理函数
static irqreturn_t grh_handle_dma_int(int irq, void *dev_id){
	printk(KERN_EMERG"dma interrupt happened!\n");
	sleep_for_dma = 1;
	wake_up_interruptible(&dma_waitq);
	return IRQ_HANDLED;
}

static int dma_init(void){
	unsigned long *p_start;
	unsigned long **pp;
	int i;

	//分配dma使用的缓存空间
	src = dma_alloc_writecombine(NULL, DMA_BUF_SIZE, &src_phys, GFP_KERNEL);
	dst = dma_alloc_writecombine(NULL, DMA_BUF_SIZE, &dst_phys, GFP_KERNEL);
	if(NULL==src){
		printk(KERN_EMERG"allocate buffer for dma error!\n");
		return -ENOMEM;
	}
	if(NULL==dst){
		dma_free_writecombine(NULL, DMA_BUF_SIZE, src, src_phys);
		printk(KERN_EMERG"allocate buffer for dma error!\n");
		return -ENOMEM;
	}

	//进行地址映射
	p_start = (unsigned long*)ioremap(DMA_REG_BASE_ADDR_0, sizeof(struct dma_regs));
	pp = &(dma_regs_ins.disrc);
	for(i=0; i<9; i++){
		*(pp++) = p_start++;
	}
	//申请dma的中断
	if(request_irq(IRQ_DMA0, grh_handle_dma_int, 0, "dma0", 1)){
		printk(KERN_EMERG"request irq for dma error!\n");
		return -ENOMEM;
	}

	//默认会等待dma进行休眠
	sleep_for_dma = 0;


	major = register_chrdev(0, GRH_MODULE_NAME, &dma_fops);
	//create my own device class
	dma_class = class_create(THIS_MODULE, "grh_dma_class");
	//create my device of my own class
	dma_class_device = device_create(dma_class, NULL, MKDEV(major,0), NULL, "grh_dma_device");

	return 0;
}

static void dma_exit(void){
	unregister_chrdev(major, GRH_MODULE_NAME);
	device_unregister(dma_class_device);
	class_destroy(dma_class);
	iounmap(dma_regs_ins.disrc);
	dma_free_writecombine(NULL, DMA_BUF_SIZE, src, src_phys);
	dma_free_writecombine(NULL, DMA_BUF_SIZE, dst, dst_phys);
	free_irq(IRQ_DMA0, 1); //释放dma中断
}

module_init(dma_init);
module_exit(dma_exit);

MODULE_AUTHOR("GRH");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("DMA DRIVER");
MODULE_LICENSE("GPL");

//用户层测试程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>


#define		MEM_CPY_NO_DMA		0
#define		MEM_CPY_USE_DMA		1

void print_usage(const char *command){
	printf("usage: %s <nodma|dma>\n", command);
}

int main(int argc, char *argv[]){
	int count;
	int fd;

	fd = open("/dev/grh_dma_device", O_RDWR);
	if(fd < 0){
		printf("open device error!\n");
		return -1;
	}

	if(2 != argc){
		printf("command format error!\n");
		print_usage(argv[0]);
		return -1;
	}

	if(0 == strcmp(argv[1],"nodma")){
		while(1){
			ioctl(fd, MEM_CPY_NO_DMA);
		}
	}
	else if(0 == strcmp(argv[1],"dma")){
		while(1){
			ioctl(fd, MEM_CPY_USE_DMA);
		}
	}
	else{
		printf("parameter error!\n");
		print_usage(argv[0]);
		return -1;
	}

	close(fd);
	return 0;
}

运行结果:使用dma时和不使用dma时数据搬移的效率明显加快,cpu消耗量锐减,系统响应速度提升。



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值