@zifehng
2017-05-26T05:44:26.000000Z
字数 3559
阅读 1865
kernel input
之前在写驱动时使用如下方式上报一次EV_LED类型事件,使用getevent命令测试却一直接收不到事件。
input_event(input_dev, EV_LED, LED_MISC, 1);input_sync(input_dev);
因为赶工无法深究原因,只能先参考普通按键上报方式(在上报抬起、按下两个事件后再上报同步事件)试着修改,结果上层收到了事件,测试通过。
input_event(input_dev, EV_LED, LED_MISC, 0);input_event(input_dev, EV_LED, LED_MISC, 1);input_sync(input_dev);
事后我猜想event事件是被过滤掉了:
1. 被getevent命令过滤;
2. 被内核input子系统过滤。
对于第一种猜想,我自己采用最简单的阻塞式读取方法,实现了一个C程序去轮询设备节点,却依然无法获取event事件,再结合getevent源码分析,可以排除getevnt命令过滤的可能性;
剩下的可能就是input子系统本身过滤了event事件,于是我从事件上报的入口函数input_event()分析,将事件上报流程梳理了一遍,希望从中找出答案。
代码分析流程:
input_event() -> input_handle_event() -> input_get_disposition()
-> input_pass_values()
首先从入口函数input_event()开始分析:
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value){unsigned long flags;/* 检测设备evbit位图是否支持事件总类型 */if (is_event_supported(type, dev->evbit, EV_MAX)) {spin_lock_irqsave(&dev->event_lock, flags);/* 处理event事件 */input_handle_event(dev, type, code, value);spin_unlock_irqrestore(&dev->event_lock, flags);}}
input_event()接口参数列表:
| 参数 | 描述 |
|---|---|
| dev | 上报事件的设备 |
| type | 事件总类型 |
| code | 事件子类型 |
| value | 事件值 |
经过设备evbit位图判断后,event事件被传递至input_handle_event()函数进一步处理:
static void input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value){int disposition;/* 获取event事件的描述符 */disposition = input_get_disposition(dev, type, code, value);/* 如果描述符包含“INPUT_PASS_TO_DEVICE”,调用设备自定义接口处理event事件 */if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)dev->event(dev, type, code, value);if (!dev->vals)return;/* 如果描述符包含“INPUT_PASS_TO_HANDLERS”,event事件继续走通用上报流程 */if (disposition & INPUT_PASS_TO_HANDLERS) {struct input_value *v;if (disposition & INPUT_SLOT) {v = &dev->vals[dev->num_vals++];v->type = EV_ABS;v->code = ABS_MT_SLOT;v->value = dev->mt->slot;}/* 缓存event事件 */v = &dev->vals[dev->num_vals++];v->type = type;v->code = code;v->value = value;}/* 如果描述符包含“INPUT_FLUSH”,刷新设备缓冲区,上报之前缓存的event事件 */if (disposition & INPUT_FLUSH) {if (dev->num_vals >= 2)/* 上报event事件 */input_pass_values(dev, dev->vals, dev->num_vals);dev->num_vals = 0;/* 如果缓存的event事件超过上限,也进行上报处理 */} else if (dev->num_vals >= dev->max_vals - 2) {dev->vals[dev->num_vals++] = input_value_sync;/* 上报event事件 */input_pass_values(dev, dev->vals, dev->num_vals);dev->num_vals = 0;}}
描述符宏定义:
| 描述符宏定义 | 值 | 作用 |
|---|---|---|
| INPUT_IGNORE_EVENT | 0 | 忽略该事件 |
| INPUT_PASS_TO_HANDLERS | 1 | 事件由handler处理 |
| INPUT_PASS_TO_DEVICE | 2 | 事件由设备处理 |
| INPUT_SLOT | 4 | 多点触摸事件标记 |
| INPUT_FLUSH | 8 | 刷新设备的事件缓冲区 |
| INPUT_PASS_TO_ALL | (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) | 事件由handler与设备同时处理 |
在input_handle_event()函数里,描述符disposition决定了event事件的后续处理流程,很有可能是因为描述符的原因,event事件被过滤不做任何处理,接下来我们分析input_get_disposition()函数,看看描述符是怎么生成的:
static int input_get_disposition(struct input_dev *dev,unsigned int type, unsigned int code, int value){/* 描述符默认值为“INPUT_IGNORE_EVENT” */int disposition = INPUT_IGNORE_EVENT;/* 根据event事件的总类型决定描述符 */switch (type) {....../* 同步事件 */case EV_SYN:switch (code) {case SYN_CONFIG:disposition = INPUT_PASS_TO_ALL;break;case SYN_REPORT:disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;break;case SYN_MT_REPORT:disposition = INPUT_PASS_TO_HANDLERS;break;}break;/* EV_LED事件 */case EV_LED:/* 判断ledbit位图与led位图 */if (is_event_supported(code, dev->ledbit, LED_MAX) &&!!test_bit(code, dev->led) != !!value) {/* 对led位图中LED_MISC事件的位值进行反置 */__change_bit(code, dev->led);disposition = INPUT_PASS_TO_ALL;}break;/* 其他事件 */......}return disposition;}
在处理EV_LED事件时,可以看到必须满足两个条件才能获得描述符“INPUT_PASS_TO
_ALL”:
第一个条件的结果毫无疑问为“true”,我们重点分析第二个条件:
!!test_bit(code, dev->led)读取led位图位置,__change_bit(code, dev->led)反置led位图位置,这两个相呼应的操作要求:在一次上报事件的周期内,EV_LED事件的“value”布尔值必须经过“true”和“false”的变化才能获取到描述符“INPUT_PASS_TO_ALL”,否则事件将被过滤不做任何处理。