1.杂项设备(misc device)
杂项设备也是嵌入式系统中用得比较多的一种设备驱动。在 Linux 内核的include/linux目录下有miscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,所有这些设备采用主编号10 ,一起归于misc device,其实misc_register就是用主标号10调用register_chrdev()的,只不过misc是将一些字符设备存放在misc类中。换句话说,misc设备其实也就是特殊的字符设备。
在Linux驱动中把无法归类的五花八门的设备定义为混杂设备(用miscdevice结构体表述)。miscdevice共享一个主设备号MISC_MAJOR(即10),但次设备号不同。所有的miscdevice设备形成了一个链表,对设备访问时内核根据次设备号查找对应的miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。在内核中用struct miscdevice表示miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。miscdevice的API实现在drivers/char/misc.c中,misc设备的初始化,注册,注销都在这个文件中。在内核中,misc杂项设备驱动接口是对一些字符设备的简单封装,他们共享一个主设备号,有不同的次设备号,共享一个open调用,其他的操作函数在打开后运用linux驱动程序的方法重载进行装载。
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.llseek = noop_llseek,
};
static int __init misc_init(void)
{
int err;
struct proc_dir_entry *ret;
ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops);/*创建一个proc入口项*/
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;
if (register_chrdev(MISC_MAJOR, "misc", &misc_fops))/*注册设备,其中设备的主设备号为MISC_MAJOR,为10。设备名为misc,misc_fops是操作函数的集合
goto fail_printk;
misc_class->devnode = misc_devnode;
return 0;
fail_printk:
pr_err(“unable to get major %d for misc devices\n”, MISC_MAJOR);
class_destroy(misc_class);
fail_remove:
if (ret)
remove_proc_entry(“misc”, NULL);
return err;
}
以上是misc 设备子系统的注册,主要是先向系统注册一类主设备号为10 的设备;
int __register_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name,
const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
int err = -ENOMEM;
cd = __register_chrdev_region(major, 0, 256, name);//申请一类设备号,主设备号为10,此设备号为0~256;
if (IS_ERR(cd))
return PTR_ERR(cd);
cdev = cdev_alloc(); //分配一个cdev 字符设备
if (!cdev)
goto out2;
cdev->owner = fops->owner;
cdev->ops = fops; //初始化这一类字符设备的 操作函数
kobject_set_name(&cdev->kobj, "%s", name);
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);//将主设备号为major ,次设备号从baseminor 开始的major 个元素作为一个字符设备加入到内核中
if (err)
goto out;
cd->cdev = cdev;
return major ? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, baseminor, count));
return err;
}
当应用层打开一个设备文件时,内核根据文件的设备号找到对应的主设备号,然后在根据主设备号和此设备号找到 misc类 设备 的open函数;
static int misc_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
struct miscdevice *c;
int err = -ENODEV;
const struct file_operations *new_fops = NULL;
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list) {//根据此设备号遍历 misc_list 设备链表,从中找到对应的设备,并从中获取到open 函数;
if (c->minor == minor) {
new_fops = fops_get(c->fops);
break;
}
}
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);//将找到的新的fops 操作函数集 复制给当前打开文件的fops;内核会为每个打开的文件建立一个 struct file 结构
if (file->f_op->open)
err = file->f_op->open(inode, file);// 调用设备对应的open 函数对真正的设备进行处理;
fail:
mutex_unlock(&misc_mtx);
return err;
}
当应用打开一个文件后,会根据文件的inode 找到文件的主设备号,在主设备号中查找misc设备的链表,找到对应的此设备号相同的设备,那么misc设备链表中的设备怎么来的呢? 是在调用misc_register() 注册的时候将 对应的设备加入到misc 设备链表中;
int misc_register(struct miscdevice *misc)
{
dev_t dev;
int err = 0;
bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
if (is_dynamic) {//动态注册 misc 设备,查找位图 这里存在一个次设备号的位图,一共64位。下边是遍历每一位,
如果这位为0,表示没有被占有,可以使用,为1表示被占用。
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
if (i >= DYNAMIC_MINORS) {
err = -EBUSY;
goto out;
}
misc->minor = DYNAMIC_MINORS - i - 1;
set_bit(i, misc_minors);
} else {
struct miscdevice *c;
list_for_each_entry(c, &misc_list, list) {//遍历设备是否已经注册,如果已经注册则返回;
if (c->minor == misc->minor) {
err = -EBUSY;
goto out;
}
}
}
dev = MKDEV(MISC_MAJOR, misc->minor);
misc->this_device =
device_create_with_groups(misc_class, misc->parent, dev,
misc, misc->groups, "%s", misc->name);//在/dev下创建设备文件,和device_create的作用类似;
if (is_dynamic) {
int i = DYNAMIC_MINORS - misc->minor - 1;
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors);
misc->minor = MISC_DYNAMIC_MINOR;
}
err = PTR_ERR(misc->this_device);
goto out;
}
/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
list_add(&misc->list, &misc_list);//将misc 设备加入到misc 链表中;
out:
mutex_unlock(&misc_mtx);
return err;
}