[关闭]
@zifehng 2017-05-26T13:44:26.000000Z 字数 3559 阅读 1668

内核驱动上报EV_LED事件失败原因分析

kernel input


之前在写驱动时使用如下方式上报一次EV_LED类型事件,使用getevent命令测试却一直接收不到事件。

  1. input_event(input_dev, EV_LED, LED_MISC, 1);
  2. input_sync(input_dev);

因为赶工无法深究原因,只能先参考普通按键上报方式(在上报抬起、按下两个事件后再上报同步事件)试着修改,结果上层收到了事件,测试通过。

  1. input_event(input_dev, EV_LED, LED_MISC, 0);
  2. input_event(input_dev, EV_LED, LED_MISC, 1);
  3. 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()开始分析:

  1. void input_event(struct input_dev *dev,
  2. unsigned int type, unsigned int code, int value)
  3. {
  4. unsigned long flags;
  5. /* 检测设备evbit位图是否支持事件总类型 */
  6. if (is_event_supported(type, dev->evbit, EV_MAX)) {
  7. spin_lock_irqsave(&dev->event_lock, flags);
  8. /* 处理event事件 */
  9. input_handle_event(dev, type, code, value);
  10. spin_unlock_irqrestore(&dev->event_lock, flags);
  11. }
  12. }

input_event()接口参数列表:

参数 描述
dev 上报事件的设备
type 事件总类型
code 事件子类型
value 事件值

经过设备evbit位图判断后,event事件被传递至input_handle_event()函数进一步处理:

  1. static void input_handle_event(struct input_dev *dev,
  2. unsigned int type, unsigned int code, int value)
  3. {
  4. int disposition;
  5. /* 获取event事件的描述符 */
  6. disposition = input_get_disposition(dev, type, code, value);
  7. /* 如果描述符包含“INPUT_PASS_TO_DEVICE”,调用设备自定义接口处理event事件 */
  8. if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
  9. dev->event(dev, type, code, value);
  10. if (!dev->vals)
  11. return;
  12. /* 如果描述符包含“INPUT_PASS_TO_HANDLERS”,event事件继续走通用上报流程 */
  13. if (disposition & INPUT_PASS_TO_HANDLERS) {
  14. struct input_value *v;
  15. if (disposition & INPUT_SLOT) {
  16. v = &dev->vals[dev->num_vals++];
  17. v->type = EV_ABS;
  18. v->code = ABS_MT_SLOT;
  19. v->value = dev->mt->slot;
  20. }
  21. /* 缓存event事件 */
  22. v = &dev->vals[dev->num_vals++];
  23. v->type = type;
  24. v->code = code;
  25. v->value = value;
  26. }
  27. /* 如果描述符包含“INPUT_FLUSH”,刷新设备缓冲区,上报之前缓存的event事件 */
  28. if (disposition & INPUT_FLUSH) {
  29. if (dev->num_vals >= 2)
  30. /* 上报event事件 */
  31. input_pass_values(dev, dev->vals, dev->num_vals);
  32. dev->num_vals = 0;
  33. /* 如果缓存的event事件超过上限,也进行上报处理 */
  34. } else if (dev->num_vals >= dev->max_vals - 2) {
  35. dev->vals[dev->num_vals++] = input_value_sync;
  36. /* 上报event事件 */
  37. input_pass_values(dev, dev->vals, dev->num_vals);
  38. dev->num_vals = 0;
  39. }
  40. }

描述符宏定义:

描述符宏定义 作用
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()函数,看看描述符是怎么生成的:

  1. static int input_get_disposition(struct input_dev *dev,
  2. unsigned int type, unsigned int code, int value)
  3. {
  4. /* 描述符默认值为“INPUT_IGNORE_EVENT” */
  5. int disposition = INPUT_IGNORE_EVENT;
  6. /* 根据event事件的总类型决定描述符 */
  7. switch (type) {
  8. ......
  9. /* 同步事件 */
  10. case EV_SYN:
  11. switch (code) {
  12. case SYN_CONFIG:
  13. disposition = INPUT_PASS_TO_ALL;
  14. break;
  15. case SYN_REPORT:
  16. disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
  17. break;
  18. case SYN_MT_REPORT:
  19. disposition = INPUT_PASS_TO_HANDLERS;
  20. break;
  21. }
  22. break;
  23. /* EV_LED事件 */
  24. case EV_LED:
  25. /* 判断ledbit位图与led位图 */
  26. if (is_event_supported(code, dev->ledbit, LED_MAX) &&
  27. !!test_bit(code, dev->led) != !!value) {
  28. /* 对led位图中LED_MISC事件的位值进行反置 */
  29. __change_bit(code, dev->led);
  30. disposition = INPUT_PASS_TO_ALL;
  31. }
  32. break;
  33. /* 其他事件 */
  34. ......
  35. }
  36. return disposition;
  37. }

在处理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”,否则事件将被过滤不做任何处理。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注