DMA驱动实际上是一个字符设备驱动程序,用普通的注册字符设备驱动即可。
驱动代码如下:
1 /* 2 * dma驱动实际上是一个字符设备驱动。 3 * 本程序实现了用DMA将一段内存从一个源地址拷贝到目的地址,源、目的均在内存上, 4 *大小为BUF_SIZE。 还可以不适用DMA,直接使用MCU拷贝,使用命令MEM_CPY_NO_DMA和 5 *MEM_CPY_DMA选择是否使用DMA拷贝。 6 * 7 */ 8 9 #include <linux/module.h> 10 #include <linux/init.h> 11 #include <linux/interrupt.h> 12 #include <linux/irq.h> 13 #include <linux/wait.h> 14 #include <linux/sched.h> 15 #include <linux/input.h> 16 #include <linux/errno.h> 17 #include <linux/cdev.h> 18 #include <linux/dma-mapping.h> 19 20 //定义是否使用DMA拷贝命令宏 21 #define MEM_CPY_NO_DMA 0 22 #define MEM_CPY_DMA 1 23 24 #define BUF_SIZE (512*1024) //缓存区大小 25 26 //S3c2440 4个DMA通道基地址 27 #define DMA0_BASE_ADDR 0x4B000000 28 #define DMA1_BASE_ADDR 0x4B000040 29 #define DMA2_BASE_ADDR 0x4B000080 30 #define DMA3_BASE_ADDR 0x4B0000C0 31 32 //每个DMA通道有9个控制寄存器 33 struct s3c_dma_regs 34 { 35 unsigned long disrc; 36 unsigned long disrcc; 37 unsigned long didst; 38 unsigned long didstc; 39 unsigned long dcon; 40 unsigned long dstat; 41 unsigned long dcsrc; 42 unsigned long dcdst; 43 unsigned long dmasktrig; 44 }; 45 46 47 static int major = 0; //主设备号 48 static struct class *cls; //类 49 50 static char *src; //源虚拟(映射)地址 51 static u32 src_phys; //源物理地址 52 53 static char *dst; //目的虚拟(映射)地址 54 static u32 dst_phys; //目的物理地址 55 56 static volatile struct s3c_dma_regs *dma_regs; 57 58 static DECLARE_WAIT_QUEUE_HEAD(dma_waitq); 59 /* 中断事件标志, 中断服务程序将它置1,ioctl将它清0 */ 60 static volatile int ev_dma = 0; 61 62 static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) 63 { 64 int i; 65 66 memset(src, 0xAA, BUF_SIZE); 67 memset(dst, 0x55, BUF_SIZE); 68 69 switch (cmd) 70 { 71 case MEM_CPY_NO_DMA : 72 { 73 for (i = 0; i < BUF_SIZE; i++) 74 dst[i] = src[i]; 75 if (memcmp(src, dst, BUF_SIZE) == 0) 76 { 77 printk("MEM_CPY_NO_DMA OK "); 78 } 79 else 80 { 81 printk("MEM_CPY_DMA ERROR "); 82 } 83 break; 84 } 85 86 case MEM_CPY_DMA : 87 { 88 ev_dma = 0; 89 90 /* 把源,目的,长度告诉DMA */ 91 dma_regs->disrc = src_phys; /* 源的物理地址 */ 92 dma_regs->disrcc = (0<<1) | (0<<0); /* 源位于AHB总线, 源地址递增 */ 93 dma_regs->didst = dst_phys; /* 目的的物理地址 */ 94 dma_regs->didstc = (0<<2) | (0<<1) | (0<<0); /* 目的位于AHB总线, 目的地址递增 */ 95 dma_regs->dcon = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(BUF_SIZE<<0); /* 使能中断,单个传输,软件触发, */ 96 97 /* 启动DMA */ 98 dma_regs->dmasktrig = (1<<1) | (1<<0); 99 100 /* 如何知道DMA什么时候完成? */ 101 /* 休眠 */ 102 wait_event_interruptible(dma_waitq, ev_dma); 103 104 if (memcmp(src, dst, BUF_SIZE) == 0) 105 { 106 printk("MEM_CPY_DMA OK "); 107 } 108 else 109 { 110 printk("MEM_CPY_DMA ERROR "); 111 } 112 113 break; 114 } 115 } 116 117 return 0; 118 } 119 120 static struct file_operations dma_fops = { 121 .owner = THIS_MODULE, 122 .ioctl = s3c_dma_ioctl, 123 }; 124 125 static irqreturn_t s3c_dma_irq(int irq, void *devid) 126 { 127 /* 唤醒 */ 128 ev_dma = 1; 129 wake_up_interruptible(&dma_waitq); /* 唤醒休眠的进程 */ 130 return IRQ_HANDLED; 131 } 132 133 static int s3c_dma_init(void) 134 { 135 //DMA拷贝完成后产生一个中断 136 if (request_irq(IRQ_DMA3, s3c_dma_irq, 0, "s3c_dma", (void *)1)) 137 { 138 printk("can't request_irq for DMA "); 139 return -EBUSY; 140 } 141 142 /* 分配SRC, DST对应的缓冲区 */ 143 //不能使用kalloc分配内存,因为kalloc可能分配的内存虚拟地址连续,物理地址不一 144 //定连续,而DMA不能处理物理地址不连续的地址 145 src = dma_alloc_writecombine(NULL, BUF_SIZE, &src_phys, GFP_KERNEL); 146 if (NULL == src) 147 { 148 printk("can't alloc buffer for src "); 149 free_irq(IRQ_DMA3, (void *)1); 150 return -ENOMEM; 151 } 152 153 dst = dma_alloc_writecombine(NULL, BUF_SIZE, &dst_phys, GFP_KERNEL); 154 if (NULL == dst) 155 { 156 free_irq(IRQ_DMA3, (void *)1); 157 dma_free_writecombine(NULL, BUF_SIZE, src, src_phys); 158 printk("can't alloc buffer for dst "); 159 return -ENOMEM; 160 } 161 162 major = register_chrdev(0, "s3c_dma", &dma_fops); 163 164 /* 为了自动创建设备节点 */ 165 cls = class_create(THIS_MODULE, "s3c_dma"); 166 device_create(cls, NULL, MKDEV(major, 0), NULL, "dma"); /* /dev/dma */ 167 168 //使用第三通道,使用前先看一下系统已经使用了哪些DMA通道,可查看/proc/interrupts, 169 //查看中断号即可,s3c2440的DMA对应的4个通道的中断号为33、34、35、36 170 dma_regs = ioremap(DMA3_BASE_ADDR, sizeof(struct s3c_dma_regs)); 171 172 return 0; 173 } 174 175 static void s3c_dma_exit(void) 176 { 177 iounmap(dma_regs); 178 device_destroy(cls, MKDEV(major, 0)); 179 class_destroy(cls); 180 unregister_chrdev(major, "s3c_dma"); 181 dma_free_writecombine(NULL, BUF_SIZE, src, src_phys); 182 dma_free_writecombine(NULL, BUF_SIZE, dst, dst_phys); 183 free_irq(IRQ_DMA3, (void *)1); 184 } 185 186 module_init(s3c_dma_init); 187 module_exit(s3c_dma_exit); 188 189 MODULE_LICENSE("GPL");
测试程序如下:
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <sys/ioctl.h> 6 #include <string.h> 7 8 /* ./dma_test nodma 9 * ./dma_test dma 10 */ 11 #define MEM_CPY_NO_DMA 0 12 #define MEM_CPY_DMA 1 13 14 void print_usage(char *name) 15 { 16 printf("Usage: "); 17 printf("%s <nodma | dma> ", name); 18 } 19 20 21 int main(int argc, char **argv) 22 { 23 int fd; 24 25 if (argc != 2) 26 { 27 print_usage(argv[0]); 28 return -1; 29 } 30 31 fd = open("/dev/dma", O_RDWR); 32 if (fd < 0) 33 { 34 printf("can't open /dev/dma "); 35 return -1; 36 } 37 38 if (strcmp(argv[1], "nodma") == 0) 39 { 40 while (1) 41 { 42 ioctl(fd, MEM_CPY_NO_DMA); 43 } 44 } 45 else if (strcmp(argv[1], "dma") == 0) 46 { 47 while (1) 48 { 49 ioctl(fd, MEM_CPY_DMA); 50 } 51 } 52 else 53 { 54 print_usage(argv[0]); 55 return -1; 56 } 57 return 0; 58 }
makefile 代码如下:
1 KERN_DIR = /linux-2.6.32.2 2 3 all: 4 make -C $(KERN_DIR) M=`pwd` modules 5 6 clean: 7 rm -rf modules.order *.o *.ko *.cmd *.swp *.symvers *.mod.c 8 9 obj-m += dma.o