openwrt led机制

openwrt led机制

今天在分析系统led灯闪烁状态机制。这里记录一下相关的笔记和流程。

和led相关的部分从2个流程来看:
流程1:系统启动时,由openwrt系统脚本设置LED状态。
流程2:系统启动后,由应用行为控制LED灯状态。

1 系统启动阶段

1.1 默认LED灯状态设置

1.1.1 LED GPIO识别

依赖两部分,首先是DTS中的GPIO定义,第二部分是相关内核模块加载。

1.1.1.1 DTS

例如 xxx.dts 文件中定义了GPIO和默认状态。

aliases {
    led-boot = &led_power;
    led-failsafe = &led_power;
    led-running = &led_power;
    led-upgrade = &led_power;
};

leds {
    compatible = "gpio-leds";
    led@6 {
        label = "green:wifi2";
        gpios = <&pca9555 6 GPIO_ACTIVE_LOW>;
        default-state = "off";
    };
    led@7 {
        label = "green:wifi5";
        gpios = <&pca9555 7 GPIO_ACTIVE_LOW>;
        default-state = "off";
    };
    led@10 {
        label = "green:cloud";
        gpios = <&pca9555 8 GPIO_ACTIVE_LOW>;
        default-state = "off";
    };
    led_power: led@11{
        label = "orange:sys";
        gpios = <&pca9555 9 GPIO_ACTIVE_LOW>;
        default-state = "off";
    };
};
1.1.1.2 内核模块

package的定义在 openwrt/package/kernel/linux/modules/leds.mk 中
kmod-leds-gpio
kmod-ledtrig-activity
kmod-ledtrig-transient

这里列举一下kmod的信息:

kmod-leds-gpio 的模块信息:

define KernelPackage/leds-gpio
  SUBMENU:=$(LEDS_MENU)
  TITLE:=GPIO LED support
  DEPENDS:= @GPIO_SUPPORT
  KCONFIG:=CONFIG_LEDS_GPIO
  FILES:=$(LINUX_DIR)/drivers/leds/leds-gpio.ko
  AUTOLOAD:=$(call AutoLoad,60,leds-gpio,1)
endef

define KernelPackage/leds-gpio/description
 Kernel module for LEDs on GPIO lines
endef

$(eval $(call KernelPackage,leds-gpio))

kmod-ledtrig-activity 的模块信息:

LED_TRIGGER_DIR=$(LINUX_DIR)/drivers/leds/trigger

define KernelPackage/ledtrig-activity
  SUBMENU:=$(LEDS_MENU)
  TITLE:=LED Activity Trigger
  KCONFIG:=CONFIG_LEDS_TRIGGER_ACTIVITY
  FILES:=$(LED_TRIGGER_DIR)/ledtrig-activity.ko
  AUTOLOAD:=$(call AutoLoad,50,ledtrig-activity)
endef

define KernelPackage/ledtrig-activity/description
 Kernel module that allows LEDs to blink based on system load
endef

$(eval $(call KernelPackage,ledtrig-activity))

kmod-ledtrig-transient 的模块信息:

define KernelPackage/ledtrig-transient
  SUBMENU:=$(LEDS_MENU)
  TITLE:=LED Transient Trigger
  KCONFIG:=CONFIG_LEDS_TRIGGER_TRANSIENT
  FILES:=$(LED_TRIGGER_DIR)/ledtrig-transient.ko
  AUTOLOAD:=$(call AutoLoad,50,ledtrig-transient,1)
endef

define KernelPackage/ledtrig-transient/description
 Kernel module that allows LEDs one time activation of a transient state.
endef

$(eval $(call KernelPackage,ledtrig-transient))

1.1.2 LED灯初始化阶段状态配置

在系统启动阶段,led的状态是由哪部分代码和功能去做的控制呢,我们接下来做一下梳理。


比如我当前的系统,SYS LED灯是个双色灯,其中绿色是硬件默认点亮,红色的灯是需要GPIO控制的,默认拉高,所以,如果需要点亮的话,设置GPIO为低电平(GPIO_ACTIVE_LOW)即可。

在进入kernel之后,因为硬件设计的原因,LED 绿色灯是常亮的,此时红色灯未点亮。
当系统启动进入procd管理阶段,红色 LED灯开始闪烁。
当系统启动项/etc/rc.d/S96LED 启动脚本执行后,红色LED灯熄灭,默认绿色LED常亮。
当系统升级时,红色LED灯闪烁。


接下来分析一下启动过程:

1.1.2.1 kernel引导启动

kernel引导启动后,在kernel代码 init/main.c文件中:

static int __ref kernel_init(void *unused)
{
    int ret;
    // ...
    /* init= 方式传递启动项参数到 execute_command */
    if (execute_command) {
        ret = run_init_process(execute_command);
        if (!ret)
            return 0;
    }
    if (!try_to_run_init_process("/sbin/init") ||
        !try_to_run_init_process("/etc/init") ||
        !try_to_run_init_process("/bin/init") ||
        !try_to_run_init_process("/bin/sh"))
        return 0;
}

老版本的openwrt系统,会有patch替换到启动脚本为 /etc/preinit ,但是我当前分析的系统 openwrt 21已经是使用默认的 /sbin/init 了。

1.1.2.2 进程启动

/sbin/init 进程分析
OpenWrt 启动顺序_preinit

kernel引导
/sbin/init
/sbin/procd -h /etc/hotplug-preinit.json
PREINIT=1
/bin/sh /etc/preinit
preinit 结束后,回调 /sbin/procd

再细化一下 /etc/preinit 执行的动作

