韦东山视频_第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结构体对应两个次设备号,即创建两个字符设备。

原文地址:https://www.cnblogs.com/cmembd/p/3489825.html