韦东山视频_第27课_dma设备驱动程序

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\n");
 78             }
 79             else
 80             {
 81                 printk("MEM_CPY_DMA ERROR\n");
 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\n");
107             }
108             else
109             {
110                 printk("MEM_CPY_DMA ERROR\n");
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\n");
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\n");
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\n");
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:\n");
17     printf("%s <nodma | dma>\n", 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\n");
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

 

posted @ 2013-12-28 12:34  ★行云流水★  阅读(674)  评论(0编辑  收藏  举报