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
  • 相关阅读:
    汉诺塔问题合集之汉诺塔6
    汉诺塔问题合集之汉诺塔5
    接口和抽象类有什么区别
    Java版本:JDK8的十大新特性介绍
    Linux的常用命令
    行为型模式——策略模式
    shell 后台执行命令
    ORA-01034:oracle不可用 的解决方法
    ORA-12514 TNS 监听程序当前无法识别连接描述符中请求服务 的解决方法
    linux下启动oracle
  • 原文地址:https://www.cnblogs.com/cmembd/p/3495228.html
Copyright © 2011-2022 走看看