草庐IT

DMA实践3:dmaengine的实验

千册 2025-01-03 原文

前言


        本次是第三篇。

        第一篇,写一个通用框架,做到拿来就能用。

        第二篇,实现mmap功能,内核中的read_buf和write_buf都映射到用户空间,然后呢。写read_buf和write_buf的最后一个字节为‘R’和'W',然后再release函数中打印这两个字节。更加复杂的验证,根据需要自行添加,写的太复杂,意义不大。

        第三篇,通过测试app,控制复制src_buf到dst_buf,复制方式可以使用DMA引擎和memcpy,并计算复制过程中消耗的微秒数,并在测试app中验证复制是否准确,尽最大努力保证整个流程的准确无误。

一 dmaengine标准API

        Linux内核目前推荐使用dmaengine的驱动架构来编写DMA控制器的驱动,同时外设的驱动使用标准的dmaengine API进行DMA的准备、发起和完成时的回调工作。和中断一样,在使用DMA之前,设备驱动程序需首先向dmaengine系统申请DMA通道,申请DMA通道的函数如下:

struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name);
struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
                                         dma_filter_fn fn, void *fn_param);

使用完DMA通道后,应该利用如下函数释放该通道:

void dma_release_channel(struct dma_chan *chan);

        之后,一般通过如代码清单11.15的方法初始化并发起一次DMA操作。它通过dmaengine_prep_slave_single()准备好一些DMA描述符,并填充其完成回调为xxx_dma_fini_callback(),之后通过dmaengine_submit()把这个描述符插入队列,再通过dma_async_issue_pending()发起这次DMA动作。DMA完成后,xxx_dma_fini_callback()函数会被dmaengine驱动自动调用。

  static void xxx_dma_fini_callback(void *data)
  {
         struct completion *dma_complete = data;
 
         complete(dma_complete);
  }
 
  issue_xxx_dma(...)
  {
      rx_desc = dmaengine_prep_slave_single(xxx->rx_chan,
              xxx->dst_start, t->len, DMA_DEV_TO_MEM,
              DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
      rx_desc->callback = xxx_dma_fini_callback;
      rx_desc->callback_param = &xxx->rx_done;

      dmaengine_submit(rx_desc);
      dma_async_issue_pending(xxx->rx_chan);
 }

这点知识不够用啊(⊙o⊙)…。

二 开始写代码

头文件Dmaengine.h (include\linux) 

dmaengine_prep_slave_single的函数原型

static inline 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 scatterlist sg;
	sg_init_table(&sg, 1);
	sg_dma_address(&sg) = buf;
	sg_dma_len(&sg) = len;

	return chan->device->device_prep_slave_sg(chan, &sg, 1,
						  dir, flags, NULL);
}

为了让这个函数工作起来,我们要定义如下几个变量

struct dma_async_tx_descriptor *rx_desc;
struct dma_chan rx_chan;
dma_addr_t dst_start;
size_t len = CSS_DMA_IMAGE_SIZE;
enum dma_transfer_direction dir = DMA_MEM_TO_MEM;

大概写完的伪代码就是这样的

