Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1176942
  • 博文数量: 573
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 66
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-28 16:21
文章分类

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-04 17:07:19

驱动代码:
dma.c文件

点击(此处)折叠或打开

  1. /*
  2. 1,DMA : Direct Memory Access 直接内存存取。
  3. 2,CPU同一时间,只能做一件事,如果拷贝的数据过大,整个CPU只能做拷贝的动作了,
  4. 整个系统就相当于卡死了,另外的程序就不能及时响应。所以,就需要DMA了。
  5. 3,只要把源,目的,长度告诉DMA,就可以启动DMA进行数据拷贝,是DMA硬件自动拷贝的,
  6. 拷贝过程中和CPU没有任何关系,CPU可以执行其他代码,拷贝完成后,DMA会发出一个中断给CPU。
  7. 4,即,可以认为DMA是另外一个有些智能的微控制器,它只负责数据的传输。
  8. 5, 2440有自带DMA, 是一个独立于CPU的控制部件,类似的,IIC控制器是2440的一个独立于CPU的控制部件。
  9. */

  10. #include <linux/kernel.h>
  11. #include <linux/init.h>
  12. #include <linux/module.h>
  13. #include <linux/slab.h>
  14. #include <linux/jiffies.h>
  15. #include <linux/mutex.h>
  16. #include <linux/fs.h>
  17. #include <asm/uaccess.h>
  18. #include <linux/device.h>
  19. #include <linux/dma-mapping.h>
  20. #include <linux/poll.h>

  21. #include <linux/irq.h>
  22. #include <asm/irq.h>
  23. #include <linux/interrupt.h>

  24. /* 中断事件标志, 中断服务程序将它置1,mydma_ioctl将它清0 */
  25. static volatile int ev_dma = 0;
  26. static DECLARE_WAIT_QUEUE_HEAD(dma_waitq);

  27. #define DEVICE_NAME    "mydma"
  28. static int MYDRIVER_Major = 0;

  29. static struct class * mydma_class;
  30. //static struct class_device    * mydma_class_dev;

  31. /*注意:kmalloc 分配的虚拟地址是连续的,物理地址是不连续的。
  32. dma_alloc_writecombine 分配的虚拟地址是连续的,物理地址也是连续的。
  33. DMA没这么智能,传递给MDA的是物理地址,且必须是连续的,
  34. 所以要使用函数:dma_alloc_writecombine来分配内存。
  35. */
  36. static char * src = NULL;                        //虚拟源地址
  37. static unsigned long src_phys = 0;    //源的物理地址
  38. static char * desc = NULL;                    //虚拟的目的地址
  39. static unsigned long desc_phys = 0;    //目的的物理地址
  40. #define BUF_SIZE (512*1024)

  41. /* DMA相关的寄存器
  42. 写成结构体的原因是:一次内存映射所有的寄存器。
  43.  */
  44. struct s3c_dma_regs
  45. {
  46.         unsigned long disrc;            /*0x4B000000 : DMA初始源寄存器*/
  47.         unsigned long disrcc;            /*0x4B000004 : DMA初始源控制寄存器*/
  48.         unsigned long didst;            /*0x4B000008 : DMA初始目标寄存器*/
  49.         unsigned long didstc;            /*0x4B00000C : DMA初始目标控制寄存器*/
  50.         unsigned long dcon;                /*0x4B000010 : DMA控制寄存器*/
  51.         unsigned long dstat;            /*0x4B000014 : DMA状态寄存器*/
  52.         unsigned long dcsrc;            /*0x4B000018 : DMA当前源寄存器*/
  53.         unsigned long dcdst;            /*0x4B00001C : DMA当前目标寄存器*/
  54.         unsigned long dmasktrig;    /*0x4B000020 : DMA触发屏蔽寄存器*/
  55. };
  56. #define MDA0_BASE_ADDR (0x4B000000)
  57. #define MDA1_BASE_ADDR (0x4B000040)
  58. #define MDA2_BASE_ADDR (0x4B000080)
  59. #define MDA3_BASE_ADDR (0x4B0000C0)
  60. static volatile struct s3c_dma_regs * dma_regs;

  61. static int mydma_open(struct inode *inode, struct file *file)
  62. {
  63.     printk("DEVICE:mydma_open Called!\n");
  64.     return 0;
  65. }

  66. static int mydma_release(struct inode *inode, struct file *file)
  67. {
  68.     printk("DEVICE:mydma_release Called!\n");
  69.     return 0;
  70. }

  71. #define MEM_CPY_NO_DMA 0 //不使用DMA
  72. #define MEM_CPY_DMA 1    //使用DMA
  73. static int mydma_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
  74. {
  75.         int i = 0;
  76.         int ret = 0;
  77.         memset(src, 0xAA, BUF_SIZE);
  78.         memset(desc, 0x55, BUF_SIZE);
  79.         switch(cmd)
  80.         {
  81.                 case MEM_CPY_NO_DMA :
  82.                 {
  83.                         for(i=0; i<BUF_SIZE; i++)
  84.                         {
  85.                                 desc[i] = src[i];
  86.                         }
  87.                         ret = memcmp((void*)src, (void*)desc, BUF_SIZE-1);
  88.                         if(ret == 0)
  89.                         {
  90.                                 printk("DEVICE:MEM_CPY_NO_DMA ok!\n");
  91.                         }
  92.                         else
  93.                         {
  94.                                 printk("DEVICE:MEM_CPY_NO_DMA err ret=[%d]!\n", ret);
  95.                         }
  96.                         break;
  97.                 }
  98.                 case MEM_CPY_DMA :
  99.                 {
  100.                         /*
  101.                         注意 : 本程序是直接操作DMA相关的寄存器,实际上三星公司在内核中已经提供了一堆函数,
  102.                         可以直接操作DMA控制器。
  103.                         */
  104.                         //1,把源,目的,长度告诉DAM
  105.                         //1.1),把源地址告诉DMA
  106.                         dma_regs->disrc = src_phys; //源的物理地址
  107.                         /*[0]=0:表示源地址递增 =1:表示源地址固定
  108.                          [1]=0:表示源在系统总线上 =1:表示在外设总线上*/
  109.                         dma_regs->disrcc = (0<<1)|(0<<0);

  110.                         //1.2),把目的地址告诉DMA
  111.                         dma_regs->didst = desc_phys; //目的的物理地址
  112.                         /*[0]=0:表示目的地址递增 =1:表示目的地址固定
  113.                          [1]=0:表示目的地址在系统总线上 =1:表示在外设总线上
  114.                          [2]=0:TC到达0时发生中断 =1:执行完自动再加载后发生中断*/
  115.                         dma_regs->didstc = (0<<2)|(0<<1)|(0<<0);

  116.                         //1.3),把目的地址告诉DMA
  117.                         /*[21:20]DSZ : 每1次读写过程,读写数据的大小(1,2,4字节)
  118.                          [ 28]TSZ : 单次传输 : 每1次传输,有1次读写过程。
  119.                                           burst传输 : 每1次传输,有4次读写过程。
  120.                          [19: 0]TC : 传输次数。
  121.                          总长度 = TC*TSZ(1或4)*DSZ(1或2或4)
  122.                          */
  123.                         dma_regs->dcon = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(BUF_SIZE<<0);
  124.                         
  125.                         //2,启动DMA
  126.                         dma_regs->dmasktrig = (1<<1)|(1<<0);
  127.                         
  128.                         //3,CPU怎么知道DMA什么时候完成?
  129.                         //CPU进入休眠状态,会在中断函数中唤醒。
  130.                         /*
  131.                         函数功能:进程开始休眠,表现为程序阻塞在这里,不继续向下执行。
  132.                         注意:1,应用程序ioctl也会阻塞,直到驱动ioctl被唤醒。
  133.                          2,休眠:意味着进程从调度器的运行队列中移走,不会被任何cpu调度,直到被唤醒
  134.                          3, 本进程被唤醒的条件:1),函数第2个参数为真。并且 2),调用wake_up_interruptible()函数。*/
  135.                         ev_dma = 0;
  136.                         wait_event_interruptible(dma_waitq, ev_dma);

  137.                         ret = memcmp((void*)src, (void*)desc, BUF_SIZE-1);
  138.                         if(ret == 0)
  139.                         {
  140.                                 printk("DEVICE:MEM_CPY_DMA ok!\n");
  141.                         }
  142.                         else
  143.                         {
  144.                                 printk("DEVICE:MEM_CPY_NO_DMA err ret=[%d]!\n", ret);
  145.                         }
  146.                         
  147.                         break;
  148.                 }
  149.                 default :
  150.                 {
  151.                         printk("DEVICE:cmd=[%d] err\n", cmd);
  152.                         break;
  153.                 }
  154.         }
  155.         
  156.         return 0;
  157. }

  158. /*
  159. static : 表示只在本文件中使用
  160. */
  161. static struct file_operations mydma_fops =
  162. {
  163.         .owner = THIS_MODULE, /*固定写这个*/
  164.         .open = mydma_open,
  165.         .release = mydma_release,
  166.         .ioctl = mydma_ioctl,
  167. };

  168. /*IRQ_DMA1 中断处理函数*/
  169. static irqreturn_t dma_interrupt(int irq, void *dev_id)
  170. {
  171.         //唤醒
  172.         ev_dma = 1; /* 睡眠函数的第2个参数为真 */
  173.         //printk("Driver : dma_interrupt : wake_up_interruptible begin!\n");

  174.     wake_up_interruptible(&dma_waitq); /* 唤醒休眠的进程 */
  175.     //printk("Driver : dma_interrupt : wake_up_interruptible end!\n");

  176.         return IRQ_HANDLED;
  177. }

  178. static int dma_init(void)
  179. {
  180.         unsigned int ret = 0;
  181.         printk("DEVICE:dma_init\n");
  182.         
  183.         ret = request_irq(IRQ_DMA1, dma_interrupt, 0, "dma_irq", NULL);
  184.         if(ret != 0)
  185.         {
  186.                 printk("DEVICE: request_irq IRQ_DMA1 :[%d],[%d]!\n", EIO, ret);
  187.                 return -EIO;
  188.         }

  189.         //分配源对应的缓冲区:返回的src是虚拟地址, src_phys是对应的物理地址
  190.         src = dma_alloc_writecombine(NULL, BUF_SIZE, (dma_addr_t *)&src_phys, GFP_KERNEL);
  191.         if(src == NULL)
  192.         {
  193.                 disable_irq(IRQ_DMA1);
  194.                 free_irq(IRQ_DMA1, NULL);
  195.                 printk("DEVICE:dma_alloc_writecombine() for src err!\n");
  196.                 return -1;
  197.         }

  198.         //分配目的对应的缓冲区
  199.         desc = dma_alloc_writecombine(NULL, BUF_SIZE, (dma_addr_t *)&desc_phys, GFP_KERNEL);
  200.         if(desc == NULL)
  201.         {
  202.                 disable_irq(IRQ_DMA1);
  203.                 free_irq(IRQ_DMA1, NULL);
  204.                 dma_free_writecombine(NULL, BUF_SIZE, src, (dma_addr_t)src_phys);
  205.                 printk("DEVICE:dma_alloc_writecombine() for desc err!\n");
  206.                 return -1;
  207.         }

  208.         /* 设置S3C2440的DMA寄存器 */
  209.         dma_regs = ioremap(MDA1_BASE_ADDR, sizeof(struct s3c_dma_regs));
  210.         if(dma_regs == NULL)
  211.         {
  212.                 printk("DEVICE:ioremap() dma_regs err!\n");
  213.                 return -1;
  214.         }
  215.         
  216.         MYDRIVER_Major = register_chrdev(0, DEVICE_NAME, &mydma_fops);
  217.         if(MYDRIVER_Major < 0)
  218.         {
  219.                 printk(DEVICE_NAME " can't register major number\n");
  220.                 return MYDRIVER_Major;
  221.         }
  222.         printk("DEVICE:register mydma OK! Major = [%d]\n", MYDRIVER_Major);

  223.         //自动创建一个设备节点,节点名为DEVICE_NAME
  224.         mydma_class = class_create(THIS_MODULE, DEVICE_NAME);
  225.         if(IS_ERR(mydma_class))
  226.         {
  227.                 printk("DEVICE:class_create err!\n");
  228.                 return -1;
  229.         }
  230.         /* 为了让mdev根据这些信息来创建设备节点 */
  231.         device_create(mydma_class, NULL, MKDEV(MYDRIVER_Major, 0), NULL, DEVICE_NAME); /* /dev/leddriver */

  232.         printk("DEVICE:initialized\n");
  233.         return 0;
  234. }

  235. static void dma_exit(void)
  236. {
  237.         iounmap(dma_regs);

  238.         device_destroy(mydma_class, MKDEV(MYDRIVER_Major, 0));
  239.         class_destroy(mydma_class);
  240.         unregister_chrdev(MYDRIVER_Major, DEVICE_NAME);

  241.         dma_free_writecombine(NULL, BUF_SIZE, src, (dma_addr_t)src_phys);
  242.         dma_free_writecombine(NULL, BUF_SIZE, desc, (dma_addr_t)desc_phys);

  243.         disable_irq(IRQ_DMA1);
  244.         free_irq(IRQ_DMA1, NULL);

  245.         return;
  246. }

  247. module_init(dma_init);
  248. module_exit(dma_exit);

  249. MODULE_LICENSE("GPL");


  250. /*
  251. 分析 :
  252. # cat /proc/interrupts //看看哪些中断已经被用了?
  253.            CPU0
  254.  30: 9189 s3c S3C2410 Timer Tick
  255.  33: 0 s3c s3c-mci //SD卡,这个说明 IRQ_DMA0 中断已经被用了。
  256.  37: 21 s3c s3c-mci //SD卡,这个说明 IRQ_DMA3 中断已经被用了。
  257.  41: 1 s3c s3c2410_udc
  258.  42: 0 s3c ohci_hcd:usb1
  259.  43: 0 s3c s3c2440-i2c
  260.  51: 1304 s3c-ext eth0
  261.  60: 0 s3c-ext s3c-mci
  262.  70: 48 s3c-uart0 s3c2440-uart
  263.  71: 253 s3c-uart0 s3c2440-uart
  264.  80: 0 s3c-adc adc
  265. Err: 0

  266. 卸载ko的时候,还有问题
  267. */