/bin/sh /etc/preinit
hook节点初始化 preinit_essential preinit_main failsafe initramfs preinit_mount_root
遍历 /lib/preinit/* 文件进行 boot_hook_add
run preinit_essential 内函数
run preinit_main 内函数
1.1.2.3 LED灯控制

/etc/diag.sh 文件中封装了设置 led_state 的设置函数。
然后再看一下调用的位置:
1./lib/preinit/10_indicate_failsafe 文件中:
针对failsafe模式会有一个状态设置:
set_state failsafe
2./lib/preinit/50_indicate_regular_preinit 文件中:
set_state preinit_regular
3./lib/preinit/10_indicate_preinit 文件中:
set_state preinit
4./lib/upgrade/common.sh 文件中:
set_state upgrade
5./etc/init.d/done 文件中:
set_state done
6./etc/init.d/led 文件中:
set_state done
7./etc/rc.button/reset 文件中
set_state failsafe

设置时操作的led灯信息:

boot="$(get_dt_led boot)"
failsafe="$(get_dt_led failsafe)"
running="$(get_dt_led running)"
upgrade="$(get_dt_led upgrade)"

/lib/functions/leds.sh 文件中实现函数:

get_dt_led_path() {
        local ledpath
        local basepath="/proc/device-tree"
        local nodepath="$basepath/aliases/led-$1"

        [ -f "$nodepath" ] && ledpath=$(cat "$nodepath")
        [ -n "$ledpath" ] && ledpath="$basepath$ledpath"

        echo "$ledpath"
}

get_dt_led() {
        local label
        local ledpath=$(get_dt_led_path $1)

        [ -n "$ledpath" ] && \
                label=$(cat "$ledpath/label" 2>/dev/null) || \
                label=$(cat "$ledpath/chan-name" 2>/dev/null) || \
                label=$(basename "$ledpath")

        echo "$label"
}

我这边看了下我的设备中,相关路径下的led灯定义:

cat /proc/device-tree/aliases/led-boot
/leds/led@11
cat /proc/device-tree/aliases/led-running
/leds/led@11
cat /proc/device-tree/aliases/led-upgrade
/leds/led@11
cat /proc/device-tree/aliases/led-failsafe
/leds/led@11

这一块的LED灯定义是参考 dts aliases中配置,实际都是指向 sys led灯。
各种状态的不同点只是在于LED灯闪烁频率不一样等等。

所以在procd启动到/etc/init.d/led启动之前这段时间内。LED灯的控制主要是由 /lib/preinit/ 目录下相关脚本控制设置,然后是 /etc/init.d/done 。

1.2 LED灯相关配置文件生成

1.2.1 LED灯相关配置文件生成

三个配置文件,一个是 /tmp/board.json, 一个是 /etc/board.json, 另一个是 /etc/config/system

设备配置文件的生成有三种途径:
1./etc/board.d/目录下脚本
2./bin/config_generate 脚本 (/etc/init.d/boot 调用)
3.uci-defaults目录下脚本 (/etc/init.d/boot 调用)

1.2.1.1 board.json

针对/etc/board.d/目录下脚本,会有2个调用处。
第一个是
/lib/preinit/10_indicate_preinit -> preinit_ip -> preinit_config_board -> /bin/board_detect /tmp/board.json -> 调用执行 /etc/board.d/ 目录下的可执行文件,生成配置信息。 -> /etc/board.d/01_leds
第二个是
/etc/rc.d/S10boot -> /bin/config_generate -> /bin/board_detect /etc/board.json -> 调用执行 /etc/board.d/ 目录下的可执行文件,生成配置信息。 -> /etc/board.d/01_leds

/etc/board.d/01_leds 文件内容

#!/bin/sh
. /lib/functions/uci-defaults.sh
board_config_update
board=$(board_name)
case "$board" in
xx,xxxx)
        ucidef_set_led_default "system" "SYSTEM" "orange:sys" "0"
        ucidef_set_led_wlan "wlan5g" "WLAN5G" "green:wifi5" "phy0tx"
        ucidef_set_led_wlan "wlan2g" "WLAN2G" "green:wifi2" "phy1tx"
        ucidef_set_led_default "cloud" "CLOUD" "green:cloud" "0"
        ;;
esac
board_config_flush
exit 0

我看了下 board_config_update、ucidef_set_led_xxx 、 board_config_flush 等函数实现主要是生成json 格式内容。
具体的函数实现可以看一下 /lib/functions/uci-defaults.sh 脚本内实现方法。

1.2.1.2 /etc/config/system

前面生成了 /etc/board.json文件之后,在 /bin/config_generate 文件中生成system 配置文件:

if [ ! -s /etc/config/system ]; then
    touch /etc/config/system
    generate_static_system

    json_get_keys keys rssimon
    for key in $keys; do generate_rssimon $key; done

    json_get_keys keys gpioswitch
    for key in $keys; do generate_gpioswitch $key; done

    json_get_keys keys led
    for key in $keys; do generate_led $key; done
fi

generate_led 函数内实现了 led参数的 设置。

最终生成的 /etc/config/system 配置文件中led等信息如下:

config led 'led_system'
        option name 'SYSTEM'
        option sysfs 'orange:sys'
        option default '0'

2 系统启动后LED灯控制

系统启动后,控制LED灯状态改变有以下几种情况:
1.设置LED灯默认状态
/etc/init.d/led
2.升级过程中设置LED灯状态
在/lib/upgrade/common.sh 文件中set_state upgrade
3.按键超时
/etc/rc.button/reset 文件中set_state failsafe

你可能感兴趣的:(Opwnert,openwrt)