static void css_dma_fini_callback(void *data)
{
	struct _css_dev_ *css_dev = (struct _css_dev_ *)data;
	//struct completion *dma_complete = data;
	DEBUG_CSS("css_dev->name",css_dev->name);
	//complete(dma_complete);
}
static int css_dma_init(struct _css_dev_ *css_dev)
{
	struct dma_async_tx_descriptor *rx_desc;
	struct dma_chan rx_chan;
	dma_addr_t dst_start;
	size_t len = CSS_DMA_IMAGE_SIZE;
	enum dma_transfer_direction dir = DMA_DEV_TO_MEM;
	
	rx_desc = dmaengine_prep_slave_single(&rx_chan,
              dst_start, len, dir,
              DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
      rx_desc->callback = xxx_dma_fini_callback;
      rx_desc->callback_param = css_dev;

      dmaengine_submit(rx_desc);
      dma_async_issue_pending(css_dev);
}

三 测试代码

应用代码:csi_single_mmap.c

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

#define DEBUG_INFO(format,...)	printf("%s:%d"format"\n",\
	__func__,__LINE__,##__VA_ARGS__);

#define _CSS_DMA_DEV_NAME	"/dev/css_dma"

#define CSS_DMA_IMAGE_SIZE		(1280*800)

#define CSI_DMA_SET_CUR_MAP_BUF_TYPE_IOCTL 0x1001
#define CSI_DMA_FILL_CHAR_AND_RUN_DMAENGIEN_IOCTL	0x1002
#define CSI_DMA_FILL_CHAR_RUN_MEMCPY_IOCTL	0x1003

enum _css_dev_buf_type{
   _CSS_DEV_READ_BUF = 0,
   _CSS_DEV_WRITE_BUF,
   _CSS_DEV_UNKNOWN_BUF_TYPE,
   _CSS_DEV_MAX_BUF_TYPE,
};

struct __css_dev_ {
	char *read_buf;
	char *write_buf;
}_css_dev_;



static int cycle_dmaengine_test(struct __css_dev_ *css_dev,int fd,int count){
	int i = 0,j = 0;
	char c = 'a' - 1;
	for(i = 0;i < count;i++){
		c = c + 1;
		
		if(ioctl(fd,
				CSI_DMA_FILL_CHAR_AND_RUN_DMAENGIEN_IOCTL,
				&c) < 0)
		{
			perror("ioctl");
			DEBUG_INFO("ioctl CSI_DMA_FILL_CHAR_IOCTL fail");
			return -1;
		}
		
		for(j = 0;j < CSS_DMA_IMAGE_SIZE;j++){
			if(css_dev->read_buf[j] != c || css_dev->write_buf[j] != c){
				DEBUG_INFO("error:css_dev->read_buf[%d] = %c,css_dev->write_buf[%d] = %c",
					j,css_dev->read_buf[j],
					j,css_dev->write_buf[j]);
				return -1;
			}
		}
		DEBUG_INFO("set c = %c ok",c);
		if(c == 'z'){
			c = 'a' - 1;
		}
	}
}

static int cycle_memcpy_test(struct __css_dev_ *css_dev,int fd,int count){
	int i = 0,j = 0;
	char c = 'a' - 1;
	for(i = 0;i < count;i++){
		c = c + 1;
		
		if(ioctl(fd,
				CSI_DMA_FILL_CHAR_RUN_MEMCPY_IOCTL,
				&c) < 0)
		{
			perror("ioctl");
			DEBUG_INFO("ioctl CSI_DMA_FILL_CHAR_IOCTL fail");
			return -1;
		}
		
		for(j = 0;j < CSS_DMA_IMAGE_SIZE;j++){
			if(css_dev->read_buf[j] != c || css_dev->write_buf[j] != c){
				DEBUG_INFO("error:css_dev->read_buf[%d] = %c,css_dev->write_buf[%d] = %c",
					j,css_dev->read_buf[j],
					j,css_dev->write_buf[j]);
				return -1;
			}
		}
		DEBUG_INFO("set c = %c ok",c);
		if(c == 'z'){
			c = 'a' - 1;
		}
	}
}

int main(int argc,char *argv[])
{
	struct __css_dev_ *css_dev = &_css_dev_;
	char *name = _CSS_DMA_DEV_NAME;
	int fd = open(name,O_RDWR);
	if(fd < 0){
		DEBUG_INFO("open %s",name);
		return -1;
	}
	DEBUG_INFO("open %s ok",name);
	if(ioctl(fd,
			CSI_DMA_SET_CUR_MAP_BUF_TYPE_IOCTL,
			_CSS_DEV_READ_BUF) < 0)
	{
		perror("ioctl");
		DEBUG_INFO("ioctl fail");
		goto fail;
	}
	css_dev->read_buf = (char*)mmap(NULL,
		CSS_DMA_IMAGE_SIZE,
		PROT_READ|PROT_WRITE,
		MAP_SHARED,fd,0);
	if(css_dev->read_buf == NULL){
		perror("mmap");
		DEBUG_INFO("mmap fail");
		goto fail;
	}
	DEBUG_INFO("css_dev->read_buf = %p",css_dev->read_buf);
	if(ioctl(fd,
			CSI_DMA_SET_CUR_MAP_BUF_TYPE_IOCTL,
			_CSS_DEV_WRITE_BUF) < 0)
	{
		perror("ioctl");
		DEBUG_INFO("ioctl fail");
		goto fail;
	}
	css_dev->write_buf = (char*)mmap(NULL,
		CSS_DMA_IMAGE_SIZE,
		PROT_READ|PROT_WRITE,
		MAP_SHARED,fd,0);
	if(css_dev->write_buf == NULL){
		perror("mmap");
		DEBUG_INFO("mmap fail");
		goto fail;
	}
	
	DEBUG_INFO("css_dev->write_buf = %p",css_dev->write_buf);
	cycle_dmaengine_test(css_dev,fd,10);
	cycle_memcpy_test(css_dev,fd,10);
	
fail:
	close(fd);
	return 0;
}

驱动代码:csi_single.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/gfp.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/platform_data/dma-imx.h>
#include <linux/timer.h>


#define DEBUG_CSS(format,...)\
		printk(KERN_INFO"info:%s:%s:%d: "format"\n",\
		__FILE__,__func__,__LINE__,\
		##__VA_ARGS__)
#define DEBUG_CSS_ERR(format,...)\
		printk("\001" "1""error:%s:%s:%d: "format"\n",\
		__FILE__,__func__,__LINE__,\
		##__VA_ARGS__)

#define CSS_DMA_IMAGE_SIZE		(1280*800)

#define CSI_DMA_SET_CUR_MAP_BUF_TYPE_IOCTL 0x1001
#define CSI_DMA_FILL_CHAR_AND_RUN_DMAENGIEN_IOCTL	0x1002
#define CSI_DMA_FILL_CHAR_RUN_MEMCPY_IOCTL	0x1003


enum _css_dev_buf_type{
   _CSS_DEV_READ_BUF = 0,
   _CSS_DEV_WRITE_BUF,
   _CSS_DEV_UNKNOWN_BUF_TYPE,
   _CSS_DEV_MAX_BUF_TYPE,
};
struct _css_dev_{
	struct file_operations _css_fops;
	struct miscdevice misc;
	int buf_size;
	int buf_size_order;
	char *src_buf;
	char *dst_buf;
	char *user_src_buf_vaddr;
	char *user_dst_buf_vaddr;
	dma_addr_t src_addr;
	dma_addr_t dst_addr;	
	struct spinlock slock;
	struct mutex open_lock;
	char name[10];
	enum _css_dev_buf_type buf_type; 
	struct device *dev;
	struct dma_chan * dma_m2m_chan;
	struct completion dma_m2m_ok;
	struct imx_dma_data m2m_dma_data;
	struct timeval tv_start,tv_end;
	struct device *chan_dev;
	struct dma_device *dma_dev;
	struct dma_async_tx_descriptor *dma_m2m_desc;
};

#define _to_css_dev_(file)	(struct _css_dev_ *)container_of(file->f_op,struct _css_dev_,_css_fops)
static int _css_open(struct inode *inode, struct file *file)
{
	struct _css_dev_ *css_dev = _to_css_dev_(file);
	DEBUG_CSS("css_dev->name = %s",css_dev->name);
	return 0;
}
static ssize_t _css_read(struct file *file, char __user *ubuf, size_t size, loff_t *ppos)
{
	struct _css_dev_ *css_dev = _to_css_dev_(file);
	DEBUG_CSS("css_dev->name = %s",css_dev->name);
	return 0;
}
static int _css_mmap (struct file *file, struct vm_area_struct *vma)
{
	struct _css_dev_ *css_dev = _to_css_dev_(file);
	char *p = NULL;
	char **user_addr;

	switch(css_dev->buf_type){
		case _CSS_DEV_READ_BUF:
			p = css_dev->src_buf;
			user_addr = (char**)&css_dev->user_src_buf_vaddr;
			break;
		case _CSS_DEV_WRITE_BUF:
			p = css_dev->dst_buf;
			user_addr = (char**)&css_dev->user_dst_buf_vaddr;
			break;
		default:
			p = NULL;
			return -EINVAL;
			break;			
	}
	if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(p) >> PAGE_SHIFT,
	        vma->vm_end-vma->vm_start, vma->vm_page_prot)) {
	        DEBUG_CSS_ERR( "remap_pfn_range error\n"); 
	        return -EAGAIN;
	}
	css_dev->buf_type = _CSS_DEV_UNKNOWN_BUF_TYPE;
	*user_addr = (void*)vma->vm_start;	
	DEBUG_CSS("mmap ok user_addr = %p kernel addr = %p",*user_addr,p);
	return 0;
}
static ssize_t _css_write(struct file *file, const char __user *ubuf, size_t size, loff_t *ppos)
{
	struct _css_dev_ *css_dev = _to_css_dev_(file);
	DEBUG_CSS("css_dev->name = %s",css_dev->name);
	return size;
}

static int _css_release (struct inode *inode, struct file *file)
{
	struct _css_dev_ *css_dev = _to_css_dev_(file);
	DEBUG_CSS("css_dev->name = %s",css_dev->name);
	DEBUG_CSS("css_dev->src_buf[%d] = %c",css_dev->buf_size - 1,
		((char*)css_dev->src_buf)[css_dev->buf_size - 1]);
	DEBUG_CSS("css_dev->dst_buf[%d] = %c",css_dev->buf_size - 1,
		((char*)css_dev->dst_buf)[css_dev->buf_size - 1]);
	return 0;
}

static int _css_set_buf_type(struct file *file, enum _css_dev_buf_type buf_type)
{
	unsigned long flags;
	struct _css_dev_ *css_dev = _to_css_dev_(file);
	DEBUG_CSS("buf_type=%d",buf_type);
	if(buf_type >= _CSS_DEV_MAX_BUF_TYPE){
		DEBUG_CSS_ERR("invalid buf type");
		return -EINVAL;
	}
	spin_lock_irqsave(&css_dev->slock,flags);
	css_dev->buf_type = buf_type;	
	spin_unlock_irqrestore(&css_dev->slock,flags);
	return 0;
}
static void css_dma_async_tx_callback(void *dma_async_param)
{
	struct _css_dev_ *css_dev = (struct _css_dev_ *)dma_async_param;
	complete(&css_dev->dma_m2m_ok);
}
static int css_dmaengine_cpy(struct _css_dev_ *css_dev)
{	
	dma_cookie_t cookie;
	enum dma_status dma_status;

	do_gettimeofday(&css_dev->tv_start);
	
	css_dev->src_addr = dma_map_single(css_dev->chan_dev, css_dev->src_buf, css_dev->buf_size, DMA_TO_DEVICE);	

	css_dev->dst_addr = dma_map_single(css_dev->chan_dev, css_dev->dst_buf, css_dev->buf_size, DMA_FROM_DEVICE);	
	//DEBUG_CSS("32bit:css_dev->src_addr = %x,css_dev->dst_addr = %x",css_dev->src_addr,css_dev->dst_addr);

	css_dev->dma_m2m_desc = css_dev->dma_dev->device_prep_dma_memcpy(css_dev->dma_m2m_chan,
													css_dev->dst_addr,
													css_dev->src_addr,
													css_dev->buf_size,0);
	css_dev->dma_m2m_desc->callback = css_dma_async_tx_callback;
	css_dev->dma_m2m_desc->callback_param = css_dev;
	init_completion(&css_dev->dma_m2m_ok);
	cookie = dmaengine_submit(css_dev->dma_m2m_desc);
	if(dma_submit_error(cookie)){
		DEBUG_CSS("dmaengine_submit error");
		return -EINVAL;
	}
	dma_async_issue_pending(css_dev->dma_m2m_chan);
	wait_for_completion(&css_dev->dma_m2m_ok);
	
	dma_status = dma_async_is_tx_complete(css_dev->dma_m2m_chan,cookie,NULL,NULL);
	if(DMA_COMPLETE != dma_status){
		DEBUG_CSS("error:dma_status = %d",dma_status);
	}
	dma_unmap_single(css_dev->chan_dev,css_dev->src_addr,css_dev->buf_size,DMA_TO_DEVICE);
	dma_unmap_single(css_dev->chan_dev,css_dev->dst_addr,css_dev->buf_size,DMA_FROM_DEVICE);
	
	do_gettimeofday(&css_dev->tv_end);
	DEBUG_CSS("dma used time = %ld us",
	(css_dev->tv_end.tv_sec - css_dev->tv_start.tv_sec)*1000000 
	+ (css_dev->tv_end.tv_usec - css_dev->tv_start.tv_usec));
	return 0;
}

static long _css_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	struct _css_dev_ *css_dev = _to_css_dev_(file);
	char *p;
	switch(cmd){
		case CSI_DMA_SET_CUR_MAP_BUF_TYPE_IOCTL:
		ret = _css_set_buf_type(file,(enum _css_dev_buf_type)arg);
		break;
		case CSI_DMA_FILL_CHAR_AND_RUN_DMAENGIEN_IOCTL:
		p = (char*)arg;
		memset(css_dev->src_buf,*p,css_dev->buf_size);		
		css_dmaengine_cpy(css_dev);
		break;
		case CSI_DMA_FILL_CHAR_RUN_MEMCPY_IOCTL:
		p = (char*)arg;
		do_gettimeofday(&css_dev->tv_start);
		memset(css_dev->src_buf,*p,css_dev->buf_size);	
		do_gettimeofday(&css_dev->tv_end);
	DEBUG_CSS("memset used time = %ld us",
	(css_dev->tv_end.tv_sec - css_dev->tv_start.tv_sec)*1000000 
	+ (css_dev->tv_end.tv_usec - css_dev->tv_start.tv_usec));

		do_gettimeofday(&css_dev->tv_start);
		memcpy(css_dev->dst_buf,css_dev->src_buf,css_dev->buf_size);
		do_gettimeofday(&css_dev->tv_end);
	DEBUG_CSS("memcpy used time = %ld us",
	(css_dev->tv_end.tv_sec - css_dev->tv_start.tv_sec)*1000000 
	+ (css_dev->tv_end.tv_usec - css_dev->tv_start.tv_usec));
		break;
		default:
			DEBUG_CSS_ERR("unknown cmd = %x",cmd);
			ret = -EINVAL;
			break;
	}
	
	return ret;
}