makefile文件

点击(此处)折叠或打开

  1. # File: Makefile
  2. # wangxiancai

  3. MODEXT = ko
  4. # INSTALLDIR=/home/wangxc/linux/rootfs/nfs_2.6.13/wxc/driver/chardriver/ko
  5. INSTALLDIR=/home/wangxc/linux/rootfs/nfs_2.6.30/wxc/driver/chardriver/ko
  6. # CROSS=/home/wangxc/linux/toolchain/crosstools_3.4.1_softfloat/arm-linux/gcc-3.4.1-glibc-2.3.3/bin/arm-linux-
  7. CROSS=/home/wangxc/linux/toolchain/crosstools_4.4.3_softfloat/bin/arm-linux-
  8. # KERNELDIR=/home/wangxc/linux/kernel/kernel-2.6.13
  9. KERNELDIR=/home/wangxc/linux/kernel/kerner-2.6.30

  10. CC= $(CROSS)gcc
  11. LD= $(CROSS)ld

  12. #############################################################################
  13. # Compiler Flags
  14. #############################################################################
  15. EXTRA_CFLAGS += -I$(KERNELDIR)/include
  16. #############################################################################
  17. # Make Targets
  18. #############################################################################
  19. obj-m := dma.o
  20. default:
  21.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  22. # Otherwise we were called directly from the command line; invoke the kernel build system.

  23. install: default
  24.     cp -rf $(PWD)/dma.$(MODEXT) $(INSTALLDIR)

  25. clean:
  26.     rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.bak modules.order Module.symvers
