分析一个内核提供的input_handler

Linux 输入子系统分析(一)
Linux 输入子系统分析(二)
分析一个内核提供的input_handler

对应一些常用的输入设备,如键盘、鼠标等,内核提供的对应的input_handler,用来统一处理这类设备的事件。比如键盘,不管哪种型号的键盘,能上传的事件信息都是一样的,如上传键值’A’、'B’等。

本次分析drivers\input\evdev.c,其注册的input_handler支持所有的input_dev。

入口函数:

/* evdev_handler只设置了driver_info,evbit、keybit等未设置,都为0,从上一章分析
   input_attach_handler函数得知,该input_handler会与所有的input_dev匹配成功
 */
static const struct input_device_id evdev_ids[] = {
	{ .driver_info = 1 },	/* Matches all devices */
	{ },			/* Terminating zero entry */
};

static struct input_handler evdev_handler = {
	.event		= evdev_event,
	.events		= evdev_events,
	.connect	= evdev_connect,
	.disconnect	= evdev_disconnect,
	.legacy_minors	= true,
	.minor		= EVDEV_MINOR_BASE,
	.name		= "evdev",
	.id_table	= evdev_ids,
};

static int __init evdev_init(void)
{
	//注册input_handler
	return input_register_handler(&evdev_handler);
}

input_handler与input_dev匹配成功,调用evdev_connect:


static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
	struct evdev *evdev;
	int minor;
	int dev_no;
	int error;

	......
	
	//分配一个struct evdev结构体
	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	
	......

	//初始化分配的struct evdev结构体
	INIT_LIST_HEAD(&evdev->client_list);
	spin_lock_init(&evdev->client_lock);
	mutex_init(&evdev->mutex);
	init_waitqueue_head(&evdev->wait);
	evdev->exist = true;

	......

	dev_set_name(&evdev->dev, "event%d", dev_no);

	//初始化input_handle
	evdev->handle.dev = input_get_device(dev);
	evdev->handle.name = dev_name(&evdev->dev);
	evdev->handle.handler = handler;
	evdev->handle.private = evdev;

	evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
	evdev->dev.class = &input_class;
	evdev->dev.parent = &dev->dev;
	evdev->dev.release = evdev_free;
	device_initialize(&evdev->dev);

	//注册input_handle
	error = input_register_handle(&evdev->handle);
	if (error)
		goto err_free_evdev;

	//初始化一个字符设备,file_operations为evdev_fops
	cdev_init(&evdev->cdev, &evdev_fops);
	evdev->cdev.kobj.parent = &evdev->dev.kobj;

	//注册字符设备
	error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
	if (error)
		goto err_unregister_handle;

	error = device_add(&evdev->dev);
	
	......
	
}

前面分析事件处理层时,我们知道事件处理层除了处理驱动层提交的数据处理,还为应用层提供编程的接口,所以在evdev_connect函数里,会注册字符设备。

在evdev_connect函数里,会初始化一个struct input_handle并注册:

int input_register_handle(struct input_handle *handle)
{
	struct input_handler *handler = handle->handler;
	struct input_dev *dev = handle->dev;
	int error;


	error = mutex_lock_interruptible(&dev->mutex);
	if (error)
		return error;

	//把input_handle插入到input_dev的h_list链表
	if (handler->filter)
		list_add_rcu(&handle->d_node, &dev->h_list);
	else
		list_add_tail_rcu(&handle->d_node, &dev->h_list);

	mutex_unlock(&dev->mutex);


	//把input_handle插入到input_handler的h_list链表
	list_add_tail_rcu(&handle->h_node, &handler->h_list);

	//执行input_handler里的start函数
	if (handler->start)
		handler->start(handle);

	return 0;
}

evdev_connect函数执行完之后,input_dev与input_handler之间关系已建立,并在/dev目录下生成eventX设备文件,对应file_operations为evdev_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,
};

打开/dev/eventX,对应的调用到evdev_open:

struct evdev_client {
	unsigned int head;
	unsigned int tail;
	unsigned int packet_head; 
	spinlock_t buffer_lock; 
	struct fasync_struct *fasync;
	struct evdev *evdev;
	struct list_head node;
	unsigned int clk_type;
	bool revoked;
	unsigned long *evmasks[EV_CNT];
	unsigned int bufsize;
	struct input_event buffer[];
};


static int evdev_open(struct inode *inode, struct file *file)
{
	struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
	unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
	unsigned int size = sizeof(struct evdev_client) +
					bufsize * sizeof(struct input_event);
	struct evdev_client *client;
	int error;
	
	//为打开设备文件的每个进程分配一个evdev_client结构体
	client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
	if (!client)
		client = vzalloc(size);
	if (!client)
		return -ENOMEM;

	//设置evdev_client
	client->bufsize = bufsize;
	spin_lock_init(&client->buffer_lock);
	client->evdev = evdev;

	//把evdev_client插入到evdev的client_list链表
	evdev_attach_client(evdev, client);

	//open devcie
	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);
	kvfree(client);
	return error;
}

evdev_open_device:

static int evdev_open_device(struct evdev *evdev)
{
	int retval;

	retval = mutex_lock_interruptible(&evdev->mutex);
	if (retval)
		return retval;

	if (!evdev->exist)
		retval = -ENODEV;
	else if (!evdev->open++) { //首次打开
		//调用input_dev->open函数
		retval = input_open_device(&evdev->handle);
		if (retval)
			evdev->open--;
	}

	mutex_unlock(&evdev->mutex);
	return retval;
}

“read /dev/eventX”,对应的调用到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;
	size_t read = 0;
	int error;

	if (count != 0 && count < input_event_size())
		return -EINVAL;

	for (;;) {
			
		......

		//无数据,且不阻塞读取数据,返回-EAGAIN,这里使用环形队列的存取设备上报的事件
		if (client->packet_head == client->tail &&
		    (file->f_flags & O_NONBLOCK))
			return -EAGAIN;

		......

		while (read + input_event_size() <= count &&
		       evdev_fetch_next_event(client, &event)) {
			//有数据则拷贝回用户空间
			if (input_event_to_user(buffer + read, &event))
				return -EFAULT;

			read += input_event_size();
		}

		if (read)
			break;

		//无数据,阻塞读取数据则进程睡眠,等待有数据
		if (!(file->f_flags & O_NONBLOCK)) {
			error = wait_event_interruptible(evdev->wait,
					client->packet_head != client->tail ||
					!evdev->exist || client->revoked);
			if (error)
				return error;
		}
	}

	return read;
}

前面分析了,设备上报事件,核心层最终会调用到input_handler的events函数进行处理,对于evdev这个input_handler而言,events函数对应为evdev_events:

static void evdev_events(struct input_handle *handle,
			 const struct input_value *vals, unsigned int count)
{
	struct evdev *evdev = handle->private;
	struct evdev_client *client;
	ktime_t ev_time[EV_CLK_MAX];

	......
		//遍历evdev->client_list链表,调用evdev_pass_values,对应多个进程读取数据
		list_for_each_entry_rcu(client, &evdev->client_list, node)
			evdev_pass_values(client, vals, count, ev_time);
	......

	rcu_read_unlock();
}

evdev_pass_values:

static void evdev_pass_values(struct evdev_client *client,
			const struct input_value *vals, unsigned int count,
			ktime_t *ev_time)
{
	struct evdev *evdev = client->evdev;
	const struct input_value *v;
	struct input_event event;
	bool wakeup = false;

	......

	for (v = vals; v != vals + count; v++) {
		if (__evdev_is_filtered(client, v->type, v->code))
			continue;

		if (v->type == EV_SYN && v->code == SYN_REPORT) {
			/* drop empty SYN_REPORT */
			if (client->packet_head == client->head)
				continue;

			wakeup = true;
		}

		event.type = v->type;
		event.code = v->code;
		event.value = v->value;

		//把事件放入环形缓冲区
		__pass_event(client, &event);
	}

	spin_unlock(&client->buffer_lock);

	if (wakeup)
		wake_up_interruptible(&evdev->wait); //唤醒进程
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值