static struct _css_dev_ _global_css_dev = {
	.name = "lkmao",
	._css_fops = {
		.owner = THIS_MODULE,
		.mmap = _css_mmap,
		.open = _css_open,
		.release = _css_release,
		.read = _css_read,
		.write = _css_write,
		.unlocked_ioctl = _css_unlocked_ioctl,
	},
	.misc = {
		.minor = MISC_DYNAMIC_MINOR,
		.name = "css_dma",
	},
	.buf_type = _CSS_DEV_UNKNOWN_BUF_TYPE,
	.user_src_buf_vaddr = NULL,
	.user_dst_buf_vaddr = NULL,
	.m2m_dma_data = {
		.peripheral_type = IMX_DMATYPE_MEMORY,
		.priority = DMA_PRIO_HIGH,
	},
};


static int css_dev_get_dma_addr(struct _css_dev_ *css_dev,char **vaddr,dma_addr_t *phys, int direction)
{
	char *p;
	dma_addr_t dma_addr;

	p = (char*)__get_free_pages(GFP_KERNEL|GFP_DMA,css_dev->buf_size_order);
	if(p == NULL || IS_ERR(p)){
		DEBUG_CSS("devm_kmalloc error");
		return -ENOMEM;
	}
	dma_addr = dma_map_single(css_dev->dev, p, css_dev->buf_size, direction);	

	*vaddr = p;
	*phys = dma_addr;

	DEBUG_CSS("32bit:p = %p,dma_addr = %x",p,dma_addr);
	return 0;
}