应用程序代码
dmatest.c文件

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <fcntl.h>
  5. #include <sys/ioctl.h>
  6. #include <stdlib.h>
  7. #include <errno.h>


  8. /*下面是ioctl识别的宏定义 和内核中定义的一样*/
  9. #define MEM_CPY_NO_DMA 0 //不使用DMA
  10. #define MEM_CPY_DMA 1    //使用DMA

  11. /* 命令行参数如下:
  12.  * dmatest nodma 100                //oled初始化
  13.  * dmatest dma 100                //oled清屏
  14.  */

  15. void print_usage(char *cmd);

  16. int main(int argc, char **argv)
  17. {
  18.     int fd;

  19.     /*判断参数的合法性*/
  20.     if(argc != 2)
  21.     {
  22.         print_usage(argv[0]);
  23.         return -1;
  24.     }
  25.         
  26.     /*打开oled文件*/
  27.     printf("open begin:\n");
  28.     fd = open("/dev/mydma", O_RDWR);
  29.     if(fd < 0)
  30.     {
  31.         printf("can't open /dev/mydma\n");
  32.         return -1;
  33.     }
  34.     printf("open succ!\n");
  35.     
  36.     if(strcmp(argv[1], "nodma") == 0)
  37.     {
  38.             while(1)
  39.             {
  40.                     ioctl(fd, MEM_CPY_NO_DMA);
  41.             }
  42.     }
  43.     else if(strcmp(argv[1], "dma") == 0)
  44.     {
  45.             while(1)
  46.             {
  47.                     ioctl(fd, MEM_CPY_DMA);
  48.             }
  49.     }
  50.     else
  51.     {
  52.             print_usage(argv[0]);
  53.             return -1;
  54.     }
  55.     close(fd);

  56.     return 0;
  57. }


  58. void print_usage(char *cmd)
  59. {
  60.     printf("Usage:\n");
  61.     printf("%s [nodma|dma]\n", cmd);
  62. }


  63. /*
  64. 测试 :
  65. 1, 刚才开始的测试的时候,由于open函数使用的是 RDONLY 只读打开,驱动函数 write不能调用,
  66. 使用 O_RDWR 可读可写方式打开,才能正确
  67. 2,
  68. # ./dmatest
  69. Usage:
  70. ./dmatest [nodma|dma]

  71. # nohup ./dmatest nodma & //后台执行
  72. 会不停的打印一些信息:DEVICE:MEM_CPY_NO_DMA
  73. 如果这个时候 执行 ls命令,
  74. 会看见ls的结果不是立马执行的。要等一会才会执行。

  75. 重新启动开发板:
  76. # nohup ./dmatest dma & //后台执行
  77. 会不停的打印一些信息:
  78. 如果这个时候 执行 ls命令,
  79. 会看见ls的结果是立马执行的。不用等,立即执行。

  80. 测试结果说明 : 如果使用DMA,cpu会解放出来,有更多的时间可以干其他事情
  81. */
makefile文件

点击(此处)折叠或打开

  1. # CROSS=/home/wangxc/linux/toolchain/crosstools_3.4.1_softfloat/arm-linux/gcc-3.4.1-glibc-2.3.3/bin/arm-linux-gcc
  2. CROSS=/home/wangxc/linux/toolchain/crosstools_4.4.3_softfloat/bin/arm-linux-gcc
  3. all:dmatest
  4. dmatest:dmatest.c
  5.     $(CROSS) -o dmatest dmatest.c
  6. clean:
  7.     rm -vf dmatest.o dmatest

阅读(963) | 评论(0) | 转发(0) |
0

上一篇:linux 下IIC驱动程序

下一篇:linux下 SPI驱动

给主人留下些什么吧!~~