Linux中miscdevice的分析

当字符设备不符合预先确定的字符设备范畴时,就可以采用Linux的杂项设备,这类设备用得比较多,misc device用主设备号为10来调用misc_register注册。misc device的实现在drivers/char/misc.c文件中,先看它的init函数。
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;
	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;
}
subsys_initcall(misc_init);
通过subsys_initcall宏在内核初始化的时候调用misc_init函数,首先通过class_create申请misc_class类,再调用register_chrdev来注册字符设备,其中MISC_MAJOR的值是10。
再来看misc device的核心部分misc_register函数。
/**
 *	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) {
			mutex_unlock(&misc_mtx);
			return -EBUSY;
		}
		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) {
				mutex_unlock(&misc_mtx);
				return -EBUSY;
			}
		}
	}

	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);
 out:
	mutex_unlock(&misc_mtx);
	return err;
}
首先根据传入的miscdevice指针初始化链表头,接下来判断次设备号是否要求动态分配,如果是动态分配,就去misc_minors数组找到第一个不为0的bit,将其赋值给misc->minor,紧接着设置misc_minors数组的第i位。如果次设备号是固定分配,就调用list_for_each_entry查看是否有其他的misc已经注册了该次设备号,若已经分配,则返回错误。最后调用device_create函数根据misc_init函数申请的misc_class来在/dev目录下申请节点,并将驱动的miscdevice加入链表头。
misc_deregister函数定义如下:
/**
 *	misc_deregister - unregister a miscellaneous device
 *	@misc: device to unregister
 *
 *	Unregister a miscellaneous device that was previously
 *	successfully registered with misc_register(). Success
 *	is indicated by a zero return, a negative errno code
 *	indicates an error.
 */

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;
}
open操作时,misc_open会在驱动的open方法之前被调用,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) {
		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) {
		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;
}
这个函数最为关键的一点就是file->private_data = c;这条语句将struct file的private_data指针指向驱动程序中的注册的misc device,这样一来,就可以将struct miscdevice结构放到自己定义的结构中去,在open方法中通过container_of宏即可获得自定义结构体的地址。
在misc_register函数中,调用了find_first_zero_bit,这个函数是在misc_minors数组中找到第一个不为0的bit,misc_minors在一开始通过DECLARE_BITMAP宏进行定义。
static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);
DECLARE_BITMAP宏定义在include/linux/types.h:
#define DECLARE_BITMAP(name,bits) \
             unsigned long name[BITS_TO_LONGS(bits)]
BITS_TO_LONGS宏计算一共需要多少个unsigned long型的整数才能存储bits个位。
下面通过一个实例来说明misc device的使用。
驱动的私有数据结构定义:
struct pn547_dev {
	bool suspended;
	bool irq_enabled;
	wait_queue_head_t read_wq;
	spinlock_t irq_enabled_lock;
	struct mutex pn547_mutex;
	struct i2c_client *client;
	struct miscdevice pn547_device;
	struct regulator *vdd;
	struct regulator *vsim1;
	struct regulator *vsim2;
	struct workqueue_struct *pn547_wq;
	struct notifier_block fb_notif;
};
定义file_operations结构体:
static const struct file_operations pn547_dev_fops = {
	.owner	        = THIS_MODULE,
	.llseek	        = no_llseek,
	.read	        = pn547_dev_read,
	.write	        = pn547_dev_write,
	.open	        = pn547_dev_open,
	.unlocked_ioctl	= pn547_dev_ioctl,
	.release	= pn547_dev_release,
};
在驱动的probe函数中注册:
pdata = kzalloc(sizeof(struct pn547_dev), GFP_KERNEL);
if (!pdata) {
    dev_err(&client->dev, "%s: Alloc mem fail!", __func__);
    return -ENOMEM;
}

pdata->pn547_device.minor = MISC_DYNAMIC_MINOR;
pdata->pn547_device.name = "pn544";
pdata->pn547_device.fops = &pn547_dev_fops;

ret = misc_register(&pdata->pn547_device);
if (ret) {
	dev_err(&client->dev, "%s : misc_register failed\n", __FILE__);
	misc_deregister(&pdata->pn547_device);
	return -EINVAL;
}

如果注册成功,会在/dev/目录下生成pn544节点,之后就可以调用驱动的open,read,write函数了。重点关注open函数:

static int pn547_dev_open(struct inode *inode, struct file *filp)
{
	struct pn547_dev *pn547_dev = container_of(filp->private_data,
						struct pn547_dev,
						pn547_device);

	filp->private_data = pn547_dev;

	pr_debug("%s : %d,%d\n", __func__, imajor(inode), iminor(inode));
	irq_set_irq_wake(pn547_dev->client->irq, 1);
	return 0;
}
通过container_of宏,根据pn547_dev结构体的pn547_device成员的地址获得整个pn547_dev结构体的地址。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值