static bool css_dma_filter_fn(struct dma_chan *chan, void *filter_param)
{
	if(!imx_dma_is_general_purpose(chan)){
		DEBUG_CSS("css_dma_filter_fn error");
		return false;
	}
	chan->private = filter_param;
	return true;
}

static int css_dmaengine_init(struct _css_dev_ *css_dev)
{
	dma_cap_mask_t dma_m2m_mask;
	struct dma_slave_config dma_m2m_config = {0};
	css_dev->m2m_dma_data.peripheral_type = IMX_DMATYPE_MEMORY;
	css_dev->m2m_dma_data.priority = DMA_PRIO_HIGH;
	
	dma_cap_zero(dma_m2m_mask);
	dma_cap_set(DMA_MEMCPY,dma_m2m_mask);

	

	css_dev->dma_m2m_chan = dma_request_channel(dma_m2m_mask,css_dma_filter_fn,&css_dev->m2m_dma_data);
	if(!css_dev->dma_m2m_chan){
		DEBUG_CSS("dma_request_channel error");
		return -EINVAL;
	}
	dma_m2m_config.direction = DMA_MEM_TO_MEM;
	dma_m2m_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
	dmaengine_slave_config(css_dev->dma_m2m_chan,&dma_m2m_config);

	css_dev->dma_dev = css_dev->dma_m2m_chan->device;
	css_dev->chan_dev = css_dev->dma_m2m_chan->device->dev;

	
	
	return 0;
}

