zoukankan      html  css  js  c++  java
  • linux设备驱动之字符设备驱动模型(1)

     一:字符设备驱动

      在linux下面,应用层看到的一切皆为文件(名字)所有的设备都是文件,都可以调用open,read,write来操作,而在内核中每个中每个设备有唯一的对应一个设备号;

        APP   (名字)            
        OS      (设备号)
        HW

      下面我们写一个简单的字符设备驱动,再应用层我们打开一个设备,看看它是怎么来调用内核中的函数的;

      首先我们使用命令mknod 创建一个名为wangcai的设备名,在应用曾打开它;

      mknod /dev/设备文件名  c   主设备号   次设备号

      命令 mknod wangcai c 9  0

      在注册字符设备是我们需要用到这几个struct cdev,原型如下:

    1 struct cdev
    2 {
    3     struct kobject kobj;          // 内嵌的kobject对象,描述设备引用计数
    4     struct module *owner;         // 所属模块,一般赋值为THIS_MODULE
    5     struct file_operations *ops;  // 文件操作结构体
    6     struct list_head list;
    7     dev_t dev;                    // 设备号
    8     unsigned int count;
    9 };

       cdev结构体的dev_t定义了设备号,32位。高12位为主设备号,低20位为次设备号。

      (1)应用层打开设备

     1 #include <stdio.h>
     2 #include <fcntl.h>
     3 
     4 int main()
     5 {
     6     int fd = 0;
     7     fd = open("wangcai", O_RDWR);
     8     if(fd < 0) {
     9         perror("open error");
    10         return 1;
    11     }
    12 
    13     return 0;
    14 }

      (2)在内核中册了设备文件wangcai和方法ops

      

     1 #include <linux/init.h>
     2 #include <linux/module.h>
     3 #include <linux/cdev.h>
     4 #include <linux/fs.h>
     5 
     6 MODULE_LICENSE("GPL");
     7 MODULE_AUTHOR("bunfly");
     8 int file_open(struct inode *no, struct file *fp);
     9 
    10 struct cdev wangcai;//设备
    11 struct file_operations fops;//方法
    12 dev_t devno;
    13 
    14 int bunfly_init()
    15 {
    16     fops.open = file_open;//调用open        
    17     //wangcai.ops = &fops;
    18     cdev_init(&wangcai, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
    19     //wangcai.dev = (9, 0);
    20     devno = MKDEV(9, 0);//字符设备号注册
    21     //insert_list(&wangcai);
    22     cdev_add(&wangcai, devno, 1);// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中
    23 
    24 }
    25 
    26 int bunfly_exit()
    27 {
    28     printk("this is bunfly_exit
    ");
    29 }
    30 
    31 module_init(bunfly_init);
    32 module_exit(bunfly_exit);
    33 
    34 int file_open(struct inode *no, struct file *fp)
    35 {
    36     printk("this is file_open
    ");
    37 
    38     return 0;
    39 }

      下面代码是实现字符设备的读写操作:

      (1)应用层

      write:

    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>
    
    int main(int argc, char *argv[])
    {
        if(argc < 3) {
            printf("using %s <dev> <msg>
    ", argv[0]);
            return 1;
        }
    
        int fd = 0;
        int ret = 0;
    
        fd = open(argv[1], O_RDWR);
        if(fd < 0) {
            perror("open error");
    
            return 1;
        }
    
        ret = write(fd, argv[2], strlen(argv[2]));
        if(ret < 0) {
            perror("write error");
            return 1;
        }
        close(fd);
    
        return 0;
    }

      read:

      

     1 #include <stdio.h>
     2 #include <fcntl.h>
     3 #include <string.h>
     4 
     5 int main(int argc, char *argv[])
     6 {
     7     if(argc != 2) {
     8         printf("using %s <dev>
    ", argv[0]);
     9         return 1;
    10     }
    11 
    12     int fd = 0;
    13     int ret = 0;
    14     unsigned char data[1024] = {0};
    15 
    16     fd = open(argv[1], O_RDWR);
    17     if(fd < 0) {
    18         perror("open error");
    19 
    20         return 1;
    21     }
    22 
    23     ret = read(fd, data, 1024);
    24     if(ret < 0) {
    25         perror("write error");
    26         return 1;
    27     }
    28 
    29     printf("data is: %s
    ", data);
    30 
    31     close(fd);
    32 
    33     return 0;
    34 }

      

      (2)内核  

     1 #include <linux/init.h>
     2 #include <linux/module.h>
     3 #include <linux/cdev.h>
     4 #include <linux/fs.h>
     5 
     6 MODULE_LICENSE("GPL");
     7 MODULE_AUTHOR("bunfly");
     8 
     9 int file_open(struct inode *no, struct file *fp);
    10 ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t * loff);
    11 ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff); 
    12 
    13 struct cdev wangcai;//设备
    14 struct file_operations fops;//方法
    15 dev_t devno;
    16 unsigned char data[1024] = {0};
    17 
    18 int bunfly_init()
    19 {
    20     fops.open = file_open;        
    21     fops.read = file_read;
    22     fops.write = file_write;
    23     
    24     //wangcai.ops = &fops;
    25     cdev_init(&wangcai, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
    26     //wangcai.dev = (9, 0);
    27     devno = MKDEV(9, 0);//字符设备号注册
    28     //insert_list(&wangcai);
    29     cdev_add(&wangcai, devno, 1);// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中
    30 
    31 }
    32 
    33 int bunfly_exit()
    34 {
    35     cdev_del(&wangcai);   /*注销设备*/
    36       unregister_chrdev_region(MKDEV(9, 0), 1);
    37     printk("this is bunfly_exit
    ");
    38 }
    39 
    40 module_init(bunfly_init);
    41 module_exit(bunfly_exit);
    42 
    43 int file_open(struct inode *no, struct file *fp)
    44 {    
    45     return 0;
    46 }
    47 ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t *loff)
    48 {
    49     strcpy(buff, data);    
    50     return size;
    51 }
    52 
    53 ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff)
    54 {
    55     memset(data, 0, 1024);
    56     strcpy(data, buff);
    57     return size;
    58 }
    59

      下面代码是通过ioctl()函数来控制灯亮灯灭:

      (1)应用层

      

    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    
    int main(int argc, char *argv[])
    {
        if(argc != 3) {
            printf("using %s <devname> 1:0
    ", argv[0]);
            return 1;
        }
    
        int fd = 0;
        fd = open(argv[2], O_RDWR);
        if(fd < 0) {
            perror("open");
            return 1;
        }    
    
        ioctl(fd, atoi(argv[2]));
        close(fd);
        return 0;
    }

      (2)内核

      

     1 #include <linux/init.h>
     2 #include <linux/module.h>
     3 #include <linux/cdev.h>
     4 #include <linux/fs.h>
     5 #include <linux/io.h>
     6 #include <linux/gpio.h>
     7 
     8 MODULE_LICENSE("GPL");
     9 MODULE_AUTHOR("bunfly");
    10 
    11 long my_ioctl(struct file *fp, unsigned int id, unsigned long fd);
    12 int file_open(struct inode *no, struct file *fp);
    13 
    14 struct cdev wangcai;
    15 struct file_operations fops;
    16 dev_t devno;
    17 unsigned long gpio_virt;
    18 unsigned long *gpm4con, *gpm4dat;
    19 
    20 int bunfly_init()
    21 {
    22     fops.open = file_open;        
    23     fops.unlocked_ioctl = my_ioctl;
    24 
    25     //wangcai.ops = &fops;
    26     cdev_init(&wangcai, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
    27     //wangcai.dev = (9, 0);
    28     devno = MKDEV(9, 0);//字符设备号注册
    29     //insert_list(&wangcai);
    30     cdev_add(&wangcai, devno, 1);// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中
    31 
    32 
    33     gpio_virt = ioremap(0x11000000, SZ_4K);//led物理地址到虚拟地址映射
    34       gpm4con = gpio_virt + 0x02e0;
    35      gpm4dat = gpio_virt + 0x02e4;
    36 
    37     return 0;
    38 }
    39 
    40 int bunfly_exit()
    41 {
    42     cdev_del(&wangcai);   /*注销设备*/
    43       unregister_chrdev_region(MKDEV(9, 0), 1);
    44     printk("this is bunfly_exit
    ");
    45 
    46     return 0;
    47 }
    48 
    49 module_init(bunfly_init);
    50 module_exit(bunfly_exit);
    51 
    52 int file_open(struct inode *no, struct file *fp)
    53 {    
    54     return 0;
    55 }
    56  
    57 long my_ioctl(struct file *fp, unsigned int id, unsigned long fd)
    58 {
    59     if(id == 1) {
    60         *gpm4con = 0x1111;
    61         *gpm4dat = 0x0;
    62     }
    63     if(id == 0) {
    64         *gpm4con = 0x1111;
    65         *gpm4dat = 0xf;
    66     }
    67 }

         通过上面的代码我们已经了解了字符设备驱动的原理,在linux下应用层看到的设备都只 是一个名字,应用层打开一个设备最终会调到内核中的file_operations方法来进行读写操作,如果我们只创建一个的设备的时候,我们可以对他正 常的读写,那如果当我们有两个设备时,我们是否还能正常的进行读写操作,明显是存在问题的,就是第二次的数据会将第一次的数据覆盖,因为我们只有一个数据存储的data;那么解决这个问题的方法就是封装;

      下面是具体代码:

      

     1 #include <linux/init.h>
     2 #include <linux/module.h>
     3 #include <linux/cdev.h>
     4 #include <linux/fs.h>
     5 
     6 MODULE_LICENSE("GPL");
     7 MODULE_AUTHOR("bunfly");
     8 
     9 int file_open(struct inode *no, struct file *fp);
    10 ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t *loff);
    11 ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff); 
    12 
    13 struct file_operations fops;//方法
    14 
    15 /*封装*/
    16 struct bunfly_cdev {
    17     dev_t devno;//设备号
    18     struct cdev cdev;
    19     unsigned char data[1024];    
    20 };
    21 
    22 int bunfly_init()
    23 {
    24     fops.open = file_open;        
    25     fops.read = file_read;
    26     fops.write = file_write;
    27     
    28     struct bunfly_cdev wangcai;
    29     cdev_init(&wangcai.cdev, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
    30     wangcai.devno = MKDEV(9, 0);//注册设备号
    31     cdev_add(&wangcai.cdev, wangcai.devno, 1);// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中
    32 
    33     struct bunfly_cdev tugou;
    34     cdev_init(&tugou.cdev, &fops);
    35     tugou.devno = MKDEV(9, 1);
    36     cdev_add(&tugou.cdev, tugou.devno, 1);
    37 
    38     return 0;
    39 }
    40 
    41 int bunfly_exit()
    42 {
    43     printk("this is bunfly_exit
    ");
    44 
    45     return 0;
    46 }
    47 
    48 module_init(bunfly_init);
    49 module_exit(bunfly_exit);
    50 
    51 int file_open(struct inode *no, struct file *fp)
    52 {
    53     struct cdev *addr = no->i_cdev;//找到struct cdev dev 在struct bunfly_cdev中的地址
    54     struct bunfly_cdev *this = container_of(addr, struct bunfly_cdev, cdev);
    55     fp->private_data = this;       //父类在子类中的地址  //子类类型       
    56 
    57     return 0;
    58 }
    59 ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t *loff)
    60 {
    61     struct bunfly_cdev *this = fp->private_data;
    62     strcpy(buff,this-> data);    
    63 
    64     return size;
    65 }
    66 
    67 ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff)
    68 {
    69     struct bunfly_cdev *this = fp->private_data;
    70     memset(this->data, 0, 1024);
    71     strcpy(this->data, buff);
    72 
    73     return size;
    74 }
    75  

      代码中有看到了container_of,再次强调掌握,还需要注意的是:

      (1)每一个设备文件仅有inode结构体 ;

      (2)每打开一次文件就创建一个file 结构体;

      

  • 相关阅读:
    【转载】这才是真正的表扩展方案
    【转载】啥,又要为表增加一列属性?
    【转载】这才是真正的分布式锁
    mysql备份表sql
    selenium定位当前处于那个iframe(frame)中
    MQ手动推送消息
    报表导出时间格式数据多‘0‘
    python里的原始字符串
    qq邮箱设置授权码方法(jenkins)
    Apache与Tomcat有什么关系和区别(转)
  • 原文地址:https://www.cnblogs.com/wenqiang/p/4803161.html
Copyright © 2011-2022 走看看