一直都有报TP问题,但一般情况下都是效果类的,只要叫fae来改就行,也不是驱动能分析的,他们都有固有的一套参数来调试;再者,虽然每家TP厂的代码都不一样,但都要符合原理,都要能够正确的将最后的触点数据上报到input子系统。所以的话,TP这一块如果想直接做好业务这一块的话就要多多联系fae,他们才是最熟悉代码的人,加功能、修复bug,他们可以搞定。
但如果是上层出现问题丢给我们,那就要求驱动人员对input子系统的上报流程基本了解,这样的话也好定位问题;定位问题也是方案解决公司研发人员需要修炼的最基本的技能,其实对代码的了解不需要太深刻,我们只是在应用而已,这可能就是被称为工程师而不是程序员的原因吧。说归说,但我们研发也要有研发的样子,要对代码、原理做到精通。
接下来进入正题:屏幕上产生的触摸点是怎样上报到上层系统的呢?
Linux最经典的一句话就是:一切皆文件。
Linux的各种逻辑就是对文件进行读写增删(这时候我却又想到了数据库的基本操作,增删查改,哈哈),也就是说核心层和用户层的接口就是在于一个文件,你比如TP的操作就是底层将信息储存在 /sys/class/input/eventn 中,然后上层对其进行读取识别,然后根据其中的信息进行事件处理(所以文件系统应该是学linux必须要掌握的了吧,我先打脸,现在对文件系统还不是很熟悉)。
看一张图:
先看这个关键的文件节点是怎么创建的。
Input子系统的核心文件:/kernel-3.18/drivers/input/input.c
模块初始化代码:
static int __init input_init(void)
{
int err;
err = class_register(&input_class);
……
err = input_proc_init();
……
err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input");
……
}
class_register(&input_class) 是注册类的方式,可以在 /sys/class 下看到对应节点文件;
input_proc_init() 代码:
static int __init input_proc_init(void)
{
struct proc_dir_entry *entry;
proc_bus_input_dir = proc_mkdir("bus/input", NULL);//创建proc中的路径
……
entry = proc_create("devices", 0, proc_bus_input_dir,
&input_devices_fileops);//创建proc中的节点
……
entry = proc_create("handlers", 0, proc_bus_input_dir,
&input_handlers_fileops); //创建proc中的节点
……
}
register_chrdev_region(MKDEV(INPUT_MAJOR, 0), INPUT_MAX_CHAR_DEVICES, “input”);
注册input字符设备,主节点为INPUT_MAJOR==13,可以去input_fops里看注册函数,注册到/dev/input;
相应的对应关系可以使用adb 命令进入文件系统之后,cat /proc/bus/input/devices ,查看各个设备对应的event多少,比如
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="himax-touchscreen"
P: Phys=
S: Sysfs=/devices/virtual/input/input9
U: Uniq=
H: Handlers=gpufreq_ib event8
B: PROP=2
B: EV=b
B: KEY=400 0 0 0 2000000 0 40000800 40 0 0 0
B: ABS=6658000 0
Name就是对应的输入设备的名称,这里是 himax 的触摸屏,event8 就是事件序号,
我们在调试的时候直接 adb shell getevent /dev/input/event8,来实时捕捉 event8 中储存的数据。
getevent 查看我们所想要的是对应的 eventn。
然后分析下底层,TP 的驱动文件中处理事件的函数一般是 touch_event_handler,
其中最后一步上报坐标点都是下列格式:
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, w);
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1);
input_mt_sync(ts->input_dev);
这些函数跟踪的话会发现都会调用 input_handle_event,
比如:
input_report_abs
->input_event
->input_handle_event
->dev->event(dev, type, code, value);
->evdev.c/evdev_event()
->evdev_events
-> evdev_pass_values
将 type、value、code 存储在 evdev_client 的 struct input_event buffer[] 中。
再看 framework 上层怎么读取这个文件中的 buffer 的,我们从 InputReader.cpp 来分析,
路径在 frameworks/native/services/inputflinger/InputReader.cpp
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
->void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
...
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
可以跟踪到在构造函数里,mEventHub 是 eventHub 的实例,那么就是调用 eventHub 的 getEvents 方法。
文件在frameworks/native/services/inputflinger/EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
...
for (;;) {
...
scanDevicesLocked(); //这个往里走就是通过EventHub::openDeviceLocked 打开*DEVICE_PATH = "/dev/input" 这个设备 ,最终用的open,实际到kernel层就是input设备注册的open
...
int32_t readSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity); //这里的device->fd就是/dev/input/eventn这个设备文件,就是从这里读取出event的buffer
再往上就是对这些数据的处理了。
到这里就把上报数据的传递过程串起来了。
关于上层的处理,后面再整理。
吃水不忘挖井人:
input子系统详解:http://blog.csdn.net/column/details/input.html