input子系统事件处理层evdev分析

  • Post author:
  • Post category:其他


在input子系统中事件处理层有很多evdev,joydev,evbug,mousedev。但是他们的实现基本上是一样的,对于触摸屏来说,事件处理器是evdev,现在就来分析一下evdev的代码。
首先还是evdev的入口和出口
static int __init evdev_init(void)
{
	/*将evdev_handler注册到系统中*/
	return input_register_handler(&evdev_handler);
}
static void __exit evdev_exit(void)
{
	input_unregister_handler(&evdev_handler);
}
module_init(evdev_init);
module_exit(evdev_exit);
由于evdev是事件处理器,给用户空间提供接口,所以必须对它进行注册。
static struct input_handler evdev_handler = {
	.event		= evdev_event,//当有事件来的时候调用,把事件放入input_event数组,为用户空间读写事件做准备
	.connect	= evdev_connect,//在input_dev和input_handler注册过程最终都会调用这个函数,完成handle的注册
	.disconnect	= evdev_disconnect,//在input_dev和input_handler注销过程最终会调用这个函数,完成handle的注销
	.fops		= &evdev_fops,//对事件的操作函数集
	.minor		= EVDEV_MINOR_BASE,//次设备号基数
	.name		= "evdev",
	.id_table	= evdev_ids,
};
这里有一个id_table成员,这个成员在注册input_dev和input_handler的里面,都需要通过 evdev_ids来筛选出同种类型的ID和比较支持的事件,然后建立device和handler关联。
static const struct input_device_id evdev_ids[] = {
	{ .driver_info = 1 },	/* Matches all devices *///适合所有的类型的设备
	{ },			/* Terminating zero entry */
};
对于event和connect函数在我的事件上报过程和input重要结构体分析的日志中有讲,这里不再分析。
现在主要来看提供给用户空间的操作函数集,这有点像标准的字符设备驱动
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,
};
先来看两个结构体
/*在进程打开event0设备的时候调用evdev的open方法,在open中创建和初始化*/
struct evdev_client {
	unsigned int head;//针对buffer数组的索引
	unsigned int tail;//针对buffer数组的索引,当head和tail相等的时候,说明没有事件
	unsigned int packet_head; /* [future] position of the first element of next packet */
	spinlock_t buffer_lock; /* protects access to buffer, head and tail */
	struct fasync_struct *fasync;//异步通知函数
	struct evdev *evdev;//evdev设备
	struct list_head node;//evdev_client链表项
	unsigned int bufsize;
	struct input_event buffer[];//一个input_event数据结构的数组,input_event代表一个事件,基本成员:类型(type)编码(code)值(value)
};
/*evdev结构体再配对成功的时候生成,由handler->connect生成*/
struct evdev {
	int open;//打开引用计数
	int minor;//次设备号
	struct input_handle handle;//关联的input_handle
	wait_queue_head_t wait;//等待队列
	struct evdev_client __rcu *grab;
	struct list_head client_list;//evdev_client链表,说明一个evdev设备可以处理多个evdev_client,可以有多个进程访问
	spinlock_t client_lock; /* protects client_list */
	struct mutex mutex;
	struct device dev;
	bool exist;
};
同字符设备一样先来看open函数
static int evdev_open(struct inode *inode, struct file *file)
{
	struct evdev *evdev;
	struct evdev_client *client;
	int i = iminor(inode) - EVDEV_MINOR_BASE;
	unsigned int bufsize;
	int error;
	if (i >= EVDEV_MINORS)//判断是否超出了能处理的最大设备数
		return -ENODEV;
	error = mutex_lock_interruptible(&evdev_table_mutex);
	if (error)
		return error;
	evdev = evdev_table[i];//得到evdev设备结构,每次调用evdev_connect配对成功后都会把分配的evdev结构以minor为索引保存在evdev_table中
	if (evdev)
		get_device(&evdev->dev);//增加device引用计数
	mutex_unlock(&evdev_table_mutex);
	if (!evdev)
		return -ENODEV;
	bufsize = evdev_compute_buffer_size(evdev->handle.dev);
	client = kzalloc(sizeof(struct evdev_client) +
				bufsize * sizeof(struct input_event),
			 GFP_KERNEL);//分配用户端结构
	if (!client) {
		error = -ENOMEM;
		goto err_put_evdev;
	}
	client->bufsize = bufsize;
	spin_lock_init(&client->buffer_lock);
	client->evdev = evdev;//使用户端与evdev设备结构关联起来
	evdev_attach_client(evdev, client);//把client链接到evdev的client链表上
	error = evdev_open_device(evdev);//打开设备
	if (error)
		goto err_free_client;
	file->private_data = client;
	nonseekable_open(inode, file);
	return 0;
 err_free_client:
	evdev_detach_client(evdev, client);
	kfree(client);
 err_put_evdev:
	put_device(&evdev->dev);
	return error;
}
evdev_attach_client就是把client关联到evdev->client_list的链表上
static void evdev_attach_client(struct evdev *evdev,
				struct evdev_client *client)
{
	spin_lock(&evdev->client_lock);
	list_add_tail_rcu(&client->node, &evdev->client_list);
	spin_unlock(&evdev->client_lock);
}
evdev_open_device主要通过evdev的exist标志判断是否已经获得了evdev结构体
static int evdev_open_device(struct evdev *evdev)
{
	int retval;
	retval = mutex_lock_interruptible(&evdev->mutex);
	if (retval)
		return retval;
	if (!evdev->exist)//判断evdev结构体是否存在,在evdev_connect中初始化成员为1
		retval = -ENODEV;
	else if (!evdev->open++) {
		retval = input_open_device(&evdev->handle);
		if (retval)
			evdev->open--;
	}
	mutex_unlock(&evdev->mutex);
	return retval;
}
真正的打开是在input.c中 input_open_device这个函数完成的。
int input_open_device(struct input_handle *handle)
{
	struct input_dev *dev = handle->dev;
	int retval;
	retval = mutex_lock_interruptible(&dev->mutex);
	if (retval)
		return retval;
	if (dev->going_away) {//判断设备是否在open期间被注销
		retval = -ENODEV;
		goto out;
	}
	handle->open++;//handle的打开计数加一
	if (!dev->users++ && dev->open)//如果输入设备没有进程引用,并定义了open方法,就调用open方法
		retval = dev->open(dev);
	if (retval) {//retval=1,说明没有打开成功
		dev->users--;
		if (!--handle->open) {//说明有其它进程打开了这个handle
			/*
			 * Make sure we are not delivering any more events
			 * through this handle
			 */
			synchronize_rcu();
		}
	}
 out:
	mutex_unlock(&dev->mutex);
	return retval;
}
到这里,如果input_dev有定义open方法,打开函数最终会调用到input_dev下面的open函数,否者只是简单的增加打开引用计数。
这里read函数也在事件上报过程的那片日志有分析到,我们来看write函数。
static ssize_t evdev_write(struct file *file, const 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 = 0;
	if (count < input_event_size())//write操作的数据大小至少要大于一个事件的大小
		return -EINVAL;
	retval = mutex_lock_interruptible(&evdev->mutex);//上锁
	if (retval)
		return retval;
	if (!evdev->exist) {
		retval = -ENODEV;
		goto out;
	}
	do {
		if (input_event_from_user(buffer + retval, &event)) {//得到用户空间的数据
			retval = -EFAULT;
			goto out;
		}
		retval += input_event_size();//累加事件长度,计算得到用户空间的数据长度
		input_inject_event(&evdev->handle,
				   event.type, event.code, event.value);//从input handler发送一个input事件
	} while (retval + input_event_size() <= count);
 out:
	mutex_unlock(&evdev->mutex);
	return retval;
}
看看发送事件的实现
void input_inject_event(struct input_handle *handle,
			unsigned int type, unsigned int code, int value)
{
	struct input_dev *dev = handle->dev;
	struct input_handle *grab;
	unsigned long flags;
	if (is_event_supported(type, dev->evbit, EV_MAX)) {
		spin_lock_irqsave(&dev->event_lock, flags);
		rcu_read_lock();
		grab = rcu_dereference(dev->grab);
		if (!grab || grab == handle)
			input_handle_event(dev, type, code, value);
		rcu_read_unlock();
		spin_unlock_irqrestore(&dev->event_lock, flags);
	}
}
EXPORT_SYMBOL(input_inject_event);
input_inject_event->input_handle_event->input_pass_event->handler下的event
最终还是由evdev中的event完成的发送.所以这个事件就不是有具体设备驱动发出的,而是我们从用户空间直接写一个事件到evdev中,然后通过input core最终调用event发送,一般提供给测试用的接口。
接下来看poll的实现,允许进程决定是否可以完成非阻塞的操作
static unsigned int evdev_poll(struct file *file, poll_table *wait)
{
	struct evdev_client *client = file->private_data;
	struct evdev *evdev = client->evdev;
	unsigned int mask;
	poll_wait(file, &evdev->wait, wait);//添加等待队列到evdev->wait
	mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;//设置位掩码
	if (client->packet_head != client->tail)
		mask |= POLLIN | POLLRDNORM;
	return mask;
}
ioctl的实现
static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
}
static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
				void __user *p, int compat_mode)
{
	struct evdev_client *client = file->private_data;
	struct evdev *evdev = client->evdev;
	int retval;
	retval = mutex_lock_interruptible(&evdev->mutex);
	if (retval)
		return retval;
	if (!evdev->exist) {
		retval = -ENODEV;
		goto out;
	}
	retval = evdev_do_ioctl(file, cmd, p, compat_mode);
 out:
	mutex_unlock(&evdev->mutex);
	return retval;
}
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
			   void __user *p, int compat_mode)
{
	struct evdev_client *client = file->private_data;
	struct evdev *evdev = client->evdev;
	struct input_dev *dev = evdev->handle.dev;
	struct input_absinfo abs;
	struct ff_effect effect;
	int __user *ip = (int __user *)p;
	unsigned int i, t, u, v;
	unsigned int size;
	int error;
	/* First we check for fixed-length commands */
	switch (cmd) {
	case EVIOCGVERSION:
		return put_user(EV_VERSION, ip);
	case EVIOCGID:
		if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
			return -EFAULT;
		return 0;
	case EVIOCGREP:
		if (!test_bit(EV_REP, dev->evbit))
			return -ENOSYS;
		if (put_user(dev->rep[REP_DELAY], ip))
			return -EFAULT;
		if (put_user(dev->rep[REP_PERIOD], ip + 1))
			return -EFAULT;
		return 0;
	case EVIOCSREP:
		if (!test_bit(EV_REP, dev->evbit))
			return -ENOSYS;
		if (get_user(u, ip))
			return -EFAULT;
		if (get_user(v, ip + 1))
			return -EFAULT;
		input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
		input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
		return 0;
	case EVIOCRMFF:
		return input_ff_erase(dev, (int)(unsigned long) p, file);
	case EVIOCGEFFECTS:
		i = test_bit(EV_FF, dev->evbit) ?
				dev->ff->max_effects : 0;
		if (put_user(i, ip))
			return -EFAULT;
		return 0;
	case EVIOCGRAB:
		if (p)
			return evdev_grab(evdev, client);
		else
			return evdev_ungrab(evdev, client);
	case EVIOCGKEYCODE:
		return evdev_handle_get_keycode(dev, p);
	case EVIOCSKEYCODE:
		return evdev_handle_set_keycode(dev, p);
	case EVIOCGKEYCODE_V2:
		return evdev_handle_get_keycode_v2(dev, p);
	case EVIOCSKEYCODE_V2:
		return evdev_handle_set_keycode_v2(dev, p);
	}
	size = _IOC_SIZE(cmd);
	/* Now check variable-length commands */
#define EVIOC_MASK_SIZE(nr)	((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
	switch (EVIOC_MASK_SIZE(cmd)) {
	case EVIOCGPROP(0):
		return bits_to_user(dev->propbit, INPUT_PROP_MAX,
				    size, p, compat_mode);
	case EVIOCGKEY(0):
		return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
	case EVIOCGLED(0):
		return bits_to_user(dev->led, LED_MAX, size, p, compat_mode);
	case EVIOCGSND(0):
		return bits_to_user(dev->snd, SND_MAX, size, p, compat_mode);
	case EVIOCGSW(0):
		return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode);
	case EVIOCGNAME(0):
		return str_to_user(dev->name, size, p);
	case EVIOCGPHYS(0):
		return str_to_user(dev->phys, size, p);
	case EVIOCGUNIQ(0):
		return str_to_user(dev->uniq, size, p);
	case EVIOC_MASK_SIZE(EVIOCSFF):
		if (input_ff_effect_from_user(p, size, &effect))
			return -EFAULT;
		error = input_ff_upload(dev, &effect, file);
		if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
			return -EFAULT;
		return error;
	}
	/* Multi-number variable-length handlers */
	if (_IOC_TYPE(cmd) != 'E')
		return -EINVAL;
	if (_IOC_DIR(cmd) == _IOC_READ) {
		if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
			return handle_eviocgbit(dev,
						_IOC_NR(cmd) & EV_MAX, size,
						p, compat_mode);
		if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
			if (!dev->absinfo)
				return -EINVAL;
			t = _IOC_NR(cmd) & ABS_MAX;
			abs = dev->absinfo[t];
			if (copy_to_user(p, &abs, min_t(size_t,
					size, sizeof(struct input_absinfo))))
				return -EFAULT;
			return 0;
		}
	}
	if (_IOC_DIR(cmd) == _IOC_WRITE) {
		if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
			if (!dev->absinfo)
				return -EINVAL;
			t = _IOC_NR(cmd) & ABS_MAX;
			if (copy_from_user(&abs, p, min_t(size_t,
					size, sizeof(struct input_absinfo))))
				return -EFAULT;
			if (size < sizeof(struct input_absinfo))
				abs.resolution = 0;
			/* We can't change number of reserved MT slots */
			if (t == ABS_MT_SLOT)
				return -EINVAL;
			/*
			 * Take event lock to ensure that we are not
			 * changing device parameters in the middle
			 * of event.
			 */
			spin_lock_irq(&dev->event_lock);
			dev->absinfo[t] = abs;
			spin_unlock_irq(&dev->event_lock);
			return 0;
		}
	}
	return -EINVAL;
}
ioctl主要通过cmd的不同来完成不同的操作,这里不再详细分析.



版权声明:本文为zhengsheng23原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。