杂项设备MISC
杂项设备共用主设备号10,MISC就是最简单的字符设备驱动。
misc 驱动通常嵌套在platform 总线驱动中,实现复杂的驱动。
所有的MISC设备驱动的主设备号都是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;
};
定义一个misc 设备,我们需要设置minor
,name
,fops
这三个成员变量,minor是次设备号,有几个次设备号被系统占用了,定义在include/linux/miscdevice.h
#define PSMOUSE_MINOR 1
#define MS_BUSMOUSE_MINOR 2 /* unused */
#define ATIXL_BUSMOUSE_MINOR 3 /* unused */
/*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
#define ATARIMOUSE_MINOR 5 /* unused */
#define SUN_MOUSE_MINOR 6 /* unused */
......
#define MISC_DYNAMIC_MINOR 255
若指定次设备号为255 表示由内核自动分配次设备号。
设置好miscdevice 结构体后,就可以使用misc_register 函数向系统注册一个MISC设备。
替代普通字符驱动注册,如下步骤:
alloc_chrdev_region() //申请设备号
cdev_init() //初始化cdev
cdev_add() //添加 cdev
class_create() //创建 类
device_create() //创建 设备
使用杂项misc设备,只需要调用misc_register
函数就可以完成 alloc_chrdev_region,cdev_init,cdev_add,class_create,device_create
这些函数完成的功能。
卸载杂项字符设备时,需要调用misc_deregister 函数来注销misc 设备。
替代普通字符设备的卸载操作,如下:
cdev_del() //删除设备
unregister_chrdev_region() //注销设备号
device_destroy() //删除设备
class_destroy() //删除类
分析/drivers/char/msic.c
subsys_initcall(misc_init); //会在系统初始化是调用到
misc_int()
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"); //创建msic_class类
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class))
goto fail_remove;
err = -EIO;
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) //申请主设备号10的256个设备号,cdev_add
goto fail_printk;
misc_class->devnode = misc_devnode;
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;
}
misc_register()
misc_deregister()
注册misc设备
int misc_register(struct miscdevice * misc)
{
struct miscdevice *c;
dev_t dev;
int err = 0;
INIT_LIST_HEAD(&misc->list);//初始化list节点,用来添加到misc_list中的
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list) { //检查minor是否已分配
if (c->minor == misc->minor) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
}
if (misc->minor == MISC_DYNAMIC_MINOR) { //misc自动分配次设备号
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
if (i >= DYNAMIC_MINORS) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
misc->minor = DYNAMIC_MINORS - i - 1;
set_bit(i, misc_minors); //设置映射表
}
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 = DYNAMIC_MINORS - misc->minor - 1;
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors);
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_list头部,为了新的可以覆盖旧的设备
out:
mutex_unlock(&misc_mtx);
return err;
}
int misc_deregister(struct miscdevice *misc)
{
int i = DYNAMIC_MINORS - misc->minor - 1;
if (WARN_ON(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)
clear_bit(i, misc_minors);
mutex_unlock(&misc_mtx);
return 0;
}
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 *old_fops, *new_fops = NULL;
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list) { //遍历misc_list,找到miscdevice->fops
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; //替换file->f_ops
if (file->f_op->open) {
file->private_data = c;
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);
return err;
}
driver()
struct miscdevice miscLeds;
miscLeds.fops = &misc_leds_ops;
miscLeds.name = "misc3Leds";
miscLeds.minor = MISC_DYNAMIC_MINOR;
ret = misc_register(&miscLeds);