问题描述和分析:
在手机休眠状态下通过按键唤醒手机的过程中getevent获取到的event事件信息和正常状态下信息不一致,导致某应用程序出现异常。
正常状态下按下power key的log信息和其对应的event事件:
[ 45.663482] c0 [SPRD_EIC_KEYS_INFO] Pressed! Key:Power Key ScanCode:116 value:1
0001 0074 00000001
0000 0000 00000000
正常状态下释放power key的log信息和其对应的event事件:
[ 45.732818] c0 [SPRD_EIC_KEYS_INFO] Released! Key:Power Key ScanCode:116 value:0
0001 0074 00000000
0000 0000 00000000 正常释放事件对应的sync信息为全0
休眠状态下通过power key唤醒手机时的流程为:
按下power key之后便触发一个EIC中断,导致手机开始唤醒动作,kernel开始resume流程。该过程并不会检查按键是否抬起。
通常情况下,用户的操作是按下并很快释放,因此从kernel log中我们看到的现象和上述的正常流程是一样的。也就是有一个按下和一个释放的log出现。
但是如果如果我们持续用getevent去捕获事件信息的话,这个过程中我们却可以看到3组事件信息:一个按下,两个释放。
为了确认这个现象,我们又做了一个实验:只按下且不释放。
果然在这个实验中我们看到了kernel中的按下的log,但是getevent却得到了两组事件,一个按下,一个释放。
而且,释放对应的event事件和正常释放对应的事件有差异!
[ 45.663482] c0 [SPRD_EIC_KEYS_INFO] Pressed! Key:Power Key ScanCode:116 value:1
0001 0074 00000001
0000 0000 00000000
系统补发了一个释放事件:
0001 0074 00000000
0000 0000 00000001 注意这个补发事件的sync信息和正常的事件信息有小差异!
因此可以得到结论:在唤醒时,系统在某处补发了一个释放的事件。
因为getevent是运行在应用层的,它得到的事件都是来自kernel的,所以推测问题处在kernel。
而kernel中能发出一个事件的过程由驱动调用input子系统的相关函数来完成。
我们可以确认驱动没有做这个额外的动作,那么就剩下input子系统可以被怀疑了。
而问题仅仅在系统从休眠中被唤醒时,所以我们怀疑了一下input.c的resume流程,找到了疑点,这个过程中调用了一个reset函数,代码如下:
/** * input_reset_device() - reset/restore the state of input device * @dev: input device whose state needs to be reset * * This function tries to reset the state of an opened input device and * bring internal state and state if the hardware in sync with each other. * We mark all keys as released, restore LED state, repeat rate, etc. */ void input_reset_device(struct input_dev *dev) { mutex_lock(&dev->mutex); if (dev->users) { input_dev_toggle(dev, true); /* * Keys that have been pressed at suspend time are unlikely * to be still pressed when we resume. */ spin_lock_irq(&dev->event_lock); input_dev_release_keys(dev); spin_unlock_irq(&dev->event_lock); } mutex_unlock(&dev->mutex); } EXPORT_SYMBOL(input_reset_device); #ifdef CONFIG_PM static int input_dev_suspend(struct device *dev) { struct input_dev *input_dev = to_input_dev(dev); mutex_lock(&input_dev->mutex); if (input_dev->users) input_dev_toggle(input_dev, false); mutex_unlock(&input_dev->mutex); return 0; } static int input_dev_resume(struct device *dev) { struct input_dev *input_dev = to_input_dev(dev); input_reset_device(input_dev); return 0; }
可见,input子系统在resume时会检查每一个输入设备,如果该设备的按键处于按下状态,它就会强制补发一个释放事件。