输入子系统研究

本文详细分析了Linux内核输入子系统,包括驱动分析、整体结构、多层面联系及初始化过程。探讨了驱动与输入子系统如何通过input_dev结构体建立联系,并解析了事件处理流程,特别是input_report_abs函数在事件上报中的作用。此外,还研究了open函数的调用路径,揭示了evdev在事件处理中的关键角色。
摘要由CSDN通过智能技术生成

输入子系统驱动分析

之前分析触摸屏驱动仅仅从触摸屏驱动层面进行的分析,没有结合输入子系统进行分析,分析不够全面,这里首先对输入子系统进行一定程度的分析,然后结合输入子系统,对触摸屏驱动进行进一步的分析。

输入子系统整体分析

经过查询一系列的资料,对输入子系统有了一定的概念,首先讲一下关于输入子系统存在的作用。
输入子系统能够为输入设备提供很多的接口,这么做有很多的好处。
1、能够为驱动提供很多的接口,驱动可以调用这些接口,就可以节省时间,不再重复的去实现相同的接口。
2、用户层要实现功能也只需要调用事件处理层的接口,来是相应的功能。
针对以上优点进行详细的分析。
输入子系统的组成

多个层面之间的联系

初始化过程

首先分析驱动层与输入子系统核心层的联系。
继续以触摸屏驱动为例,驱动层与输入子系统之间的联系是在probe函数中实现的。

	input_dev = input_allocate_device();
	if (!input_dev) {
		dev_err(&pdev->dev, "failed to allocate input device.\n");
		err = -ENOMEM;
		goto err_free_mem;
	}
	ts_dev->input = input_dev;

这是probe函数中申请的input_dev结构体,并且将结构体赋予给了ts_dev结构体,也就是说触摸屏设备结构体中包含了input结构体的信息。

	input_dev->name = "Resistance_ts";
	input_dev->dev.parent = &pdev->dev;

	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

	input_set_abs_params(input_dev, ABS_X,
			ts_dev->x_min ? : 0,
			ts_dev->x_max ? : MAX_12BIT,
			0, 0);
	input_set_abs_params(input_dev, ABS_Y,
			ts_dev->y_min ? : 0,
			ts_dev->y_max ? : MAX_12BIT,
			0, 0);
	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);

	/* register to the input system */
	err = input_register_device(input_dev);
	if (err)
		goto err_fail;

以上函数对input_dev进行了数据的设置。
tscadc结构体
可以看到input结构体中的内容非常多,这个input结构体就是属于输入子系统的一部分,通过input结构体完成输入设备的通用性功能,tscadc结构体完成其独有的输入设备特性。
完成input_dev结构体的构建、注册之后,就可以通过这一结构体向输入子系统报告事件。
在注册input_dev时用到了input_attach_handler()函数。
具体的调用情况如下:

input_register_device()
    input_attach_handler()
        input_match_device()

这里主要是执行handler和input_dev是否匹配,handler可以处理input_dev的事件,那么就调用connect函数,将硬件和处理器连接。
至此完成初始化的分析。这个初始化过程将三个层面连接了起来,驱动层包含了input_dev,input_dev在注册过程中,通过handle将input_dev与handler连接起来。

handler、handle从何而来

可以看到在tsc驱动中并没有注册handler以及handle,但是在结合的时候,确实匹配成功,并且将其连接了起来,那么handler和handle是从哪里来的呢?
通过两个函数寻找handler和handle的来源。
分别是以下两个函数:

input_register_handler()
input_register_handle()

首先handler是一个处理器,可以被添加到多个支持它的设备中,也就是说一个handler只要能够处理设备的事件,就是可以被匹配成功的,根据输入子系统的分析,有一个evdev输入事件驱动是提供默认事件处理方法的驱动,evdev创建了evdev_handler处理器,在evdev初始化的过程中注册了handler,tsc中匹配时就是匹配的这一处理器。
handle是一个双向接口,一面接向input_dev,另一面接input_handler,在evdev的connect函数中,注册了这一结构体。也就是说handler匹配成功之后,会从connect中创建handle用于两者链接。

