概述
input子系统设计的目的:将多种输入设备以统一类型的事件进行上报,Android上层通过inputreader读取绝对坐标事件(TP),相对坐标事件(鼠标),按键或者msc事件(键盘)。
然后Android上层根据事件类型分别给对应的处理者,比如绝对坐标事件就丢给WMS(我猜的)。个人认为这只是把输入设备的事件形成一套规范而已,也可以用不同的设备产生相同类型的事件达到一样的效果。
分析input子系统:
分为三层:抽象设备处理层 核心层 输入设备驱动层
抽象设备处理层:也就是handler层,有evdev.c joydev.c mousedev.c等,除了evdev可以处理所有事件以外,其他handler都是只能用于处理指定类型的事件;
核心层:维护了设备链表和驱动链表,主要还是给另外两层提供API,用于管理这俩链表;
输入设备驱动层:具体的硬件驱动,里面做的无非就是申请注册中断、向handler层上报事件。
注册handler流程:(以event.c为例)
input_register_handler(&event_handler);
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node) //这里会遍历所有的dev和当前handler匹配,有则调用connect
input_attach_handler(dev, handler);
id = input_match_device(handler, dev);
error = handler->connect(handler, dev, id); //比如 evdev_handler.connect
event_connect
kzalloc(sizeof(struct evdev), GFP_KERNEL); //实例化一个evdev,里面主要包含了handle(这里包含了handler和dev)和client_list(每次open该evdev时会创建一个client并加入该链表)
input_register_handle(&evdev->handle); //个人认为这里就是把所有handle串成一个链表。 每个handle和evdev都是单次匹配成功后的生成的对象。
注册device流程:(随便找了个tp驱动)
input_allocate_device(); //申请input设备
mk712_dev->open = mk712_open;
mk712_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
mk712_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); //设置能产生哪些事件
input_register_device(mk712_dev);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler
...... //同上了,最后匹配成功就connect
device上报input事件流程:
input_report_key input_report_rel input_report_abs 等API,最终都是调用到input_event,直接分析:
input_event
input_handle_event(dev, type, code, value);
input_pass_values(dev, dev->vals, dev->num_vals);
list_for_each_entry_rcu(handle, &dev->h_list, d_node) //应该是在这里,遍历dev的所有连接到的handle,依次分发事件给handler
if (handle->open)
count = input_to_handler(handle, vals, count);
handler->events //evdev_event为例
list_for_each_entry_rcu(client, &evdev->client_list, node) //遍历该evdev上所有的client(比如进程A和B都open了这个节点,就会有两个client),传送事件
evdev_pass_values(client, vals, count, ev_time); //__pass_event(client, &event); 通过这个把event写入到该client的buffer中
节点被open的流程:
evdev_open //以evdev为例,某个设备节点被打开 如/dev/class/input/event0
client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN) //如上,上层每open一次都会实例化一个client
evdev_open_device(evdev); //input_dev -> open
mk712_dev->open = mk712_open; //开始具体设备的初始化逻辑
上层读取input事件流程:(以TP为例,framework/native/.../EventHub.cpp中)
read(fd,/*buffer*/,/* count*sizeof(input_event) */,/* ppos */); //user space的buffer地址,几个input_event的大小
while (read + input_event_size() <= count && evdev_fetch_next_event(client, &event)) //读出指定大小的事件或者读到没事件为止
input_event_to_user(buffer + read, &event) //从client->buffer中取出一个event给到user buffer中,里面就是copy_to_user
以Android TP为例,看看一个事件是怎么处理的:
触摸屏幕 -> 由TP芯片向AP产生中断,唤醒等待队列 -> 通过I2C读取坐标值 -> 将坐标值写入client->buffer中 -> 上层应该是定时去读取buffer中的内容,不管是否有具体事件(EventHub.cpp中有个read(fd,…)) -> 经过WMS传递给Activity ->
依次传递给viewgroup view,如果是自定义的话可以设置拦截事件,一般默认是不拦截 -> view viewgroup activity依次看是否消费事件,消费则结束事件传递,不消费则还给上一层
用户空间从kernel读取到的单个事件的数据结构如下:
input子系统框架总结如下:
参考文章: https://www.cnblogs.com/deng-tao/p/6094049.html
最后
以上就是无限摩托为你收集整理的input子系统分析的全部内容,希望文章能够帮你解决input子系统分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复