12.3.6 misc设备驱动
Linux驱动倾向于分层设计,各个具体的设备都可以找到它归属的类型,从而套到它相应的架构里面去,并且只需要实现最底层的那一部分。但是,也有部分类似globalmem、globalfifo的字符设备,确实不知道它属于什么类型,一般推荐大家采用miscdevice框架结构。miscdevice本质上也是字符设备,在miscdevice核心层(drivers/char/misc.c)的misc_init()函数中,通过register_chrdev(MISC_MAJOR,"misc",&misc_fops)注册字符设备,具体miscdevice实例调用(drivers/char/misc.c)misc_register()函数又自动完成device_create()、获取动态次设备号的动作。
miscdevice的主设备号是固定的,MISC_MAJOR定义为10,Linux内核中,大概找到200多处使用miscdevice框架结构的驱动。
miscdevice结构体的定义如代码清单12.21所示。
linux/miscdevice.h
struct miscdevice {
/*minor为MISC_DYNAMIC_MINOR,miscdevice核心层会自动找一个空闲的次设
备号,否则用minor指定的次设备号。*/
const char *name;/*设备的名称*/
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
umode_t mode;
};
备注:miscdevice结构体内file_operations中的成员函数,实际上是由drivers/char/misc.c中misc驱动核心层的misc_fops成员函数间接调用的,比如misc_open()就会间接调用底层注册的miscdevice的fops->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) {
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;
replace_fops(file, new_fops);
if (file->f_op->open) {
file->private_data = c;
err = file->f_op->open(inode,file);
}
fail:
mutex_unlock(&misc_mtx);
return err;
}
miscdevice驱动的注册和注销分别用下面两个API:
linux/miscdevice.h
extern int misc_register(struct miscdevice * misc);
extern int misc_deregister(struct miscdevice *misc);
drivers/char/misc.c
/**
* misc_register - register a miscellaneous device
* @misc: device structure
*
* Register a miscellaneous device with the kernel. If the minor
* number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
* and placed in the minor field of the structure. For other cases
* the minor number requested is used.
*
* The structure passed is linked into the kernel and may not be
* destroyed until it has been unregistered.
*
* A zero is returned on success and a negative errno code for
* failure.
*/
int misc_register(struct miscdevice * misc)
{
dev_t dev;
int err = 0;
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
if (misc->minor == MISC_DYNAMIC_MINOR) {
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(misc_class, misc->parent, dev,
misc, "%s", misc->name); /* 创建设备 */
if (IS_ERR(misc->this_device)) {
int i =