事件处理流程

初始化完成之后,设备已经具备了向子系统上报事件的能力,当产生一个事件时候,首先产生中断,然后从中断中进行数据处理,然后上报子系统。

#ifdef USER_A70
					input_report_abs(input_dev, ABS_X,
							val_y);
					input_report_abs(input_dev, ABS_Y,
							val_x);
#else
					input_report_abs(input_dev, ABS_X,
							val_x);
					input_report_abs(input_dev, ABS_Y,
							val_y);
#endif
					input_report_abs(input_dev, ABS_PRESSURE,
							z);
					input_report_key(input_dev, BTN_TOUCH,
							1);
					input_sync(input_dev);

以input_report_abs(input_dev, ABS_X,val_x)为例进行分析。

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_ABS, code, value);
}
void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)
{
	unsigned long flags;

	if (is_event_supported(type, dev->evbit, EV_MAX)) {

		spin_lock_irqsave(&dev->event_lock, flags);
		add_input_randomness(type, code, value);
		input_handle_event(dev, type, code, value);
		spin_unlock_irqrestore(&dev->event_lock, flags);
	}
}

可以通过以上代码看到,最核心的上传数据的函数是input_handle_event(dev, type, code, value)
对这个函数进行进一步分析。

	case EV_ABS:
		if (is_event_supported(code, dev->absbit, ABS_MAX))
			disposition = input_handle_abs_event(dev, code, &value);

		break;
input_handle_abs_event(struct input_dev *dev,unsigned int code, int *pval)
		input_abs_set_val(dev, ABS_MT_SLOT, dev->slot);
		input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);

该函数继续向下调用上述两个函数,一个是将数据刷新,另一个要调用相应的handler进行本次上报事件的处理。

static void input_pass_event(struct input_dev *dev,
			     unsigned int type, unsigned int code, int value)
{
	struct input_handler *handler;
	struct input_handle *handle;

	rcu_read_lock();

	handle = rcu_dereference(dev->grab);
	if (handle)
		handle->handler->event(handle, type, code, value);
	else {
		bool filtered = false;

		list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
			if (!handle->open)
				continue;

			handler = handle->handler;
			if (!handler->filter) {
				if (filtered)
					break;

				handler->event(handle, type, code, value);

			} else if (handler->filter(handle, type, code, value))
				filtered = true;
		}
	}

	rcu_read_unlock();
}

通过以上代码可知,如果强制指定了handle,就会直接调用事件处理。
如果未强制指定,就会遍历链表,寻找handle成员,只有在handle被打开时,才能够上报事件。
最终是调用的handler->event()处理本次事件。
最终是从此处从input_core层面上升到了事件处理层

验证处理事件是调用的evdev中的handler

如何验证,就是通过printk函数进行验证。
evdev_read函数
可以看到确实是调用了evdev_handler进行的数据处理。
evdev_read测试
通过以上简单处理,验证了evdev中注册的handler是与触摸屏驱动有关的
其实就是应用程序调用了handler的read函数。但可以确认触摸屏的handler就是evdev中注册的handler。

关于open函数的研究

关于open函数采用从上而下的研究方式,首先是由应用程序调用open,在之前已经验证了evdev_ops被调用的情况,所以open必然也是调用的evdev_open,
具体调用情况如下:

evdev_open()
    evdev_open_device()
        input_open_device()
            handle->dev->open()

可以看到evdev最后调用的open是input_dev的open函数,handle两侧分别是input_dev和handler,这在之前已经分析过。
所以open函数最终调用的应该是input_dev的open函数。
input_init
可以看到input_dev的open函数是用的input_open_file()函数。
input_open_file
hander获取到了当前要打开的节点的fops,最终执行的是该节点的open函数。也就是说打开某一个event,最终打开的设备的open,但是经过分析发现,实际dev并没有open函数,实际走的逻辑是,仅将fops付给了input,而没有执行open函数。
实际执行的逻辑

if(!new_fops||!new_fops->open)
{
    fops_put(new_fops);
    err=-ENODEV;
    goto out;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔通天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值