一、杂项设备
在上次的实验中编写了一个内核模块,是使用内核提供的API实现了一个简单的虚拟设备的驱动,其中字符设备是使用函数alloc_chrdev_region -> cdev_alloc-> cdev_init-> cdev_add来注册和初始化的,而字符设备驱动也可以使用MISC机制来进行注册。
Linux把不符合预先确定的字符设备范畴称为杂项设备-MISC。
Linux的驱动设计是趋向于分层的,大多数设备都有自己归属的类型,例如按键、触摸屏属于输入设备,对于他们有一个input子系统框架。但是对于随机数发生器、时钟发生器等设备,无法明确其属于什么类型,对于这种设备统一使用misc驱动框架编写驱动程序。
MISC设备也是一个字符设备,主设备号是10,不同的杂项设备通过次设备号进行区分。
定义在major.h中,语句如下:
#define MISC_MAJOR 10
MISC设备通过结构体struct miscdevice来存储,源码如下:
struct miscdevice {
int minor; //指定次设备号
const char *name; //名字
const struct file_operations *fops; //文件操作
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};
minor字段相当于dev_t设备号,但是由于设定上MISC设备的主设备号是固定的,所以只需要一个int型的字段来区分次设备号就可以了
MISC的相关函数定义在drivers\char\misc.c文件中,接下来对几个重要函数进行详细分析。
二、入口函数
驱动的入口首先创建misc类,这将给misc设备实例生成设备节点时使用,然后定义文件操作集合misc_fops。
源码如下:
static struct class *misc_class;
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.llseek = noop_llseek,
};
可以看到misc_fops中只实现了open函数,此函数是用于转发系统调用到具体的misc设备实例中,是misc驱动框架的核心。
源码如下:
static int misc_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
//找到inode对应的设备号,再用MINOR宏求出次设备号后赋给minor变量
struct miscdevice *c;
int err = -ENODEV;
const struct file_operations *new_fops = NULL;
mutex_lock(&misc_mtx); //互斥锁
//在misc设备双链表中找minor对应的misc设备
list_for_each_entry(c, &misc_list, list) {
if (c->minor == minor) {
new_fops = fops_get(c->fops);//获得它的文件操作
break;
}
}
//如果new_fops为空进行的纠正
if (!new_fops) {
mutex_unlock(&misc_mtx);
request_module("char-major-%d-%d", MISC_MAJOR, minor);
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list) {
if (c->minor == minor) {
new_fops = fops_get(c->fops);
break;
}
}
if (!new_fops)
goto fail;
}
/*
* Place the miscdevice in the file's
* private_data so it can be used by the
* file operations, including f_op->open below
*/
file->private_data = c;
err = 0;
replace_fops(file, new_fops);
//replace_fops只能用用在open()实例中,new_fops必须非空,结果是用new_fops的内容填充了open后生成的file结构体中的>f_op字段
if (file->f_op->open)
err = file->f_op->open(inode, file);
fail:
mutex_unlock(&misc_mtx);
return err;
}
可以看出这个函数和普通字符设备chrdev_open的原理基本一致,文件操作替换过程示意图如下:
三、misc_init
misc_init函数完成misc子系统在内核中的初始化,主要包括创建文件、创建misc类、注册字符设备等一系列操作,最后注册到内核中。
源码如下:
static int __init misc_init(void)
{
int err;
struct proc_dir_entry *ret;
//如果使用proc文件系统,则创建misc项
ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops);
misc_class = class_create(THIS_MODULE, "misc");
//在/sys/class/目录下创建一个名为misc的类
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class))
goto fail_remove;
err = -EIO;
//注册misc类的设备驱动,最终调用内部函数__register_chrdev_region来注册一个字符设备
if (register_chrdev(MISC_MAJOR, "misc", &misc_fops))
goto fail_printk;
misc_class->devnode = misc_devnode;
return