static int css_dev_init(struct platform_device *pdev,struct _css_dev_ *css_dev)
{
	css_dev->misc.fops = &css_dev->_css_fops;

	pr_debug("css_init init ok");
	mutex_init(&css_dev->open_lock);
	spin_lock_init(&css_dev->slock);
	printk("KERN_ALERT = %s",KERN_ALERT);
	

	css_dev->dev = &pdev->dev;
	css_dev->buf_size = CSS_DMA_IMAGE_SIZE;
	css_dev->buf_size_order = get_order(css_dev->buf_size);
	
	if(css_dev_get_dma_addr(css_dev,&css_dev->src_buf,&css_dev->src_addr,DMA_TO_DEVICE)){
		return -ENOMEM;
	}
	css_dev->src_buf = (char*)__get_free_pages(GFP_KERNEL|GFP_DMA,css_dev->buf_size_order);
	if(css_dev->src_buf == NULL || IS_ERR(css_dev->src_buf)){
		DEBUG_CSS("devm_kmalloc error");
		return -ENOMEM;
	}
	css_dev->dst_buf = (char*)__get_free_pages(GFP_KERNEL|GFP_DMA,css_dev->buf_size_order);
	if(css_dev->dst_buf == NULL || IS_ERR(css_dev->dst_buf)){
		DEBUG_CSS("devm_kmalloc error");
		return -ENOMEM;
	}
	DEBUG_CSS("32bit:css_dev->src_buf = %p,css_dev->dst_buf = %p",css_dev->src_buf,css_dev->dst_buf);

	if(misc_register(&css_dev->misc) != 0){
		DEBUG_CSS("misc_register error");
		return -EINVAL;
	}
	platform_set_drvdata(pdev,css_dev);
	css_dmaengine_init(css_dev);
	return 0;
}


