zoukankan      html  css  js  c++  java
  • 韦东山视频_第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
    ");
     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
  • 相关阅读:
    在Centos 7下编译openwrt+njit-client
    开博随笔
    Chapter 6. Statements
    Chapter 4. Arrays and Pointers
    Chapter 3. Library Types
    Chapter 2.  Variables and Basic Types
    关于stm32不常用的中断,如何添加, 比如timer10 timer11等
    keil 报错 expected an identifier
    案例分析 串口的地不要接到电源上 会烧掉
    案例分析 CAN OPEN 调试记录 进度
  • 原文地址:https://www.cnblogs.com/cmembd/p/3495228.html
Copyright © 2011-2022 走看看