MISC device

杂项设备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);
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值