谷动谷力

 找回密码
 立即注册
查看: 1362|回复: 1
打印 上一主题 下一主题
收起左侧

【OpenWRT】详解 OpenWRT RESET按键 键盘响应逻辑

[复制链接]
跳转到指定楼层
楼主
发表于 2024-1-11 17:16:13 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 sunsili 于 2024-1-11 17:17 编辑

详解 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服务响应程序。源码如下:
  1. static void state_enter(void)
  2. {
  3.         char ubus_cmd[] = "/sbin/ubusd";
  4.         switch (state) {
  5.         case STATE_EARLY:
  6.                 LOG("- early -\n");
  7.                 watchdog_init(0);
  8.                 hotplug("/etc/hotplug.json");   //热插拔函数初始化,hotplug.json文件内容非常关键
  9.                 procd_coldplug();                                //冷插拔函数初始化
  10.                 break;

  11.         case STATE_UBUS:
  12.                 // try to reopen incase the wdt was not available before coldplug

  13.                 watchdog_init(0);
  14.                 set_stdio("console");
  15.                 LOG("- ubus -\n");
  16.                 procd_connect_ubus();
  17.                 service_start_early("ubus", ubus_cmd);
  18.                 break;

  19.         case STATE_INIT:
  20.                 LOG("- init -\n");
  21.                 procd_inittab();
  22.                 procd_inittab_run("respawn");
  23.                 procd_inittab_run("askconsole");
  24.                 procd_inittab_run("askfirst");
  25.                 procd_inittab_run("sysinit");
  26.                 // switch to syslog log channel
  27.                 ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd");
  28.                 break;

  29.         case STATE_RUNNING:
  30.                 LOG("- init complete -\n");
  31.                 procd_inittab_run("respawnlate");                procd_inittab_run("askconsolelate");

  32.                 break;

  33. ..... 省略部分源码

  34.         default:
  35.                 ERROR("Unhandled state %d\n", state);

  36.                 return;
  37.         };
  38. }
复制代码

文件 /etc/hotplug.json 内容
  1. [

  2.         [ "case", "ACTION", {

  3.                 "add": [

  4.                         [ "if",

  5.                                 [ "and",

  6.                                         [ "has", "MAJOR" ],

  7.                                         [ "has", "MINOR" ]

  8.                                 ],

  9.                                 [

  10.                                         [ "if",

  11.                                                 [ "eq", "DEVNAME",

  12.                                                         [ "null", "full", "ptmx", "zero", "tty", "net", "random", "urandom" ]

  13.                                                 ],

  14.                                                 [

  15.                                                         [ "makedev", "/dev/%DEVNAME%", "0666" ],

  16.                                                         [ "return" ]
  17.                                                 ]
  18.                                         ],
  19.                                         [ "if",
  20.                                                 [ "regex", "DEVNAME", "^snd" ],
  21.                                                 [ "makedev", "/dev/%DEVNAME%", "0660", "audio" ]
  22.                                         ],
  23.                                         [ "if",
  24.                                                 [ "regex", "DEVNAME", "^tty" ],
  25.                                                 [ "makedev", "/dev/%DEVNAME%", "0660", "dialout" ]
  26.                                         ],
  27.                                         [ "if",
  28.                                                 [ "has", "DEVNAME" ],
  29.                                                 [ "makedev", "/dev/%DEVNAME%", "0600" ]
  30.                                         ]
  31.                                 ]
  32.                         ],
  33.                         [ "if",
  34.                                 [ "has", "FIRMWARE" ],
  35.                                 [
  36.                                         [ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ],
  37.                                         [ "load-firmware", "/lib/firmware" ],
  38.                                         [ "return" ]
  39.                                 ]
  40.                         ]
  41.                 ],
  42.                 "remove" : [
  43.                         [ "if",
  44.                                 [ "and",
  45.                                         [ "has", "DEVNAME" ],
  46.                                         [ "has", "MAJOR" ],
  47.                                         [ "has", "MINOR" ]

  48.                                 ],
  49.                                 [ "rm", "/dev/%DEVNAME%" ]
  50.                         ]
  51.                 ]
  52.         } ],
  53.         [ "if",
  54.                 [ "and",                                                                                //键盘事件
  55.                         [ "has", "BUTTON" ],
  56.                         [ "eq", "SUBSYSTEM", "button" ]
  57.                 ],
  58.                 [ "button", "/etc/rc.button/%BUTTON%" ]                        // 键盘事件执行的脚本位置

  59.         ],
  60.         [ "if",
  61.                 [ "and",                                                                                // usb串口事件
  62.                         [ "eq", "SUBSYSTEM", "usb-serial" ],
  63.                         [ "regex", "DEVNAME",
  64.                                 [ "^ttyUSB", "^ttyACM" ]
  65.                         ]
  66.                 ],
  67.                 [ "exec", "/sbin/hotplug-call", "tty" ],
  68.                 [ "if",
  69.                         [ "isdir", "/etc/hotplug.d/%SUBSYSTEM%" ],
  70.                         [ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ]
  71.                 ]
  72.         ]
  73. ]
