本次是第三篇。
第一篇,写一个通用框架,做到拿来就能用。
第二篇,实现mmap功能,内核中的read_buf和write_buf都映射到用户空间,然后呢。写read_buf和write_buf的最后一个字节为‘R’和'W',然后再release函数中打印这两个字节。更加复杂的验证,根据需要自行添加,写的太复杂,意义不大。
第三篇,通过测试app,控制复制src_buf到dst_buf,复制方式可以使用DMA引擎和memcpy,并计算复制过程中消耗的微秒数,并在测试app中验证复制是否准确,尽最大努力保证整个流程的准确无误。
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);
}
#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;
}
#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");
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毫秒。我一度怀疑是代码出了问题。还得想办法做更多的测试才行。
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
我认为我的问题最好用一个例子来描述。假设我有一个名为“Thing”的简单模型,它有一些简单数据类型的属性。像...Thing-foo:string-goo:string-bar:int这并不难。数据库表将包含具有这三个属性的三列,我可以使用@thing.foo或@thing.bar之类的东西访问它们。但我要解决的问题是当“foo”或“goo”不再包含在简单数据类型中时会发生什么?假设foo和goo代表相同类型的对象。也就是说,它们都是“Whazit”的实例,只是数据不同。所以现在事情可能看起来像这样......Thing-bar:int但是现在有一个新的模型叫做“Whazit”,看起来
我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion
参见下面的示例,我想最好使用第二种方法,但第一种也可以。哪种方法最好,使用另一种的后果是什么?classTestdefstartp"started"endtest=Test.newtest.startendclassTest2defstartp"started"endendtest2=Test2.newtest2.start 最佳答案 我肯定会说第二种变体更有意义。第一个不会导致错误,但对象实例化完全过时且毫无意义。外部变量在类的范围内不可见:var="string"classAvar=A.newendputsvar#=>strin
如果我构建了一个应用程序来访问来自Gmail、Twitter和Facebook的一些数据,并且我希望用户只需输入一次他们的身份验证信息,并且在几天或几周后重置,那会怎样是在Ruby中动态执行此操作的最佳方法吗?我看到很多人只是拥有他们客户/用户凭证的配置文件,如下所示:gmail_account:username:myClientpassword:myClientsPassword这看起来a)非常不安全,b)如果我想为成千上万的用户存储此类信息,它就无法工作。推荐的方法是什么?我希望能够在这些服务之上构建一个界面,因此每次用户进行交易时都必须输入凭据是不可行的。
我正在使用Devise在Rails应用程序中,并希望通过API公开一些模型数据,但应该像应用程序一样限制对API的访问。$curlhttp://myapp.com/api/v1/sales/7.json{"error":"Youneedtosigninorsignupbeforecontinuing."}很明显。在这种情况下是否有访问API的最佳实践?我更喜欢一步验证+获取数据,但这只是为了让客户的工作更轻松。他们将使用JQuery在客户端提取数据。感谢您提供任何信息!凡妮莎 最佳答案 我建议您按照以下帖子中的选项2:使用APIke
我正在开发一个Rails2.3.1网站。在整个网站中,我需要一个用于在各种页面(主页、创建帖子页面、帖子列表页面、评论列表页面等)上创建帖子的表单——只要说这个表单需要在由各种Controller)。这些页面中的每一个都显示在相应的Controller/操作中检索到的各种其他信息。例如,主页列出了最新的10篇文章、从数据库中提取的内容等。因此,我已将帖子创建表单移动到它自己的部分中,并将该部分包含在所有必要的页面中。请注意,部分POST中的表单到/questions(路由到PostsController::create——这是默认的Rails行为)。我遇到的问题是当Posts表单没有正
我正在按照我一直在研究的研讨会实现“服务对象”,我正在构建一个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}
一、RIPV2协议简介 RIP(RoutingInformationProtocol)路由协议是一种相对古老,在小型以及同介质网络中得到了广泛应用的一种路由协议。RIP采用距离向量算法,是一种距离向量协议。RIP-1是有类别路由协议(ClassfulRoutingProtocol),它只支持以广播方式发布协议报文。RIP-1的协议报文无法携带掩码信息,它只能识别A、B、C类这样的自然网段的路由,因此RIP-1不支持非连续子网(DiscontiguousSubnet)。RIP-2是一种无类别路由协议(ClasslessRoutingProtocol),支持路由标记,在路由策略中可根据路由标记对