input 子系统的分析与学习(一)


依然不明白struct device 中的parent是什么意思?

最近在写一个光感传感器设备的驱动。不知为何上层的接口是通过input设备实现的。

其实我们自己也可以讲某一个输入设备 如传感器等写为一个字符设备,但是那样的话,我们需要自己实现open\read\write等方法,需要自己实现阻塞。但是通过实现成一个input设备,我们可以把注意力集中到input设备要汇报的事件上面,其他的工作系统已经帮我们完成了。

经过研究,input设备提供给我们的便利 就是它剥离了设备、设备产生的事件、事件的处理 这三者之间的逻辑。

三者的关系本来如下: 设备 ------- >事件----------> 事件的处理-------->上层应用。 其中设备驱动程序负责产生事件,产生的事件由 事件的handler处理,通常handler处理后,再向用户态发送消息,也就说与用户态的交互都有handler完成。设备驱动程序只负责检测并产生事件,产生事件后调用input_event 函数汇报事件。 然后,对于每一个input_dev,都有相应的handler负责处理。 input_dev与handler之间是独立的,也就是说一个input_dev 提交的 事件,会由这个设备上注册的每个handler都进行处理,而一个handler可以注册给多个设备。

 

input_dev和handler之间的挂钩是通过input_attach_handler函数来实现的,在这个函数中有很严格的匹配条件。内核中实现了evdev_handler和evbug_handler,这两个handler因为限制条件很宽松,所以可以挂接到所有的input_dev。 其中,evbug_handler用于打印消息,相当于是一个调试器,每当input_dev提交事件的时候,事件提交给evbug_handler处理时,evbug_handler都会把事件相关的消息打印出来。

evdev_handler是用于与用户态交互的,evdev_handler中实现了一系列函数结构体,如下:

 

static struct input_handler evdev_handler = {
	.event		= evdev_event,
	.connect	= evdev_connect,
	.disconnect	= evdev_disconnect,
	.fops		= &evdev_fops,
	.minor		= EVDEV_MINOR_BASE,
	.name		= "evdev",
	.id_table	= evdev_ids,
};


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
};

 


 在evdev_handler中实现了connect、disconne函数等,分别实现handler与input_dev的挂接。其中,evdev_fops实现了完整的文件打开、读写等操作。值得重点一提。

 

首先,在input_init函数中, 有:  err = register_chrdev(INPUT_MAJOR, "input", &input_fops); 其中,input_fops中有:

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;

	lock_kernel();
	/* No load-on-demand here? */
	handler = input_table[iminor(inode) >> 5];
	if (!handler || !(new_fops = fops_get(handler->fops))) {
		err = -ENODEV;
		goto out;
	}

	/*
	 * That's _really_ odd. Usually NULL ->open means "nothing special",
	 * not "no device". Oh, well...
	 */
	if (!new_fops->open) {
		fops_put(new_fops);
		err = -ENODEV;
		goto out;
	}
	old_fops = file->f_op;
	file->f_op = new_fops;

	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:
	unlock_kernel();
	return err;
}

 

也就是说,当我们通过open  /dev/input/ 这个目录下的设备时,都会调用input_open_file这个函数。因为通过register_chrdev这种方法注册的函数体 对于所有的主设备号是INPUT_MAJOR的设备都是通用的然后,在这个input_open_file中,通过这个 handler = input_table[iminor(inode) >> 5]; 可以得到evdev_handler。这里之所以得到的handler是evdev_handler 而不是其他的handler,是因为其他的handler都没有实现fops函数,内核在handler_register函数中,可以实现过滤。保证这里得到的就是evdev_handler。 因此,在这个input_open_file中,将file->f_ops进行了修改,把file->f_ops 指向了 evdev_handler->ev_fops。这样在 用户态以后调用 read\write等方法时,就会调用到evdev_handler中的方法中。

 

这里有些像是手动实现了 C++中的虚函数和多态。内核中有许多面向对象的思想,还需要多多领悟。

 

下面是evdev_read函数:

 

static ssize_t evdev_read(struct file *file, char __user *buffer,
			  size_t count, loff_t *ppos)
{
	struct evdev_client *client = file->private_data;
	struct evdev *evdev = client->evdev;
	struct input_event event;
	int retval;

	if (count < input_event_size())
		return -EINVAL;

	if (client->head == client->tail && evdev->exist &&
	    (file->f_flags & O_NONBLOCK))
		return -EAGAIN;

	retval = wait_event_interruptible(evdev->wait,
		client->head != client->tail || !evdev->exist);
	if (retval)
		return retval;

	if (!evdev->exist)
		return -ENODEV;

	while (retval + input_event_size() <= count &&
	       evdev_fetch_next_event(client, &event)) {

		if (input_event_to_user(buffer + retval, &event))
			return -EFAULT;

		retval += input_event_size();
	}

	return retval;
}

 

在这个函数中实现了read时的阻塞。

 


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值