复制代码

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

OpenWRT 系统中的按键处理方法如下:
在 etc/rc.button/ 文件夹下有热拔插事件响应脚本
按键热拔插事件、触发etc/rc.button/ 对应的按键响应脚本
系统按键响应逻辑是,执行响应脚本文件,用户可以直接修改脚本文件,来修订键盘处理响应逻辑。
查看 rc.button/
内容如下
  1. root@LEDE:/# ls /etc/rc.button/
  2. failsafe  power     reset     rfkill
复制代码

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

电源按键
  1. /# cat etc/rc.button/power       // 电源off执行内容

  2. #!/bin/sh

  3. [ "${ACTION}" = "released" ] || exit 0
  4. exec /sbin/poweroff

  5. return 0
复制代码


安全模式
  1. /# cat etc/rc.button/failsafe         // 进入安全模式

  2. #!/bin/sh
  3. [ "${TYPE}" = "switch" ] || echo ${BUTTON} > /tmp/failsafe_button

  4. return 0
复制代码

重点看 复位按键 的脚本

  1. root@LEDE:/# cat etc/rc.button/reset //复位按键处理
  2. #!/bin/sh
  3. . /lib/functions.sh
  4. OVERLAY="$( grep ' /overlay ' /proc/mounts )"
  5. case "$ACTION" in
  6. pressed)                                                                // 内核模块gpio_button_hotplug 发送事件类型pressed
  7.         [ -z "$OVERLAY" ] && return 0
  8.         return 5
  9. ;;
  10. timeout)
  11.         . /etc/diag.sh
  12.         set_state failsafe
  13. ;;
  14. released)                                                                // 内核模块gpio_button_hotplug回发送事件类型released,
  15.                                                                                 // 并携带按键时间SEEN变量值,因此脚本判定此变量的时间值
  16.         if [ "$SEEN" -lt 1 ]                                                // 小于 1s , 执行reboot指令
  17.         then
  18.                 echo "REBOOT" > /dev/console
  19.                 sync
  20.                 reboot                                                               
  21.         elif [ "$SEEN" -gt 5 -a -n "$OVERLAY" ]                // 按键超出 5s , 就执行 jffs2reset -y && reboot
  22.         then
  23.                 echo "FACTORY RESET" > /dev/console
  24.                 jffs2reset -y && reboot &                       
  25.         fi
  26. ;;
  27. esac
  28. return 0
复制代码

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

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

执行 jffs2reset -y 结果如下
  1. root@ixeRouter:~# jffs2reset -y                                                                                                                              

  2. /dev/mtdblock6 is mounted as /overlay, only erasing files
复制代码


查看分区
  1. root@ixeRouter:~# cat /proc/mtd                                                                                                                              

  2. dev:    size   erasesize  name

  3. mtd0: 00030000 00010000 "bootloader"

  4. mtd1: 00010000 00010000 "config"

  5. mtd2: 00010000 00010000 "factory"

  6. mtd3: 01fb0000 00010000 "firmware"

  7. mtd4: 0020670f 00010000 "kernel"

  8. mtd5: 01da98f1 00010000 "rootfs"

  9. mtd6: 00640000 00010000 "rootfs_data"
复制代码

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

jffs2reset 源码分析
查看 fstools 文件夹下的 CMakeList.txt文件内容,摘录部分内容如下:
  1. ADD_EXECUTABLE(mount_root mount_root.c) TARGET_LINK_LIBRARIES(mount_root fstools) INSTALL(TARGETS mount_root RUNTIME DESTINATION sbin)

  2. find_library(json NAMES json-c json)



  3. ADD_EXECUTABLE(blockd blockd.c) TARGET_LINK_LIBRARIES(blockd fstools ubus blobmsg_json ${json}) INSTALL(TARGETS blockd RUNTIME DESTINATION sbin)

  4. ADD_EXECUTABLE(block block.c probe.c probe-libblkid.c) IF(DEFINED CMAKE_UBIFS_EXTROOT) ADD_DEFINITIONS(-DUBIFS_EXTROOT)

  5.         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)



  6. # jffs2reset 命令内容对应的源码文件

  7. ADD_EXECUTABLE(jffs2reset jffs2reset.c) TARGET_LINK_LIBRARIES(jffs2reset fstools) INSTALL(TARGETS jffs2reset RUNTIME DESTINATION sbin)



  8. ADD_EXECUTABLE(snapshot_tool snapshot.c) TARGET_LINK_LIBRARIES(snapshot_tool fstools) INSTALL(TARGETS snapshot_tool RUNTIME DESTINATION sbin)

  9. ADD_EXECUTABLE(ubi ubi.c) TARGET_LINK_LIBRARIES(ubi ubi-utils ubox) INSTALL(TARGETS ubi RUNTIME DESTINATION sbin)
