输入子系统驱动分析
之前分析触摸屏驱动仅仅从触摸屏驱动层面进行的分析,没有结合输入子系统进行分析,分析不够全面,这里首先对输入子系统进行一定程度的分析,然后结合输入子系统,对触摸屏驱动进行进一步的分析。
输入子系统整体分析
经过查询一系列的资料,对输入子系统有了一定的概念,首先讲一下关于输入子系统存在的作用。
输入子系统能够为输入设备提供很多的接口,这么做有很多的好处。
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进行了数据的设置。
可以看到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_handler进行的数据处理。
通过以上简单处理,验证了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_dev的open函数是用的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;
}