首页 > 代码库 > input.c中的resume会补发一个释放事件

input.c中的resume会补发一个释放事件

问题描述和分析:

在手机休眠状态下通过按键唤醒手机的过程中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;
}

而reset中又调用了 input_dev_release_keys(dev); 问题就在这里。通过注释掉该release动作,我们发现上述补发动作就没有了。

可见,input子系统在resume时会检查每一个输入设备,如果该设备的按键处于按下状态,它就会强制补发一个释放事件。


input.c中的resume会补发一个释放事件