input子系统分析

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值