DMA驱动程序

  • 格式:pdf
  • 大小:127.36 KB
  • 文档页数:11

static u32 src_phys;
static char *dst; static u32 dst_phys;
static struct class *cls;
static volatile struct s3c_dma_regs *dma_regs;
static DECLARE_WAIT_QUEUE_HEAD(dma_waitq); /* 中断事件标志, 中断服务程序将它置 1,ioctl 将它清 0 */ static volatile int ev_dma = 0;
/* 启动 DMA */ dma_regs->dmasktrig
= (1<<1) | (1<<0);
/* 如何知道 DMA 什么时候完成? */ /* 休眠 */ wait_event_interruptible(dma_waitq, ev_dma);
if (memcmp(src, dst, BUF_SIZE) == 0) { printk("MEM_CPY_DMA OK\n"); } else { printk("MEM_CPY_DMA ERROR\n"); } break; } }
static int s3c_dma_init(void) { /* 这里注册一个中断,当 DMA 数据传输完毕之后会发生此中断 */ if (request_irq(IRQ_DMA3, s3c_dma_irq, 0, "s3c_dma", 1)) { printk("can't request_irq for DMA\n"); return -EBUSY; } /* 分配 SRC, DST 对应的缓冲区,关于此函数详见注释 1 */ src = dma_alloc_writecombine(NULL, BUF_SIZE, &src_phys, GFP_KERNEL);//源 if (NULL == src) { printk("can't alloc buffer for src\n"); free_irq(IRQ_DMA3, 1); return -ENOMEM; } dst = dma_alloc_writecombine(NULL, BUF_SIZE, &dst_phys, GFP_KERNEL);//目的 if (NULL == dst) { free_irq(IRQ_DMA3, 1); dma_free_writecombine(NULL, BUF_SIZE, src, src_phys); printk("can't alloc buffer for dst\n"); return -ENOMEM; }
Initial State
LOC
[1]
0
INC
[0]
0
DIDSTn D_ADDR
DIDS TCn CHK _INT
Bit
Description 当设置为自动加载时,用来选择中断发生的时间 0:TC 为 0 是产生中断 1:自动加载完成的时候产生中断 用于选择目的设备的位置 0:目的设备在系统总线上 1:目的设备在外设总线上 用于选择地址是否自动增加 0:地址自动增加 1:地址固定不变(此时即便是 burst 模式下,传输过程 中地址自动增加,但是一旦传输完这一次数据,地址又
//这是 DMA 模式 case MEM_CPY_DMA : { ev_dma = 0; /* 把源,目的, 长度告诉 DMA */ /* 关于下面寄存器的具体情况,我们在注释 3 里面来详细讲一 下 */ dma_regs->disrc = src_phys; /* 源的物理地址 */ dma_regs->disrcc = (0<<1) | (0<<0); /* 源位于 AHB 总线, 源地址递增 */ dma_regs->didst = dst_phys; /* 目的的物理地址 */ dma_regs->didstc = (0<<2) | (0<<1) | (0<<0); /* 目的位于 AHB 总线, 目的地址递增 */ dma_regs->dcon = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(BUF_SIZE<<0); /* 使能中断,单个传输, 软件触发, */
#define DMA0_BASE_ADDR #define DMA1_BASE_ADDR #define DMA2_BASE_ADDR #define DMA3_BASE_ADDR struct s3c_dma_regs { unsigned long disrc; unsigned long disrcc; unsigned long didst; unsigned long didstc; unsigned long dcon; unsigned long dstat; unsigned long dcsrc; unsigned long dcdst; unsigned long dmasktrig; }; static int major = 0; static char *src;
dma_regs = ioremap(DMA3_BASE_ADDR, sizeof(struct s3c_dma_regs));//这边是将 DMA 控制 寄存器映射到内核空间 return 0; }
static void s3c_dma_exit(void) { iounmap(dma_regs); class_device_destroy(cls, MKDEV(major, 0)); class_destroy(cls); unregister_chrdev(major, "s3c_dma"); dma_free_writecombine(NULL, BUF_SIZE, src, src_phys); dma_free_writecombine(NULL, BUF_SIZE, dst, dst_phys); free_irq(IRQ_DMA3, 1); } module_init(s3c_dma_init); module_exit(s3c_dma_exit); MODULE_LICENSE("GPL"); 注释 1: 之前我们知道在内核中开辟空间可以用 kmalloc 函数,这里却用了 dma_alloc_writecombine, 这是为什么呢?这是因为 kmalloc 开辟的空间其逻辑地址虽然是连续的,但是其实际的物理 地址可能不是连续的。 而 DMA 传输数据时, 要求物理地址是连续的, dma_alloc_writecombine 就满足这个要求 注释 2: int memcmp(const void *cs, const void *ct, size_t count) { const unsigned char *su1, *su2; int res = 0;
注释 3: 我们先来解析一下上面几个寄存器: DISRCn S_ADDR bit [30:0] Description 源起始地址 Initial State 0x00000000
DISR CCn
bit
Description 用于选择源的位置 0:源在系统总线上 1:源在外设总线上 用于选择地址是否自动增加 0:地址自动增加 1:地址固定不变(此时即便是 burst 模式下,传输过程 中地址自动增加, 但是一旦传输完这一次数据,地址 又变为初值) bit [30:0] Description 目的起始地址 Initial State 0x00000000
本程序来韦东山视频,适用于 S3C2440 本人用的是 ok6410,因此需要适当修改程序 本文参照了别人的成果 本文包括:DMA 驱动程序 以及 #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> #include <linux/poll.h> #include <linux/dma-mapping.h> #define MEM_CPY_NO_DMA 0 #define MEM_CPY_DMA 1 #define BUF_SIZE (512*1024) 0x4B000000 0x4B000040 0x4B000080 0x4B0000C0 DMA 测试程序 ,如有不当之处还望您多多指教
major = register_chrdev(0, "s3c_dma", &dma_fops);//注册字符设备
/* 为了自动创建设备节点 */ cls = class_create(THIS_MODULE, "s3c_dma"); class_device_create(cls, NULL, MKDEV(major, 0), NULL, "dma"); /* /dev/dma */
for (su1 = cs, su2 = ct; 0 < count; ++su1, ++) != 0)