38 --> 详解 OpenWRT RESET按键、键盘响应逻辑

OpenWrt 按键处理逻辑采用 hotplug 事件方式进行管理,reset按键,用来进行重启或者恢复出厂操作。热插拔事件流程:内核具有检测 键盘守护程序,gpio_button_hotplug 模块,源码位于 openwrt/package/kernel/gpio-button-hotplug 文件夹下,系统启动时加载键盘检测模块,就驻留到内核。
在这里插入图片描述
内核gpio_button_hotplug 模块检测到键盘后,通过netlink-kobject方式发送到用户空间,用户空间 ubusd 系统获得到事件后,执行键盘响应的回调函数,响应键盘。
本文直接分析用户空间程序处理逻辑,不分析内核键盘模块程序、请读者自行参考netlink框架和gpio_button_hotplug.c文件分析。

RESET按键的操作对应为:
单击 - 重启设备
长按 5秒以上 – 恢复出厂设置
当然,这些操作可以通过配置etc/rc.button/reset脚本处理方法,重新定义按键功能。为什么修改rese脚本就能够重新定义呢?
接下来逐步分析,揭晓 openWRT的按键管理逻辑。

hotplug 事件注册

在 《详解 OpenWRT系统框架基础软件模块之 procd》篇,分析过procd线程启动时,调用 openWRT的state_enter() 状态机函数,此函数中,在系统 early_init 阶段,调用 hotplug() 函数注册RPC服务响应程序。源码如下:

static void state_enter(void)
{
	char ubus_cmd[] = "/sbin/ubusd";

	switch (state) {
	case STATE_EARLY:
		LOG("- early -\n");
		watchdog_init(0);
		hotplug("/etc/hotplug.json");   //热插拔函数初始化,hotplug.json文件内容非常关键
		procd_coldplug();				//冷插拔函数初始化
		break;

	case STATE_UBUS:
		// try to reopen incase the wdt was not available before coldplug
		watchdog_init(0);
		set_stdio("console");
		LOG("- ubus -\n");
		procd_connect_ubus();
		service_start_early("ubus", ubus_cmd);
		break;

	case STATE_INIT:
		LOG("- init -\n");
		procd_inittab();
		procd_inittab_run("respawn");
		procd_inittab_run("askconsole");
		procd_inittab_run("askfirst");
		procd_inittab_run("sysinit");

		// switch to syslog log channel
		ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd");
		break;

	case STATE_RUNNING:
		LOG("- init complete -\n");
		procd_inittab_run("respawnlate");
		procd_inittab_run("askconsolelate");
		break;
..... 省略部分源码
	default:
		ERROR("Unhandled state %d\n", state);
		return;
	};
}

文件 /etc/hotplug.json 内容

[
	[ "case", "ACTION", {
		"add": [
			[ "if",
				[ "and",
					[ "has", "MAJOR" ],
					[ "has", "MINOR" ]
				],
				[
					[ "if",
						[ "eq", "DEVNAME",
							[ "null", "full", "ptmx", "zero", "tty", "net", "random", "urandom" ]
						],
						[
							[ "makedev", "/dev/%DEVNAME%", "0666" ],
							[ "return" ]
						]
					],
					[ "if",
						[ "regex", "DEVNAME", "^snd" ],
						[ "makedev", "/dev/%DEVNAME%", "0660", "audio" ]
					],
					[ "if",
						[ "regex", "DEVNAME", "^tty" ],
						[ "makedev", "/dev/%DEVNAME%", "0660", "dialout" ]
					],
					[ "if",
						[ "has", "DEVNAME" ],
						[ "makedev", "/dev/%DEVNAME%", "0600" ]
					]
				]
			],
			[ "if",
				[ "has", "FIRMWARE" ],
				[
					[ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ],
					[ "load-firmware", "/lib/firmware" ],
					[ "return" ]
				]
			]
		],
		"remove" : [
			[ "if",
				[ "and",
					[ "has", "DEVNAME" ],
					[ "has", "MAJOR" ],
					[ "has", "MINOR" ]
				],
				[ "rm", "/dev/%DEVNAME%" ]
			]
		]
	} ],
	[ "if",
		[ "and",										//键盘事件
			[ "has", "BUTTON" ],
			[ "eq", "SUBSYSTEM", "button" ]
		],
		[ "button", "/etc/rc.button/%BUTTON%" ]			// 键盘事件执行的脚本位置
	],
	[ "if",
		[ "and",										// usb串口事件
			[ "eq", "SUBSYSTEM", "usb-serial" ],
			[ "regex", "DEVNAME",
				[ "^ttyUSB", "^ttyACM" ]
			]
		],
		[ "exec", "/sbin/hotplug-call", "tty" ],
		[ "if",
			[ "isdir", "/etc/hotplug.d/%SUBSYSTEM%" ],
			[ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ]
		]
	]
]