static int css_probe(struct platform_device *pdev)
{
	struct _css_dev_ *css_dev = (struct _css_dev_ *)&_global_css_dev;
	if(css_dev_init(pdev,css_dev)){
		return -EINVAL;
	}	
	DEBUG_CSS("init ok");
	return 0;
}

static int css_remove(struct platform_device *pdev)
{
	struct _css_dev_ *css_dev = &_global_css_dev;

	//dma_unmap_single(css_dev->dev,css_dev->src_addr,css_dev->buf_size,DMA_FROM_DEVICE);
	//dma_unmap_single(css_dev->dev,css_dev->dst_addr,css_dev->buf_size,DMA_FROM_DEVICE);

	free_page((unsigned long )css_dev->dst_buf);
	free_page((unsigned long )css_dev->src_buf);

	misc_deregister(&css_dev->misc);
	dma_release_channel(css_dev->dma_m2m_chan);
	DEBUG_CSS("exit ok");
	return 0;
}

static const struct of_device_id css_of_ids[] = {
	{.compatible = "css_dma"},
	{},
};
MODULE_DEVICE_TABLE(of,css_of_ids);
static struct platform_driver css_platform_driver = {
	.probe =  css_probe,
	.remove = css_remove,
	.driver = {
		.name = "css_dma",
		.of_match_table = css_of_ids,
		.owner = THIS_MODULE,
	},
};