复制代码

jffs2reset.c 源码文件内容
如下:
  1. int main(int argc, char **argv)
  2. {
  3.         struct volume *v;
  4.         int ch, yes = 0, reset = 0;

  5.         while ((ch = getopt(argc, argv, "yr")) != -1) {
  6.                 switch(ch) {
  7.                 case 'y':
  8.                         yes = 1;
  9.                         break;
  10.                 case 'r':
  11.                         reset = 1;
  12.                         break;

  13.                 }
  14.         }

  15.         if (!yes && ask_user())
  16.                 return -1;

  17.         /*
  18.          * TODO: Currently this only checks if kernel supports OverlayFS. We
  19.          * should check if there is a mount point using it with rootfs_data
  20.          * as upperdir.
  21.          */

  22.         if (find_filesystem("overlay")) {
  23.                 ULOG_ERR("overlayfs not supported by kernel\n");
  24.                 return -1;
  25.         }
  26.         v = volume_find("rootfs_data");                                // 查找分区 rootfs_data

  27.         if (!v) {
  28.                 ULOG_ERR("MTD partition 'rootfs_data' not found\n");
  29.                 return -1;
  30.         }

  31.         volume_init(v);
  32.         if (!strcmp(*argv, "jffs2mark"))
  33.                 return jffs2_mark(v);
  34.         return jffs2_reset(v, reset);                                // 执行 jffs2_reset
  35. }
复制代码

jffs2_reset 函数内容
如下
  1. static int jffs2_reset(struct volume *v, int reset)
  2. {
  3.         char *mp;

  4.         mp = find_mount_point(v->blk, 1);

  5.         if (mp) {
  6.                 ULOG_INFO("%s is mounted as %s, only erasing files\n", v->blk, mp);
  7.                 fs_state_set("/overlay", FS_STATE_PENDING);
  8.                 overlay_delete(mp, false);                                                                //delete overlay分区内容
  9.                 mount(mp, "/", NULL, MS_REMOUNT, 0);
  10.         } else {
  11.                 ULOG_INFO("%s is not mounted\n", v->blk);
  12.                 return jffs2_mark(v);
  13.         }

  14.         if (reset) {
  15.                 sync();
  16.                 sleep(2);

  17.                 reboot(RB_AUTOBOOT);

  18.                 while (1)
  19.                         ;
  20.         }

  21.         return 0;
  22. }
复制代码

路由器系统文件构成
  1. root@ixeRouter:~# cat /proc/filesystems                                                                                                                     

  2. nodev   sysfs

  3. nodev   rootfs

  4. nodev   ramfs

  5. nodev   bdev

  6. nodev   proc

  7. nodev   tmpfs

  8. nodev   debugfs

  9. nodev   tracefs

  10. nodev   sockfs

  11. nodev   bpf

  12. nodev   pipefs

  13. // 路由器文件系统构成

  14. nodev   devpts

  15.         squashfs        //文件系统

  16. nodev   jffs2

  17. nodev   overlay                //文件系统

  18. nodev   ubifs
复制代码

+10
回复

使用道具 举报

沙发
 楼主| 发表于 2024-1-26 21:11:47 | 只看该作者
也有写/etc/config/system里的

  1. config button
  2.         option button 'BTN_3'
  3.         option action 'released'
  4.         option handler '/root/resetparam && reboot'
  5.         option min '1'
  6.         option max '3'

  7. config button
  8.         option button 'BTN_3'
  9.         option action 'released'
  10.         option handler '/root/resetall && reboot'
  11.         option min '5'
  12.         option max '30'
复制代码


+10
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|深圳市光明谷科技有限公司|光明谷商城|Sunshine Silicon Corpporation ( 粤ICP备14060730号|Sitemap

GMT+8, 2024-12-27 09:22 , Processed in 0.089203 second(s), 43 queries .

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表