5.输入子系统 4

evdev_open的实现过程

应用程序中调用了输入子系统的代码,数据是如何传递给用户层的?

open("/dev/event1", O_RDWR);

vfs
sys_open();
struct file file->f_ops = cdev->ops;
file->f_ops->open();

设备驱动层:输入子系统
input handler 层:evdev.c
cdev;
xxx_ops = {
.open = xxx_open,
.write = xxx_write,
}

 static const struct file_operations evdev_fops = {
				.owner		= THIS_MODULE,
				.read		= evdev_read,
				.write		= evdev_write,
				.poll		= evdev_poll,
				.open		= evdev_open,
 }

实际最终调用了evdev_open();
	|
	// 实际cdev是谁,就是evdev_connect注册的那个
	struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);

	// 通过儿子,找到老母input device
	unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);

	// size就包含了很多input_event
	unsigned int size = sizeof(struct evdev_client) +
					bufsize * sizeof(struct input_event);

	struct evdev_client *client;

	// 分配一个client对像,描述一个缓冲队列,存放的就是input_event
	client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);

	// client中有一个缓冲区
	client->bufsize = bufsize;
	spin_lock_init(&client->buffer_lock);
	//在client中记录evdev
	client->evdev = evdev;
	// 将client加入到evdev中一个小链表
	evdev_attach_client(evdev, client);
			|
			list_add_tail_rcu(&client->node, &evdev->client_list);

	// 将client记录到file,方面其他的接口使用
	file->private_data = client;

	总结:
		1,为输入设备分配一个缓冲区evdev_client,用户存放input device层上报的数据
		2,evdev_client记录到evdev中
		3,evdev_client记录到file中,方面其他的接口使用

evdev_read读取输入子系统数据的方式

应用程序中read,是如何获取到数据的
read(fd, &event, sizeof(struct input_event));
---------------------------------------------
vfs
sys_read();
file->f_ops->read();
-------------------------------------------
evdev.c
static const struct file_operations evdev_fops = {
.read = evdev_read,
evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
|
// 获取到open中分配的缓冲区对象
struct evdev_client *client = file->private_data;
//获取到evdev
struct evdev *evdev = client->evdev;
//表示一个数据包,要给用户
struct input_event event;

	for (;;) {
		// 实现非阻塞
		if (client->packet_head == client->tail &&
			(file->f_flags & O_NONBLOCK))
			return -EAGAIN;

			while (read + input_event_size() <= count &&
			// 1从缓冲区获取数据,存放在 input_event数据包
			   evdev_fetch_next_event(client, &event)) {
						|
						*event = client->buffer[client->tail++];
			// 2, 将数据上报给用户
			if (input_event_to_user(buffer + read, &event))
						|
						copy_to_user(buffer, event, sizeof(struct input_event)

			// 3,统计上报多少数据
			read += input_event_size();
		}

		// 如果当前是阻塞模式
		if (!(file->f_flags & O_NONBLOCK)) {
			//等待---休眠,需要被唤醒,有数据时候唤醒
			error = wait_event_interruptible(evdev->wait,
					client->packet_head != client->tail ||
					!evdev->exist || client->revoked);
	}

	总结:
		1,如果没有数据,就会休眠等待
		2,如果有数据,就从缓冲区client->buffer[client->tail++]拿数据
			通过copy_to_user上报给用户
	
		疑问:
			数据到底是如何存放在缓冲区的
			等待队列是谁唤醒的
			input_report_key(inputdev, pdesc->key_code, 0);
			input_sync(inputdev);//上报数据结束

input_event()上报数据的实现过程

input_report_key(inputdev, pdesc->key_code, 0);
input_sync(inputdev);//上报数据结束
|//input_event(dev, EV_KEY, code, !!value);
input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);// 上报数据
|
input_handle_event(dev, type, code, value);
|
// 如果将数据交给input handler去处理
if (disposition & INPUT_PASS_TO_HANDLERS) {
struct input_value *v;
//将input device获取到数据暂存一下input value
v = &dev->vals[dev->num_vals++];
v->type = type;
v->code = code;
v->value = value;

						input_pass_values(dev, dev->vals, dev->num_vals)
							|
							// 从input device中获取到input handle
						else {

						list_for_each_entry_rcu(handle, &dev->h_list, d_node)
							if (handle->open)
								count = input_to_handler(handle, vals, count);
										|
									// 通过handle儿子找到handler父亲
									struct input_handler *handler = handle->handler;
									// 如果有events函数指针,那就调用
									if (handler->events)
										handler->events(handle, vals, count);
									else if (handler->event)//否则就调用event();
										for (v = vals; v != end; v++)
											handler->event(handle, v->type, v->code, v->value);
									
									
					}

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,
};
总结: 如果将数据上报,最终是调用handler中events()或者event()
实际是evdev.c
.event = evdev_event,
.events = evdev_events,
|
// 拿到evdev,肯定要拿到缓冲区
struct evdev *evdev = handle->private;
struct evdev_client *client;

					// 获取到缓冲evdev_client
					else
					list_for_each_entry_rcu(client, &evdev->client_list, node)
						evdev_pass_values(client, vals, count,
								  time_mono, time_real);
							|
							// 通过client获取到evdev
							struct evdev *evdev = client->evdev;
							
							const struct input_value *v;
							struct input_event event;

							event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? mono : real);
							for (v = vals; v != vals + count; v++) {
								// 将input device上报的数据获取到,并且封装成input event对象
								event.type = v->type;
								event.code = v->code;
								event.value = v->value;
								// 将input event数据存放到缓冲区中
								__pass_event(client, &event);
										|
										client->buffer[client->head++] = *event;
										client->head &= client->bufsize - 1;
								for (v = vals; v != vals + count; v++) {
									// 将input device上报的数据获取到,并且封装成input event对象
									event.type = v->type;
									event.code = v->code;
									event.value = v->value;
									// 将input event数据存放到缓冲区中
									__pass_event(client, &event);
											client->buffer[client->head++] = *event;
											client->head &= client->bufsize - 1;
									// 如果调用 input_sync()
									if (v->type == EV_SYN && v->code == SYN_REPORT)
										wakeup = true;
								}

									// 唤醒等待队列
									if (wakeup)
										wake_up_interruptible(&evdev->wait);
		
		总结:
			input_report_key(inputdev, pdesc->key_code, 0);
				//将输入设备产生数据交给input handler,调用events();将数据存放在
				// 缓冲区client->buffer[client->head++] = *event;
			input_sync(inputdev);//上报数据结束
				// 唤醒等待队列,表示输入设备上报数据完毕
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值