linux的input子系统(四)

话说马上要吃饭了,不过能写多少就写多少吧,写不完,下午接着写嘛,哈哈,你不会也饿了吧?不开玩笑了。上一篇讨论简单的按键应用层测试程序,还记得咱们详细看event0的熟悉的结果吧,如果忘了,可以再看看,通过查看,我们知道event0是一个字符设备,并且主设备号为13、次设备号为64。那问题来了,这个字符设备是在什么地方注册的,因为我前面还特意强调,在设备驱动程序里没有做相关的工作,其实是在linux-3.0.8/drivers/input/input.c文件中注册的,下面把代码贴出来,做进一步的分析:

static int __init input_init(void)
{
	int err;

	err = class_register(&input_class); //注册input设备类
	if (err) {
		pr_err("unable to register input_dev class\n");
		return err;
	}

	err = input_proc_init();//初始化input在proc的节点
	if (err)
		goto fail1;

	err = register_chrdev(INPUT_MAJOR, "input", &input_fops);//这里注意一下,INPUT_MAJOR等于13,也就是说注册了一个主设备号为13,名字为“input”的字符设备
	if (err) {
		pr_err("unable to register char major %d", INPUT_MAJOR);
		goto fail2;
	}

	return 0;

 fail2:	input_proc_exit();
 fail1:	class_unregister(&input_class);
	return err;
}
从上面的代码来看,咱们没有发现event*的字样,但主设备号的确是13,而且从打开文件的路径“/dev/input/event*”来看,input包含了event*,没错,input表示的是一类设备,而event*只是里面具体对于的设备。既然input是包含event*的一类设备,那怎么就沿着这条线继续看吧,这样看是对的,不管操作什么设备,首先应该打开嘛,而打开具体设备时,总是要通过包含它的设备类的。好吧,那咱们看“input_fops”这个文件操作结构,看看里面到底做了什么,下面是源码。

static const struct file_operations input_fops = {   //这个结构体就是实际操作input设备的结构体,不过发现里面仅仅包含open和llseek函数了,因为操作设备首先要打开设备,那open函数就是关键了,下面源码中好好研究研究吧
	.owner = THIS_MODULE,
	.open = input_open_file,
	.llseek = noop_llseek,
};

static int input_open_file(struct inode *inode, struct file *file)
{
	struct input_handler *handler;   //这是事件操作的接口
	const struct file_operations *old_fops, *new_fops = NULL;
	int err;

	err = mutex_lock_interruptible(&input_mutex);
	if (err)
		return err;

	/* No load-on-demand here? */
	handler = input_table[iminor(inode) >> 5]; //这里首先通过此设备号寻找handler,handler就是事件操作接口嘛,这个会在下面详细解释
	if (handler)
		new_fops = fops_get(handler->fops);  //得到handler里面包含的文件接口

	mutex_unlock(&input_mutex);

	/*
	 * That's _really_ odd. Usually NULL ->open means "nothing special",
	 * not "no device". Oh, well...
	 */
	if (!new_fops || !new_fops->open) { //如果得到的文件操作接口是空的,那么就返回了
		fops_put(new_fops);
		err = -ENODEV;
		goto out;
	}

	old_fops = file->f_op;
	file->f_op = new_fops;  //看到这里吧,这是重点,把新得到的文件操作结构体赋给file的f_op字段

	err = new_fops->open(inode, file);//打开新的文件操作接口,在这里才是真真的文件打开
	if (err) {
		fops_put(file->f_op);
		file->f_op = fops_get(old_fops);
	}
	fops_put(old_fops);  //减少旧的文件操作接口计数
out:
	return err;
}
先简单的梳理一下open这个函数的作用吧。a.open函数通过所要打开的具体文件的次设备号,得到input的事件操作接口handler;b.通过handler得到具体设备文件的fops;c.然后把通过把file的fops字段换成用次设备得到的新的fops;d.把原来的fops字段的计数减一。现在解释一下iminor(inode)>>5,也就是把次设备号向右移5位,因为在事件处理层中是一类设备对应的处理只有一种,也就是说不是每一个设备对应一个实际存在的处理文件,而是一类对应一个,这也就是抽象的好处,要不然那得写多少个具体事件处理文件啊。handler  *input_table[8],这是input_table的定义,它是一个全局的指针数组,你看它只定义了8个,它代表8个类了,比如evdev的次设备号是从64到95的;mousedev的次设备号是从32到63的,这里evdev和mousedev是实际处理一类input设备的事件输入的驱动,向右移位5正好把他们能区分开来,比如说从32到63中的任何数向右移5位,得到的都是1,而从64到95,就是2了。

现在咱们得做一个假设,假设咱们要打开的设备是“/dev/input/event0”,而且event0对应的次设备号就是64,因为咱们得根据具体的一个做进一步的深入,这里不可能把所有的都讲了。次设备号是64,向右移5位得到2,也就是说input_table[2]对应的事件操作接口就是咱们要找的,这一部分你可以参考linux-3.0.8/Documentation/input/input.txt文件知道它对应的是evdev.c注册的一类设备中的一个。那咱们就打开在linux-3.0.8/drivers/input/evdev.c这个文件,在这个文件中的函数evdev_connect中看到如下的一行函数:

dev_set_name(&evdev->dev, "event%d", minor);
这行函数用次设备号生产设备的名字,这也的确证明了次设备号为64的设备在对应的事件处理层驱动是evdev.c,因为咱们的设备名字是“event0”嘛。在前面的open函数中已经把对应handler的fops赋给了file的f_op字段,而且打开了handler的fops字段中的open函数。再看看evdev.c中的handler对应的fops字段吧,如下:

static const struct file_operations evdev_fops = {
	.owner		= THIS_MODULE,
	.read		= evdev_read,
	.write		= evdev_write,
	.poll		= evdev_poll,
	.open		= evdev_open,
	.release	= evdev_release,
	.unlocked_ioctl	= evdev_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= evdev_ioctl_compat,
#endif
	.fasync		= evdev_fasync,
	.flush		= evdev_flush,
	.llseek		= no_llseek,
};
看到了吧,这里涵盖处理evdev类的字符设备的函数,其实通过上面的open打开的真真的open函数就是这里的evdev_open 函数了,具体我就不贴出来了。再做一个简单的总结吧,当咱们用“/dev/input/event0”打开一个设备文件时,首先调用input_open_file()这个函数,然后在这个函数中,通过次设备号得到真真的文件操作接口,最后打开得到的文件操作接口的open函数,同时以后的什么read啊、write啊都会调用evdev.c中定义read和write方法了。通过这些方法就可以操作具体的设备文件了。

通过在应用层往下分析,咱们了解到具体设备是怎么被打开的,但是同时又有个问题,我们知道在写设备驱动函数时(比如前面的按键驱动),我们并没有定义什么次设备号啊,或者定义需要让evdev处理,它们是怎么样做匹配的?这可能需要从驱动层往上分析可能更清晰一点,好吧,咱们下一篇就继续讨论吧,下篇见喽!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值