zoukankan      html  css  js  c++  java
  • 韦东山视频_第25课_字符设备驱动程序另一种写法

      旧字符设备驱动程序的框架:

    1、确定主设备号major;

    2、构造file_operations

    3、注册register_chrdev;

    4、创建类和设备结点

    这个框架有很大的弊端,弊端出现在注册函数register_chrdev上,其实现中有这么一句:

    cd = __register_chrdev_region(major, 0, 256, name);

    其向内核连续注册了256个此设备号,也就把major这个主设备号下的所有此设备号都占用了,而其实我们大部分情况下用不到这么多次设备号,造成了巨大的浪费。

    于是有了这样两个函数:

    ①  register_chrdev_region(dev_t from, unsigned count, const char  *name)
    ②  alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char  *name)

    register_chrdev_region函数的第一个参数dev_t from是一个unsigned型的数,高12位表示主设备号,低20位表示
    次设备号。因此这个参数中包含了起始的次设备号。第二个参数unsigned count表示需要注册的连续的次设备号的个
    数。最后一个参数表示设备或驱动的名字。在解释alloc_chrdev_region函数的参数之前先解释一下,这2个函数的区别和用法。register_chrdev_region这个函数在调用之前需要确定from这个参数的值,就是说设备号是已知的。而alloc_chrdev_region这个函数调用的时候不需要知道设备的设备号是多少,内核会动态分配一个给他。在
    分配好了之后会把设备号写到设备号变量的地址,所以我们看到在调用alloc_chrdev_region这个函数的时候它的第一个参数是设备号变量的地址。alloc_chrdev_region它的第二个参数是起始次设备号,第三个参数是要注册的次设备号个数,最后一个是设备或驱动的名字。
    这两个函数最终调用的都是__register_chrdev_region,同时需要跟cdev_init(struct cdev  *cdev, const struct
    file_operations *fops)和 cdev_add(struct cdev *p, dev_t dev, unsigned count)来配合使用。这2个函数的功能在以前的字符设备驱动程序中是在register_chrdev这一个函数中完成的,在这里需要我们自己来调用。这样一来,register_chrdev这个函数就被完美的替代了。我们就可以通过控制注册的起始次设备号以及多少个次设备号,实现了用多少注册多少的目的。

    字符设备驱动新框架:

    1、确定主设备号

    主设备号已知,
    devid = MKDEV(major, 0);
    register_chrdev_region(devid, HELLO_CNT, "hello");
    主设备号未知,
    alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello");
    major = MAJOR(devid);

    2、构造file_operations

    static struct file_operations hello_fops =

    {
      .owner = THIS_MODULE,
      .open = hello_open,
    };

    3、注册

    cdev_init(&hello_cdev, &hello_fops);
    cdev_add(&hello_cdev, devid, HELLO_CNT);

    4、创建类和设备

    hello_class = class_create(THIS_MODULE, CLASS_NAME);

    device_create(hello_class, NULL, MKDEV(hello_major, 0), NULL, DEV_NAME);

    一个实例程序如下:

      1 #include <linux/module.h>
      2 #include <linux/types.h>
      3 #include <linux/fs.h>
      4 #include <linux/errno.h>
      5 #include <linux/mm.h>
      6 #include <linux/sched.h>
      7 #include <linux/init.h>
      8 #include <linux/cdev.h>
      9 #include <asm/io.h>
     10 #include <asm/system.h>
     11 #include <asm/uaccess.h>
     12 #include <linux/device.h>
     13 
     14 #define HELLO_MAJOR    0    //预设的主设备号
     15 #define START_MINOR    0    //起始次设备号
     16 #define DEV_CNT        2    //要注册的设备数
     17 #define DRV_CNT        2    //要注册的驱动数
     18 #define DRIVER_NAME    "hello"    //驱动名
     19 #define CLASS_NAME    "hello_drv"    //类名
     20 #define DEV_NAME    "hello%d"    //设备名
     21 
     22 static int hello_major = HELLO_MAJOR;
     23 
     24 struct class *hello_class = NULL;
     25 
     26 //字符设备结构体
     27 struct hello_dev
     28 {
     29     struct cdev cdev;    //cdev结构体
     30     void *private_data;    //私有数据
     31 };
     32 
     33 struct hello_dev *hello_devp;    //设备结构体指针
     34 
     35 static int hello_open(struct inode *inode, struct file *file)
     36 {
     37     printk("hello_open
    ");
     38     return 0;
     39 }
     40 
     41 //文件操作结构体
     42 static const struct file_operations hello_fops = 
     43 {
     44     .owner = THIS_MODULE,
     45     .open = hello_open,
     46     //.read = hello_read,
     47     //.write = hello_write,
     48     //.ioctl = hello_ioctl,
     49 };
     50 
     51 //设备驱动模块加载函数
     52 int hello_init(void)
     53 {
     54     int i, res;
     55     dev_t devno;
     56 
     57     //申请次设备号范围、主设备号
     58     if (hello_major)
     59     {
     60         devno = MKDEV(hello_major, START_MINOR);
     61         res = register_chrdev_region(devno, DEV_CNT * DRV_CNT, DRIVER_NAME);
     62     }
     63     else
     64     {
     65         res = alloc_chrdev_region(&devno, START_MINOR, DEV_CNT * DRV_CNT, DRIVER_NAME);
     66         hello_major = MAJOR(devno);
     67     }
     68 
     69     if (res < 0)
     70     {
     71         return res;
     72     }
     73 
     74     //动态申请设备结构体内存
     75     hello_devp = kmalloc(DRV_CNT * sizeof(struct hello_dev), GFP_KERNEL);
     76     if (hello_devp == NULL)
     77     {
     78         res = - ENOMEM;
     79         goto fail_malloc;
     80     }
     81     memset(hello_devp, 0, DRV_CNT * sizeof(struct hello_dev));
     82 
     83     for (i = 0; i < DRV_CNT; i++)
     84     {
     85         cdev_init(&hello_devp[i].cdev, &hello_fops);
     86         hello_devp[i].cdev.owner = THIS_MODULE;
     87         cdev_add(&hello_devp[i].cdev, MKDEV(hello_major, i * DEV_CNT), DEV_CNT);
     88     }
     89 
     90     //自动创建设备节点
     91     hello_class = class_create(THIS_MODULE, CLASS_NAME);
     92     
     93     for (i = 0; i < DEV_CNT * DRV_CNT; i++)
     94     {
     95         device_create(hello_class, NULL, MKDEV(hello_major, i), NULL, DEV_NAME, i);
     96     }
     97     
     98     return 0;
     99 
    100 fail_malloc:
    101     unregister_chrdev_region(devno, DEV_CNT);
    102     return res;
    103 }
    104 
    105 //模块卸载函数
    106 void hello_exit(void)
    107 {
    108     int i;
    109     
    110     cdev_del(&hello_devp->cdev);
    111     kfree(hello_devp);
    112     unregister_chrdev_region(MKDEV(hello_major, 0), DEV_CNT * DRV_CNT);
    113     for (i = 0; i < DEV_CNT * DRV_CNT; i++)
    114     {
    115         device_destroy(hello_class, MKDEV(hello_major, i));
    116     }
    117     class_destroy(hello_class);
    118 }
    119 
    120 module_init(hello_init);
    121 module_exit(hello_exit);
    122 MODULE_LICENSE("GPL");
    123 MODULE_AUTHOR("Lcm");

    程序中,一个主设备号和一个次设备号共同对应一个设备节点,例子中有两个c_dev结构体,且对应同一个主设备号,每个c_dev结构体对应两个次设备号,即创建两个字符设备。

  • 相关阅读:
    线程数究竟设多少合理
    Elasticsearch 技术分析(八):剖析 Elasticsearch 的索引原理
    作为程序员你是如何学习的?
    系统运行缓慢,CPU 100%,以及Full GC次数过多问题的排查思路
    kms相关文档
    删除所有docker容器镜像
    ubuntu mysql5.7安装
    GORM自定义日志配置
    SQL清空全部表数据
    Nginx 证书
  • 原文地址:https://www.cnblogs.com/cmembd/p/3489825.html
Copyright © 2011-2022 走看看