在系
统中,存在一类字符设备,他们共享一个主设备号(10),但此设备号不同,我们称这类设备为混杂设备(miscdeivce),查看/proc
/device中可以看到一个名为misc的主设备号为10.所有的混杂设备形成一个链表,对设备访问时内核根据次设备号找到对应的miscdevice
设备。相对于普通字符设备驱动,它不需要自己去生成设备文件。
Linux内核使用struct miscdeivce来描述一个混杂设备
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
scull 设备驱动只实现最重要的设备方法. 它的 file_operations 结构是如下初始化的:
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
minor是这个混杂设备的次设备号,若由系统自动配置,则可以设置为MISC_DYNANIC_MINOR,name是设备名.使用时只需填写minor次设备号,*name设备名,*fops文件操作函数集即可。
Linux内核使用misc_register函数注册一个混杂设备,使用misc_deregister移除一个混杂设备。注册成功后,linux内核为自动为该设备创建设备节点,在/dev/下会产生相应的节点。
注册函数:
int misc_register(struct miscdevice * misc)
输入参数:struct miscdevice
返回值:
0 表示注册成功。
负数 表示未成功。
卸载函数:
int misc_deregister(struct miscdevice *misc)
0 表示成功。
负数 表示失败。
在Linux驱动中把无法归类的五花八门的设备定义为混杂设备(用miscdevice结构体表述)。miscdevice共享一个主设备号
MISC_MAJOR(即10),但次设备号不同。
所有的miscdevice设备形成了一个链表,对设备访问时内核根据次设备号查找对应的miscdevice设备,然后调用其
file_operations结构中注册的文件操作接口进行操作。 在内核中用struct
miscdevice表示miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。miscdevice
的API实现在drivers/char/misc.c中。
下边是描述这个设备的结构体:
-
struct miscdevice {
-
int minor;
-
const char *name;
-
const struct file_operations *fops;
-
struct list_head list;
-
struct device *parent;
-
struct device *this_device;
-
};
然后来看看misc子系统的初始化函数:
-
static int __init misc_init(void)
-
{
-
int err;
-
-
#ifdef CONFIG_PROC_FS
-
-
proc_create("misc", 0, NULL, &misc_proc_fops);
-
#endif
-
-
misc_class = class_create(THIS_MODULE, "misc");
-
err = PTR_ERR(misc_class);
-
if (IS_ERR(misc_class))
-
goto fail_remove;
-
-
err = -EIO;
-
-
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
-
goto fail_printk;
-
return 0;
-
-
fail_printk:
-
printk("unable to get major %d for misc devices/n", MISC_MAJOR);
-
class_destroy(misc_class);
-
fail_remove:
-
remove_proc_entry("misc", NULL);
-
return err;
-
}
-
-
subsys_initcall(misc_init);
下边是register_chrdev函数的实现:
-
int register_chrdev(unsigned int major, const char *name,
-
const struct file_operations *fops)
-
{
-
struct char_device_struct *cd;
-
struct cdev *cdev;
-
char *s;
-
int err = -ENOMEM;
-
-
cd = __register_chrdev_region(major, 0, 256, name);
-
if (IS_ERR(cd))
-
return PTR_ERR(cd);
-
-
cdev = cdev_alloc();
-
if (!cdev)
-
goto out2;
-
-
cdev->owner = fops->owner;
-
cdev->ops = fops;
-
-
kobject_set_name(&cdev->kobj, "%s", name);
-
for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
-
*s = '!';
-
-
err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
-
if (err)
-
goto out;
-
-
cd->cdev = cdev;
-
-
return major ? 0 : cd->major;
-
out:
-
kobject_put(&cdev->kobj);
-
out2:
-
kfree(__unregister_chrdev_region(cd->major, 0, 256));
-
return err;
-
}
来看看这个设备的操作函数的集合:
-
static const struct file_operations misc_fops = {
-
.owner = THIS_MODULE,
-
.open = misc_open,
-
};
可以看到这里只有一个打开函数,用户打开miscdevice设备是通过主设备号对应的打开函数,在这个函数中找到次设备号对应的相应的具体设备的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 *old_fops, *new_fops = NULL;
-
-
lock_kernel();
-
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) {
-
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;
-
}
-
-
err = 0;
-
-
old_fops = file->f_op;
-
-
file->f_op = new_fops;
-
if (file->f_op->open) {
-
-
err=file->f_op->open(inode,file);
-
if (err) {
-
fops_put(file->f_op);
-
file->f_op = fops_get(old_fops);
-
}
-
}
-
fops_put(old_fops);
-
fail:
-
mutex_unlock(&misc_mtx);
-
unlock_kernel();
-
return err;
-
}
再来看看misc子系统对外提供的两个重要的API,misc_register,misc_deregister:
-
int misc_register(struct miscdevice * misc)
-
{
-
struct miscdevice *c;
-
dev_t dev;
-
int err = 0;
-
-
INIT_LIST_HEAD(&misc->list);
-
mutex_lock(&misc_mtx);
-
-
list_for_each_entry(c, &misc_list, list) {
-
if (c->minor == misc->minor) {
-
mutex_unlock(&misc_mtx);
-
return -EBUSY;
-
}
-
}
-
-
if (misc->minor == MISC_DYNAMIC_MINOR) {
-
-
-
*static unsigned char misc_minors[DYNAMIC_MINORS / 8];
-
*这里存在一个次设备号的位图,一共64位。下边是遍历每一位,
-
*如果这位为0,表示没有被占有,可以使用,为1表示被占用。
-
*/
-
int i = DYNAMIC_MINORS;
-
while (--i >= 0)
-
if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
-
break;
-
if (i<0) {
-
mutex_unlock(&misc_mtx);
-
return -EBUSY;
-
}
-
-
misc->minor = i;
-
}
-
-
if (misc->minor < DYNAMIC_MINORS)
-
misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
-
-
dev = MKDEV(MISC_MAJOR, misc->minor);
-
-
misc->this_device = device_create(misc_class, misc->parent, dev, NULL,
-
"%s", misc->name);
-
if (IS_ERR(misc->this_device)) {
-
err = PTR_ERR(misc->this_device);
-
goto out;
-
}
-
-
-
-
-
-
-
list_add(&misc->list, &misc_list);
-
out:
-
mutex_unlock(&misc_mtx);
-
return err;
-
}
这个是miscdevice的卸载函数:
-
int misc_deregister(struct miscdevice *misc)
-
{
-
int i = misc->minor;
-
-
if (list_empty(&misc->list))
-
return -EINVAL;
-
-
mutex_lock(&misc_mtx);
-
-
list_del(&misc->list);
-
-
device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
-
if (i < DYNAMIC_MINORS && i>0) {
-
-
misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
-
}
-
mutex_unlock(&misc_mtx);
-
return 0;
-
}
总结一下miscdevice驱动的注册和卸载流程:
misc_register:
匹配次设备号->找到一个没有占用的次设备号(如果需要动态分配的话)->计算设号->创建设备文-
miscdevice结构体添加到misc_list链表中。
misc_deregister:
从mist_list中删除miscdevice->删除设备文件->位图位清零。
阅读(778) | 评论(0) | 转发(0) |