linux dmaengine编程

开发板:A33,运行linux-3.4.39

主机:Ubuntu 14.04

----------------------------------------------

 

DMA是Direct Memory Access的缩写,顾名思义,就是绕开CPU直接访问memory的意思。在计算机中,相比CPU,memory和外设的速度是非常慢的,因而在memory和memory(或者memory和设备)之间搬运数据,非常浪费CPU的时间,造成CPU无法及时处理一些实时事件。因此,工程师们就设计出来一种专门用来搬运数据的器件----DMA控制器,协助CPU进行数据搬运。

DMA传输可以是内存到内存、内存到外设和外设到内存。这里的代码通过dma驱动实现了内存到内存的数据传输。linux实现了DMA框架,叫做DMA Engine,内核驱动开发者必须按照固定的流程编码才能正确的使用DMA。

 

1. DMA用法包括以下的步骤: 

1)分配一个DMA通道; 

dma_request_channel()

2)设置controller特定的参数; 

none

3)获取一个传输描述符;

device_prep_dma_memcpy() 

4)提交传输描述符; 

tx_submit();

5)dma_async_issue_pending()

 

2. 测试:

1)交叉编译成ko模块,下载到A33开发板

2)加载模块:insmod dma.ko

3)执行:cat /dev/dma_test

执行此操作会产生一次DMA请求,将src内存数据复制到dst内存区域,复制完后会调用回调函数,复制过程中不需要CPU的参与。

注:在Ubuntu下dma_request_channel()失败,原因未知,所以转到A33下测试。

 

3. 其它

申请DMA缓冲区:

void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag);

该函数实际获得两个地址, 

1、函数的返回值是一个 void *,代表缓冲区的内核虚拟地址 

2、相关的总线地址(物理地址),保存在dma_handle中

物理地址和虚拟地址存在映射关系。DMA传输使用物理地址,而CPU操作的是虚拟地址。

如在tx = dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, 512, flags);使用了物理地址

在dma_callback_func()中使用了虚拟地址。

使用DMA传输数据必须要基于DMA缓冲区,不能使用其他的内存区域,如kmalloc开辟的内存。

 

4. 源码dma.c

/*
Function description:When we call dmatest_read(),it will transmit src memory data
to dst memory,then print dst memory data by dma_callback_func(void) function.
*/
#include
#include
#include
#include

#include
#include
#include

#include
#include
#include
#include

#include



#define DEVICE_NAME "dma_test"

unsigned char dmatest_major;
static struct class *dmatest_class;

struct dma_chan *chan;
 //bus address
dma_addr_t dma_src;
dma_addr_t dma_dst;
//virtual address
char *src = NULL;
char *dst = NULL ;
struct dma_device *dev;
struct dma_async_tx_descriptor *tx = NULL;
enum dma_ctrl_flags flags;
dma_cookie_t cookie;


//When dma transfer finished,this function will be called.
void dma_callback_func(void)
{
	int i;
	printk("dma transfer ok.\n");

	for (i = 0; i < 512; ){
		printk("dst[%d]:%c ", i, dst[i]);
		i += 10;
	}
	printk("\n");
}

int dmatest_open(struct inode *inode, struct file *filp)
{
	return 0;
}

int dmatest_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static ssize_t dmatest_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	int ret = 0;
	//alloc a desc,and set dst_addr,src_addr,data_size.
	tx = dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, 512, flags);
	if (!tx){
		printk("Failed to prepare DMA memcpy");
	}
	
	tx->callback = dma_callback_func;//set call back function
	tx->callback_param = NULL;
	cookie = tx->tx_submit(tx); //submit the desc
	if (dma_submit_error(cookie)){
		printk("Failed to do DMA tx_submit");
	}
	
	printk("begin dma transfer.\n");	
	dma_async_issue_pending(chan);	//begin dma transfer
	
	return ret;
}

static ssize_t dmatest_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	int ret = 0;
	return ret;
}

static const struct file_operations dmatest_fops = {
	.owner = THIS_MODULE,
	.read = dmatest_read,
	.write = dmatest_write,
	.open = dmatest_open,
	.release = dmatest_release,
};


int dmatest_init(void)
{
	int i;
	dma_cap_mask_t mask;
	
	//the first parameter 0 means allocate major device number automatically
	dmatest_major = register_chrdev(0, DEVICE_NAME, &dmatest_fops);
	if (dmatest_major < 0) 
		return dmatest_major;
	//create a dmatest class
	dmatest_class = class_create(THIS_MODULE,DEVICE_NAME);
	if (IS_ERR(dmatest_class))
		return -1;
	//create a dmatest device from this class
	device_create(dmatest_class,NULL,MKDEV(dmatest_major,0),NULL,DEVICE_NAME);
	printk("device node create ok.\n");

	//alloc 512B src memory and dst memory
	src = dma_alloc_coherent(NULL, 512, &dma_src, GFP_KERNEL);
	printk("src = 0x%x, dma_src = 0x%x\n",src, dma_src);
	
	dst = dma_alloc_coherent(NULL, 512, &dma_dst, GFP_KERNEL);
	printk("dst = 0x%x, dma_dst = 0x%x\n",dst, dma_dst);
		
	for (i = 0; i < 512; i++){
		*(src + i) = 'a';
	}

	dma_cap_zero(mask);
	dma_cap_set(DMA_MEMCPY, mask);	//direction:memory to memory
	chan = dma_request_channel(mask,NULL,NULL); 	//request a dma channel
	if(chan)
		printk("dma channel id = %d\n",chan->chan_id);
	else{
		printk("dma_request_channel faild.\n");
		return -1;
	}
	
	flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
	dev = chan->device;
	
	return 0;
}

void dmatest_exit(void)
{
	unregister_chrdev(dmatest_major,DEVICE_NAME);//release major device number
	device_destroy(dmatest_class,MKDEV(dmatest_major,0));//destroy globalmem device
	class_destroy(dmatest_class);//destroy globalmem class
	
	//free memory and dma channel
	dma_free_coherent(NULL, 512, src, &dma_src);
	dma_free_coherent(NULL, 512, dst, &dma_dst);
	
	dma_release_channel(chan);
}


module_init(dmatest_init);
module_exit(dmatest_exit);

MODULE_LICENSE("GPL");

 

 

 

 

你可能感兴趣的:(Linux,Embeded,Software,Development,Linux,Application)