static int __init css_init(void)
{
	int ret_val;
	ret_val = platform_driver_register(&css_platform_driver);
	if(ret_val != 0){
		DEBUG_CSS("platform_driver_register error");
		return ret_val;
	}
	DEBUG_CSS("platform_driver_register ok");
	return 0;
}

static void __exit css_exit(void)
{
	platform_driver_unregister(&css_platform_driver);
}

module_init(css_init);
module_exit(css_exit);
MODULE_LICENSE("GPL");

Makefile:

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
KERN_DIR = /home/lkmao/imx/linux/linux-imx

FILE_NAME=csi_single
obj-m += $(FILE_NAME).o
APP_NAME=csi_single_mmap

all:
        make -C $(KERN_DIR) M=$(shell pwd) modules
        sudo cp $(FILE_NAME).ko /big/nfsroot/jiaocheng_rootfs/home/root/
        sudo scp $(FILE_NAME).ko root@192.168.0.3:/home/root/
        arm-linux-gnueabihf-gcc -o $(APP_NAME) $(APP_NAME).c
        sudo cp $(APP_NAME) /big/nfsroot/jiaocheng_rootfs/home/root/
        sudo scp $(APP_NAME) root@192.168.0.3:/home/root/

.PHONY:clean
clean:
        make -C $(KERN_DIR) M=$(shell pwd) clean
        rm $(APP_NAME) -rf

设备树配置

    sdma_m2m{
		compatible = "css_dma";
	};

测试过程和结果:

1 make编译Makefile

2 进入开发板加载模块:insmod csi_single.ko

root@ATK-IMX6U:~# insmod csi_single.ko
[   41.430795] KERN_ALERT = 1
[   41.433620] info:/big/csi_driver/css_dma/csi_single.c:css_dev_get_dma_addr:267: 32bit:p = 94c00000,dma_addr = 94c00000
[   41.447390] info:/big/csi_driver/css_dma/csi_single.c:css_dev_init:337: 32bit:css_dev->src_buf = 94d00000,css_dev->dst_buf = 94e00000
[   41.463653] info:/big/csi_driver/css_dma/csi_single.c:css_probe:355: init ok
[   41.474842] info:/big/csi_driver/css_dma/csi_single.c:css_init:398: platform_driver_register ok
root@ATK-IMX6U:~#

3 运行测试应用:./csi_single_mmap

执行结果如下图所示:

测试分两个部分,一个是dma引擎复制数据的部分,第二个是memcpy复制数据的部分

驱动中的调试信息

 应用输出的调试信息:

 小结

        我对这个测试结果是表示怀疑的,DMA引擎20多毫秒,memcpy1.7毫秒,memset 0.7毫秒。我一度怀疑是代码出了问题。还得想办法做更多的测试才行。