由此文件看到 [ “button”, “/etc/rc.button/%BUTTON%” ] 内容,也就是说键盘热插拔事件发送到用户空间后,会触发程序执行此文件夹下的脚本。

OpenWRT 系统中的按键处理方法如下:

  • 在 etc/rc.button/ 文件夹下有热拔插事件响应脚本
  • 按键热拔插事件、触发etc/rc.button/ 对应的按键响应脚本
    系统按键响应逻辑是,执行响应脚本文件,用户可以直接修改脚本文件,来修订键盘处理响应逻辑。

查看 rc.button/

内容如下

root@LEDE:/# ls /etc/rc.button/
failsafe  power     reset     rfkill

响应脚本内容,分别如下:

  • 电源按键
/# cat etc/rc.button/power       // 电源off执行内容
#!/bin/sh

[ "${ACTION}" = "released" ] || exit 0

exec /sbin/poweroff

return 0
  • 安全模式
/# cat etc/rc.button/failsafe 	// 进入安全模式
#!/bin/sh

[ "${TYPE}" = "switch" ] || echo ${BUTTON} > /tmp/failsafe_button

return 0

重点看 复位按键 的脚本

root@LEDE:/# cat etc/rc.button/reset //复位按键处理
#!/bin/sh
. /lib/functions.sh
OVERLAY="$( grep ' /overlay ' /proc/mounts )"
case "$ACTION" in
pressed)								// 内核模块gpio_button_hotplug 发送事件类型pressed
	[ -z "$OVERLAY" ] && return 0
	return 5
;;
timeout)
	. /etc/diag.sh
	set_state failsafe
;;
released)								// 内核模块gpio_button_hotplug回发送事件类型released,
										// 并携带按键时间SEEN变量值,因此脚本判定此变量的时间值
	if [ "$SEEN" -lt 1 ]						// 小于 1s , 执行reboot指令
	then
		echo "REBOOT" > /dev/console
		sync
		reboot								
	elif [ "$SEEN" -gt 5 -a -n "$OVERLAY" ]		// 按键超出 5s , 就执行 jffs2reset -y && reboot
	then
		echo "FACTORY RESET" > /dev/console
		jffs2reset -y && reboot &			
	fi
;;
esac
return 0

至此,我们揭开reset按键功能、openWRT系统是处理的全过程。接下来重点分析恢复出厂功能是如何实现的,
下面内容的重点是文件系统知识,特别是 overlay 文件系统相关知识。关于 overlay 文件系统原理,推荐阅读 《深入理解 overlayfs (一、原理)》。

reset 处理逻辑分析

reset 按键超出 5s , 就执行 jffs2reset -y && reboot & 命令,我们进一步分析 jffs2reset 命令。

  • 执行 jffs2reset -y 结果如下
root@ixeRouter:~# jffs2reset -y                                                                                                                              
/dev/mtdblock6 is mounted as /overlay, only erasing files
  • 查看分区
root@ixeRouter:~# cat /proc/mtd                                                                                                                              
dev:    size   erasesize  name
mtd0: 00030000 00010000 "bootloader"
mtd1: 00010000 00010000 "config"
mtd2: 00010000 00010000 "factory"
mtd3: 01fb0000 00010000 "firmware"
mtd4: 0020670f 00010000 "kernel"
mtd5: 01da98f1 00010000 "rootfs"
mtd6: 00640000 00010000 "rootfs_data"

得知,mtd6中的内容为 rootfs_data, 也就是路由器设备配置相关内容被擦除,
系统启动后、恢复到出厂镜像配置状态。

jffs2reset 源码分析

查看 fstools 文件夹下的 CMakeList.txt文件内容,摘录部分内容如下:

ADD_EXECUTABLE(mount_root mount_root.c) TARGET_LINK_LIBRARIES(mount_root fstools) INSTALL(TARGETS mount_root RUNTIME DESTINATION sbin)
find_library(json NAMES json-c json)

ADD_EXECUTABLE(blockd blockd.c) TARGET_LINK_LIBRARIES(blockd fstools ubus blobmsg_json ${json}) INSTALL(TARGETS blockd RUNTIME DESTINATION sbin)
ADD_EXECUTABLE(block block.c probe.c probe-libblkid.c) IF(DEFINED CMAKE_UBIFS_EXTROOT) ADD_DEFINITIONS(-DUBIFS_EXTROOT)
	TARGET_LINK_LIBRARIES(block blkid-tiny dl uci ubox ubus blobmsg_json ubi-utils ${json}) ELSE(DEFINED CMAKE_UBIFS_EXTROOT) TARGET_LINK_LIBRARIES(block blkid-tiny dl uci ubox ubus blobmsg_json ${json}) ENDIF(DEFINED CMAKE_UBIFS_EXTROOT) INSTALL(TARGETS block RUNTIME DESTINATION sbin)

# jffs2reset 命令内容对应的源码文件
ADD_EXECUTABLE(jffs2reset jffs2reset.c) TARGET_LINK_LIBRARIES(jffs2reset fstools) INSTALL(TARGETS jffs2reset RUNTIME DESTINATION sbin)

ADD_EXECUTABLE(snapshot_tool snapshot.c) TARGET_LINK_LIBRARIES(snapshot_tool fstools) INSTALL(TARGETS snapshot_tool RUNTIME DESTINATION sbin)
ADD_EXECUTABLE(ubi ubi.c) TARGET_LINK_LIBRARIES(ubi ubi-utils ubox) INSTALL(TARGETS ubi RUNTIME DESTINATION sbin) 

jffs2reset.c 源码文件内容

如下:


int main(int argc, char **argv)
{
	struct volume *v;
	int ch, yes = 0, reset = 0;
	while ((ch = getopt(argc, argv, "yr")) != -1) {
		switch(ch) {
		case 'y':
			yes = 1;
			break;
		case 'r':
			reset = 1;
			break;
		}

	}

	if (!yes && ask_user())
		return -1;

	/*
	 * TODO: Currently this only checks if kernel supports OverlayFS. We
	 * should check if there is a mount point using it with rootfs_data
	 * as upperdir.
	 */
	if (find_filesystem("overlay")) {
		ULOG_ERR("overlayfs not supported by kernel\n");
		return -1;
	}

	v = volume_find("rootfs_data");				// 查找分区 rootfs_data 
	if (!v) {
		ULOG_ERR("MTD partition 'rootfs_data' not found\n");
		return -1;
	}

	volume_init(v);
	if (!strcmp(*argv, "jffs2mark"))
		return jffs2_mark(v);
	return jffs2_reset(v, reset);				// 执行 jffs2_reset
}

jffs2_reset 函数内容

如下


static int jffs2_reset(struct volume *v, int reset)
{
	char *mp;

	mp = find_mount_point(v->blk, 1);
	if (mp) {
		ULOG_INFO("%s is mounted as %s, only erasing files\n", v->blk, mp);
		fs_state_set("/overlay", FS_STATE_PENDING);
		overlay_delete(mp, false);								//delete overlay分区内容
		mount(mp, "/", NULL, MS_REMOUNT, 0);
	} else {
		ULOG_INFO("%s is not mounted\n", v->blk);
		return jffs2_mark(v);
	}

	if (reset) {
		sync();
		sleep(2);
		reboot(RB_AUTOBOOT);
		while (1)
			;
	}

	return 0;
}

  • 路由器系统文件构成
root@ixeRouter:~# cat /proc/filesystems                                                                                                                      
nodev   sysfs
nodev   rootfs
nodev   ramfs
nodev   bdev
nodev   proc
nodev   tmpfs
nodev   debugfs
nodev   tracefs
nodev   sockfs
nodev   bpf
nodev   pipefs
// 路由器文件系统构成
nodev   devpts
        squashfs	//文件系统
nodev   jffs2
nodev   overlay		//文件系统
nodev   ubifs
  • 10
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值