@无法关机

现象


设备为开机状态下,按下电源键(PWRON_KEY),无法关机

复现方法

按下电源键进行开机操作,在系统起来的过程中,更准确的描述是在开机logo出现的过程中,不断的连续按下电源键,当系统起来后,再点击电源键,设备就无法完成关机的功能。

调查过程:

查询内核源码
刚开始肯定是想到查看内核关于按键的源码,即找到以下文档
linux/kernel/drivers/input/keyboard/adc-keys.c
linux/kernel/drivers/input/keyboard/gpio_keys.c

后来发现没有看到关机相关的功能,随即查看原理图发现电源键是在rk806芯片上的一个按键,查看该芯片的数据手册,找到该芯片的驱动文件。
linux/ultr/linux/kernel/drivers/mfd/rk806-spi.c由下图找到806-core
@无法关机

/home/ultr/linux/ultr/linux/kernel/drivers/mfd/rk806-core.c 找到rk805-pwrkey
@无法关机
linux/ultr/linux/kernel/drivers/input/misc/rk805-pwrkey.c
@无法关机
电源键的上升沿中断处理函数中通过input子系统上报电源键的状态即同步电源键释放事件
而此时下降沿中断处理函数中的内容被注释


static int rk805_pwrkey_probe(struct platform_device *pdev)
{
	struct input_dev *pwr;
	int fall_irq, rise_irq;
	struct device_node *np;
	int err;

	np = of_get_child_by_name(pdev->dev.parent->of_node, "pwrkey");
	if (np && !of_device_is_available(np)) {
		dev_info(&pdev->dev, "device is disabled
");
		return -EINVAL;
	}

	pwr = devm_input_allocate_device(&pdev->dev);
	if (!pwr) {
		dev_err(&pdev->dev, "Can't allocate power button
");
		return -ENOMEM;
	}

	pwr->name = "rk805 pwrkey";
	pwr->phys = "rk805_pwrkey/input0";
	pwr->id.bustype = BUS_HOST;
	input_set_capability(pwr, EV_KEY, KEY_POWER);

	fall_irq = platform_get_irq(pdev, 0);
	if (fall_irq < 0)
		return fall_irq;

	rise_irq = platform_get_irq(pdev, 1);
	if (rise_irq < 0)
		return rise_irq;

	err = devm_request_any_context_irq(&pwr->dev, fall_irq,
					   pwrkey_fall_irq,
					   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
					   "rk805_pwrkey_fall", pwr);
	if (err < 0) {
		dev_err(&pdev->dev, "Can't register fall irq: %d
", err);
		return err;
	}

	err = devm_request_any_context_irq(&pwr->dev, rise_irq,
					   pwrkey_rise_irq,
					   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
					   "rk805_pwrkey_rise", pwr);
	if (err < 0) {
		dev_err(&pdev->dev, "Can't register rise irq: %d
", err);
		return err;
	}

	err = input_register_device(pwr);
	if (err) {
		dev_err(&pdev->dev, "Can't register power button: %d
", err);
		return err;
	}

	platform_set_drvdata(pdev, pwr);
	device_init_wakeup(&pdev->dev, true);

	return 0;
}

用户态查看是否有此输入设备 rk805 pwrkey


cat /proc/bus/input/devices  

@无法关机
正常情况下的事件上报
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/e134b5017b454402ab3eec3857ae849e.png@无法关机
非正常情况下没有事件上报的打印,但是却有中断


root@ubuntu2004:/home/rpdzkj#
root@ubuntu2004:/home/rpdzkj# cat /proc/interrupts | grep rk805
128:          0          0          0          0         16         17          0          0     rk806   0 Edge      rk805_pwrkey_fall
129:          0          0          0          0         15         18          0          0     rk806   1 Edge      rk805_pwrkey_rise
root@ubuntu2004:/home/rpdzkj#
root@ubuntu2004:/home/rpdzkj#
root@ubuntu2004:/home/rpdzkj# cat /proc/interrupts | grep rk805
128:          0          0          0          0         16         22          0          0     rk806   0 Edge      rk805_pwrkey_fall
129:          0          0          0          0         15         23          0          0     rk806   1 Edge      rk805_pwrkey_rise
root@ubuntu2004:/home/rpdzkj#

从内核态往用户态的线索止步于此,由因往果推无法继续,那就由果往因推
@无法关机
这是点击电源键正常关机的打印日志,systemd-shutdown出现次数最多,于是在根文件系统和内核中搜索此关键词,最终了解到 systemctl poweroff关机命令、 systemd-logind.service服务(systemd-logind)。
明白整个流程:按电源键–>电源中断–>通过input子系统上发按键事件到用户态–>systemd-logind.service–>服务调用关机命令–>进行关机。
目前就处于input子系统没有上传事件至用户态,这也是bug的根源所在

猜想时刻

由于按键下降沿中断处理函数内容被注释,因此,当系统起来过程中点击按键时input子系统上传按键按下事件,但此时根文件系统还没有挂载好,因此无法进行关机操作;
而且由于下降沿中断处理函数内容被注释导致没有改变按键的状态。当系统起来后再点击电源键,由于上传按键的状态没有变化,因此在用户态看不到事件打印log,只能看到中断次数增加。

似乎猜想很合理,看下input子系统机制

按键状态管理
Linux输入子系统内部维护着每个按键的当前状态。当你调用:


input_report_key(pwr, KEY_POWER, 1);  // 按下
input_sync(pwr);

输入子系统会:记录 KEY_POWER 的状态为 按下 (1),将事件发送到用户空间

如果按键一直处于”按下”状态而没有释放,当你再次按下物理按键时:


// 第二次按下物理按键
input_report_key(pwr, KEY_POWER, 1);  // 再次尝试上报"按下"
input_sync(pwr);

输入子系统会检查当前状态:

当前状态:KEY_POWER = 1 (已按下)
新上报状态:KEY_POWER = 1 (按下)

由于状态没有变化,输入子系统会认为这不是一个新事件,因此不会向用户空间发送任何数据!
输入子系统基于状态变化工作:只有状态改变时才产生事件

解决方法

两个独立中断:下降沿中断处理函数中添加注释掉的内容,以改变按键状态。
单个中断:如果仅用一个中断,那在中断处理函数中单次按下立即释放,以改变按键状态。


@无法关机

@无法关机

扩展

当点击电源键时,不直接关机,而是在屏幕上弹出一个窗口,让用户去选择是否关机
电源键触发关机窗口

© 版权声明

相关文章

暂无评论

none
暂无评论...