有关DMA实践3:dmaengine的实验的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  3. ruby-on-rails - Rails 中同一个类的多个关联的最佳实践? - 2

    我认为我的问题最好用一个例子来描述。假设我有一个名为“Thing”的简单模型,它有一些简单数据类型的属性。像...Thing-foo:string-goo:string-bar:int这并不难。数据库表将包含具有这三个属性的三列,我可以使用@thing.foo或@thing.bar之类的东西访问它们。但我要解决的问题是当“foo”或“goo”不再包含在简单数据类型中时会发生什么?假设foo和goo代表相同类型的对象。也就是说,它们都是“Whazit”的实例,只是数据不同。所以现在事情可能看起来像这样......Thing-bar:int但是现在有一个新的模型叫做“Whazit”,看起来

  4. ruby-on-rails - 向 Rails 3 添加 Ruby 扩展方法的最佳实践? - 2

    我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion

  5. Ruby 最佳实践 : working with classes - 2

    参见下面的示例,我想最好使用第二种方法,但第一种也可以。哪种方法最好,使用另一种的后果是什么?classTestdefstartp"started"endtest=Test.newtest.startendclassTest2defstartp"started"endendtest2=Test2.newtest2.start 最佳答案 我肯定会说第二种变体更有意义。第一个不会导致错误,但对象实例化完全过时且毫无意义。外部变量在类的范围内不可见:var="string"classAvar=A.newendputsvar#=>strin

  6. ruby - 存储外部 API 的密码 - 最佳实践 - 2

    如果我构建了一个应用程序来访问来自Gmail、Twitter和Facebook的一些数据,并且我希望用户只需输入一次他们的身份验证信息,并且在几天或几周后重置,那会怎样是在Ruby中动态执行此操作的最佳方法吗?我看到很多人只是拥有他们客户/用户凭证的配置文件,如下所示:gmail_account:username:myClientpassword:myClientsPassword这看起来a)非常不安全,b)如果我想为成千上万的用户存储此类信息,它就无法工作。推荐的方法是什么?我希望能够在这些服务之上构建一个界面,因此每次用户进行交易时都必须输入凭据是不可行的。

  7. ruby-on-rails - 使用设计身份验证的 API 访问 - 最佳实践? - 2

    我正在使用Devise在Rails应用程序中,并希望通过API公开一些模型数据,但应该像应用程序一样限制对API的访问。$curlhttp://myapp.com/api/v1/sales/7.json{"error":"Youneedtosigninorsignupbeforecontinuing."}很明显。在这种情况下是否有访问API的最佳实践?我更喜欢一步验证+获取数据,但这只是为了让客户的工作更轻松。他们将使用JQuery在客户端提取数据。感谢您提供任何信息!凡妮莎 最佳答案 我建议您按照以下帖子中的选项2:使用APIke

  8. ruby-on-rails - 在多个页面上使用相同表单的 Rails 最佳实践 - 2

    我正在开发一个Rails2.3.1网站。在整个网站中,我需要一个用于在各种页面(主页、创建帖子页面、帖子列表页面、评论列表页面等)上创建帖子的表单——只要说这个表单需要在由各种Controller)。这些页面中的每一个都显示在相应的Controller/操作中检索到的各种其他信息。例如,主页列出了最新的10篇文章、从数据库中提取的内容等。因此,我已将帖子创建表单移动到它自己的部分中,并将该部分包含在所有必要的页面中。请注意,部分POST中的表单到/questions(路由到PostsController::create——这是默认的Rails行为)。我遇到的问题是当Posts表单没有正

  9. ruby-on-rails - Rails 'service objects' 最佳实践 - 类方法或实例化 - 2

    我正在按照我一直在研究的研讨会实现“服务对象”,我正在构建一个redditAPI应用程序。我需要对象返回一些东西,所以我不能只执行初始化程序中的所有内容。我有这两个选择:选项1:类需要实例化classSubListFromUserdefuser_subscribed_subs(client)@client=client@subreddits=sort_subs_by_name(user_subs_from_reddit)endprivatedefsort_subs_by_name(subreddits)subreddits.sort_by{|sr|sr[:name].downcase}

  10. 网络实验之RIPV2协议(一) - 2

    一、RIPV2协议简介  RIP(RoutingInformationProtocol)路由协议是一种相对古老,在小型以及同介质网络中得到了广泛应用的一种路由协议。RIP采用距离向量算法,是一种距离向量协议。RIP-1是有类别路由协议(ClassfulRoutingProtocol),它只支持以广播方式发布协议报文。RIP-1的协议报文无法携带掩码信息,它只能识别A、B、C类这样的自然网段的路由,因此RIP-1不支持非连续子网(DiscontiguousSubnet)。RIP-2是一种无类别路由协议(ClasslessRoutingProtocol),支持路由标记,在路由策略中可根据路由标记对

随机推荐