From 71e07a1745dc6cd84ed65ac5dc9c0df5e9599afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Wed, 20 Oct 2021 09:53:10 +0200 Subject: [PATCH 1/7] rainbow: implement generic rainbow script The original implementation of rainbow was heavilly platform dependent. This tries to change that. It removes the original integration from omnia-rainbow and turris1x-rainbot packages and introduces the new one in rainbow. The rainbow package requires custom backends that implement platform dependent operations. The rainbow script tries to mimic the original rainbow applications but it also introduces the new possibilities. The most notable change is addition of priority levels and slots. Those are intended as a way for various applications to share control of LEDs without colliding with each other. This way we can for example use LEDs to signal running update or that there is notification for user present in web interface. With this rainbow also now supports triggering based on activity. The activity sources are defined by kernel and rainbow can only set them. One more addition are animations. The primary consideration in rainbow design here was what it actually should do now when it is mostly expected to have LEDs support in the kernel. There is led init script and leds configuration options in system config provided by OpenWrt. What rainbow provides on top of that (over just runtime modification) are additional options (although we might want to expand led service instead) but primarily it approaches single RGB LED as the one LED instead of three LEDs (or one weird one). Thus it allows more direct and understandable control and configuration over just led triggers. The aim is to reduce hacks in the current version in the future and have rainbow as a shared interface to the LEDs for system programs. --- hardware/omnia/omnia-rainbow/Makefile | 35 +--- .../omnia/omnia-rainbow/files/rainbow.config | 25 --- .../omnia/omnia-rainbow/files/rainbow.cron | 2 - .../omnia/omnia-rainbow/files/rainbow.init | 40 ---- .../files/rainbow_button_sync.sh | 15 -- hardware/rainbow/Makefile | 120 ++++++++++++ hardware/rainbow/files/README.md | 134 +++++++++++++ hardware/rainbow/files/animation.sh | 79 ++++++++ hardware/rainbow/files/animation.test.sh | 37 ++++ hardware/rainbow/files/animator.py | 183 ++++++++++++++++++ .../rainbow/files/animator_backend.example.py | 14 ++ hardware/rainbow/files/backend.example.sh | 82 ++++++++ hardware/rainbow/files/brightness.sh | 73 +++++++ hardware/rainbow/files/button_sync.sh | 22 +++ hardware/rainbow/files/color.sh | 115 +++++++++++ hardware/rainbow/files/color.test.sh | 75 +++++++ hardware/rainbow/files/compat.sh | 64 ++++++ hardware/rainbow/files/led.sh | 154 +++++++++++++++ hardware/rainbow/files/led_activity.sh | 173 +++++++++++++++++ hardware/rainbow/files/led_animate.sh | 42 ++++ hardware/rainbow/files/ledid.sh | 38 ++++ hardware/rainbow/files/ledid.test.sh | 49 +++++ hardware/rainbow/files/pci.sh | 28 +++ hardware/rainbow/files/rainbow-animator.init | 27 +++ .../rainbow/files/rainbow-button-sync.init | 15 ++ hardware/rainbow/files/rainbow.init | 22 +++ hardware/rainbow/files/rainbow.sh | 152 +++++++++++++++ hardware/rainbow/files/reset.sh | 58 ++++++ hardware/rainbow/files/state.sh | 76 ++++++++ hardware/rainbow/files/state.test.sh | 119 ++++++++++++ hardware/rainbow/files/uci.sh | 149 ++++++++++++++ hardware/rainbow/files/utils.sh | 29 +++ hardware/turris1x/turris1x-rainbow/Makefile | 24 +-- .../turris1x-rainbow/files/rainbow.config | 25 --- .../turris1x-rainbow/files/rainbow.cron | 2 - .../turris1x-rainbow/files/rainbow.init | 42 ---- .../files/rainbow_button_sync.sh | 15 -- 37 files changed, 2135 insertions(+), 219 deletions(-) delete mode 100644 hardware/omnia/omnia-rainbow/files/rainbow.config delete mode 100644 hardware/omnia/omnia-rainbow/files/rainbow.cron delete mode 100644 hardware/omnia/omnia-rainbow/files/rainbow.init delete mode 100755 hardware/omnia/omnia-rainbow/files/rainbow_button_sync.sh create mode 100644 hardware/rainbow/Makefile create mode 100644 hardware/rainbow/files/README.md create mode 100644 hardware/rainbow/files/animation.sh create mode 100755 hardware/rainbow/files/animation.test.sh create mode 100755 hardware/rainbow/files/animator.py create mode 100644 hardware/rainbow/files/animator_backend.example.py create mode 100644 hardware/rainbow/files/backend.example.sh create mode 100644 hardware/rainbow/files/brightness.sh create mode 100755 hardware/rainbow/files/button_sync.sh create mode 100644 hardware/rainbow/files/color.sh create mode 100755 hardware/rainbow/files/color.test.sh create mode 100644 hardware/rainbow/files/compat.sh create mode 100644 hardware/rainbow/files/led.sh create mode 100644 hardware/rainbow/files/led_activity.sh create mode 100644 hardware/rainbow/files/led_animate.sh create mode 100644 hardware/rainbow/files/ledid.sh create mode 100755 hardware/rainbow/files/ledid.test.sh create mode 100644 hardware/rainbow/files/pci.sh create mode 100755 hardware/rainbow/files/rainbow-animator.init create mode 100755 hardware/rainbow/files/rainbow-button-sync.init create mode 100755 hardware/rainbow/files/rainbow.init create mode 100755 hardware/rainbow/files/rainbow.sh create mode 100644 hardware/rainbow/files/reset.sh create mode 100644 hardware/rainbow/files/state.sh create mode 100755 hardware/rainbow/files/state.test.sh create mode 100644 hardware/rainbow/files/uci.sh create mode 100644 hardware/rainbow/files/utils.sh delete mode 100644 hardware/turris1x/turris1x-rainbow/files/rainbow.config delete mode 100644 hardware/turris1x/turris1x-rainbow/files/rainbow.cron delete mode 100644 hardware/turris1x/turris1x-rainbow/files/rainbow.init delete mode 100755 hardware/turris1x/turris1x-rainbow/files/rainbow_button_sync.sh diff --git a/hardware/omnia/omnia-rainbow/Makefile b/hardware/omnia/omnia-rainbow/Makefile index a8bf84683..3eca71f1b 100644 --- a/hardware/omnia/omnia-rainbow/Makefile +++ b/hardware/omnia/omnia-rainbow/Makefile @@ -1,5 +1,5 @@ # -## Copyright (C) 2016-2019 CZ.NIC z.s.p.o. (http://www.nic.cz/) +## Copyright (C) 2016-2021 CZ.NIC z.s.p.o. (http://www.nic.cz/) # ## This is free software, licensed under the GNU General Public License v3. # See /LICENSE for more information. @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=omnia-rainbow PKG_VERSION:=3.1 -PKG_RELEASE:=2 +PKG_RELEASE:=3 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://gitlab.nic.cz/turris/rainbow_omnia.git @@ -25,45 +25,16 @@ include $(INCLUDE_DIR)/package.mk define Package/omnia-rainbow TITLE:=omnia-rainbow URL:=https://gitlab.nic.cz/turris/rainbow_omnia - PROVIDES:=turris-rainbow DEPENDS:=@TARGET_mvebu_cortexa9_DEVICE_cznic_turris-omnia endef -define Package/omnia-rainbow/conffiles -/etc/config/rainbow -endef - define Build/Compile $(MAKE_VARS) $(MAKE) -C $(PKG_BUILD_DIR) $(MAKE_FLAGS) endef define Package/omnia-rainbow/install $(INSTALL_DIR) $(1)/usr/bin/ - $(INSTALL_BIN) $(PKG_BUILD_DIR)/rainbow $(1)/usr/bin/ - $(INSTALL_DIR) $(1)/etc/init.d/ - $(INSTALL_BIN) ./files/rainbow.init $(1)/etc/init.d/rainbow - $(INSTALL_DIR) $(1)/etc/config/ - $(INSTALL_DATA) ./files/rainbow.config $(1)/etc/config/rainbow - $(INSTALL_DIR) $(1)/etc/cron.d - $(INSTALL_DATA) ./files/rainbow.cron $(1)/etc/cron.d/rainbow - $(INSTALL_BIN) ./files/rainbow_button_sync.sh $(1)/usr/bin/rainbow_button_sync.sh -endef - -define Package/omnia-rainbow/postinst -#!/bin/sh -[ -n "$$IPKG_INSTROOT" ] || { - /etc/init.d/rainbow enable - /etc/init.d/rainbow restart -} -endef - -define Package/omnia-rainbow/prerm -#!/bin/sh -[ -n "$$IPKG_INSTROOT" ] || { - /etc/init.d/rainbow stop - /etc/init.d/rainbow disable - rainbow all auto white -} + $(INSTALL_BIN) $(PKG_BUILD_DIR)/rainbow $(1)/usr/bin/omnia-rainbow endef $(eval $(call BuildPackage,omnia-rainbow)) diff --git a/hardware/omnia/omnia-rainbow/files/rainbow.config b/hardware/omnia/omnia-rainbow/files/rainbow.config deleted file mode 100644 index 88122fcf4..000000000 --- a/hardware/omnia/omnia-rainbow/files/rainbow.config +++ /dev/null @@ -1,25 +0,0 @@ -# You can set all the leds at once. -config led 'all' - option color 'white' - option status 'auto' - -# Any other config will overwrite the value -#config led 'pwr' -# option color 'FF0066' -# option status 'auto' -# -#config led 'lan0' -# option color 'FFFF00' -# option status 'auto' -# -#config led 'wan' -# option color 'green' -# option status 'auto' -# -#config led 'pci1' -# option color 'blue' -# option status 'auto' -# -# Available LEDs: all, pwr, lan0, lan1, lan2, lan3, wan, pci1, pci2, pci3, usr1, usr2 -# Available color: white, black, red, green, blue, or use hex code of color like: FFFF00 -# For more information: rainbow --help diff --git a/hardware/omnia/omnia-rainbow/files/rainbow.cron b/hardware/omnia/omnia-rainbow/files/rainbow.cron deleted file mode 100644 index cb4d5923d..000000000 --- a/hardware/omnia/omnia-rainbow/files/rainbow.cron +++ /dev/null @@ -1,2 +0,0 @@ -MAILTO="" -* * * * * root /usr/bin/rainbow_button_sync.sh diff --git a/hardware/omnia/omnia-rainbow/files/rainbow.init b/hardware/omnia/omnia-rainbow/files/rainbow.init deleted file mode 100644 index 1f1d48059..000000000 --- a/hardware/omnia/omnia-rainbow/files/rainbow.init +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh /etc/rc.common - -# Start at the end, so the colors change after the boot is done. -START=99 -STOP=00 - -PID_FILE="/var/run/rainbow.pid" - -reload() { - # Set the colors based on user preferrences - uci show rainbow | sed -ne 's/rainbow\.\([^.]*\)=led/\1/p' | sort | while read SECTION ; do - STATUS=$(uci get rainbow.$SECTION.status) - COLOR=$(uci get rainbow.$SECTION.color) - rainbow $SECTION $STATUS $COLOR - done -} - -restart() { - rainbow all enable green - sleep 1 - reload -} - -start() { - ( - # Signal the boot is finished - rainbow all enable blue - sleep 1 - rainbow all enable green - sleep 1 - reload - # Resume last intensity settings - rainbow intensity $(cat /etc/rainbow.magic) - ) & -} - -stop() { - # Reset to the default, so a reboot can be recognized. - rainbow all enable white -} diff --git a/hardware/omnia/omnia-rainbow/files/rainbow_button_sync.sh b/hardware/omnia/omnia-rainbow/files/rainbow_button_sync.sh deleted file mode 100755 index 5f30d9f3c..000000000 --- a/hardware/omnia/omnia-rainbow/files/rainbow_button_sync.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -MAGIC_FILE="/etc/rainbow.magic" - -# Get actual intensity -ACT_INTENSITY=$(rainbow get intensity) - -# Get value from magic file -[ -f "$MAGIC_FILE" ] || echo 0 > "$MAGIC_FILE" -LAST_VALUE=$(cat "$MAGIC_FILE") - -# Compare them and if last value is different from stored one update it -if [ "$ACT_INTENSITY" != "$LAST_VALUE" ]; then - echo $ACT_INTENSITY > "$MAGIC_FILE" -fi diff --git a/hardware/rainbow/Makefile b/hardware/rainbow/Makefile new file mode 100644 index 000000000..e11e900b1 --- /dev/null +++ b/hardware/rainbow/Makefile @@ -0,0 +1,120 @@ +# +## Copyright (C) 2021 CZ.NIC z.s.p.o. (http://www.nic.cz/) +# +## This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# # +# +include $(TOPDIR)/rules.mk + +PKG_NAME:=rainbow +PKG_VERSION:=1.0.0 + +PKG_MAINTAINER:=CZ.NIC + +include $(INCLUDE_DIR)/package.mk + +define Package/Common + TITLE:=rainbow + URL:=https://gitlab.nic.cz/turris/rainbow_omnia + DEPENDS:=@(TARGET_mvebu_cortexa9_DEVICE_cznic_turris-omnia||TARGET_mpc85xx_p2020_DEVICE_turris1x||TARGET_mvebu_cortexa53_DEVICE_cznic-mox) \ +endef + +define Package/rainbow + $(call Package/Common) + DEPENDS+= +rainbow-backend + PROVIDES:=turris-rainbow +endef + +define Package/rainbow-button-sync + $(call Package/Common) + TITLE+=-button-sync +endef + +define Package/rainbow-animator + $(call Package/Common) + TITLE+=-animator + DEPENDS+= +python3-base +endef + +define Package/rainbow/description + Leds control on Turris Omnia, Mox and 1.x routers. +endef + +define Package/rainbow-button-sync/description + Button synchronization extension for Turris Rainbow. This is used to synchronize system configuration with hardware button. +endef + +define Package/rainbow-animator/description + Animation extension for Turris Rainbow. This is used to animate leds in case of missing or unusable kernel support. +endef + +define Package/rainbow/install + $(INSTALL_DIR) $(1)/usr/bin + ln -sf /usr/libexec/rainbow/rainbow.sh $(1)/usr/bin/rainbow + $(INSTALL_DIR) $(1)/etc/init.d/ + $(INSTALL_BIN) ./files/rainbow.init $(1)/etc/init.d/rainbow + $(INSTALL_DIR) $(1)/etc/config/ + touch $(1)/etc/config/rainbow + + $(INSTALL_DIR) $(1)/usr/libexec/rainbow + $(INSTALL_BIN) ./files/rainbow.sh $(1)/usr/libexec/rainbow/ + $(INSTALL_DATA) \ + ./files/animation.sh \ + ./files/brightness.sh \ + ./files/color.sh \ + ./files/compat.sh \ + ./files/led.sh \ + ./files/led_activity.sh \ + ./files/led_animate.sh \ + ./files/ledid.sh \ + ./files/pci.sh \ + ./files/reset.sh \ + ./files/state.sh \ + ./files/uci.sh \ + ./files/utils.sh \ + $(1)/usr/libexec/rainbow/ +endef + +define Package/rainbow-button-sync/install + $(INSTALL_DIR) $(1)/etc/init.d/ + $(INSTALL_BIN) ./files/rainbow-button-sync.init $(1)/etc/init.d/rainbow-button-sync + + $(INSTALL_DIR) $(1)/usr/libexec/rainbow + $(INSTALL_BIN) ./files/button_sync.sh $(1)/usr/libexec/rainbow/ +endef + +define Package/rainbow-animator/install + $(INSTALL_DIR) $(1)/etc/init.d/ + $(INSTALL_BIN) ./files/rainbow-animator.init $(1)/etc/init.d/rainbow-animator + + $(INSTALL_DIR) $(1)/usr/libexec/rainbow + $(INSTALL_BIN) ./files/animator.py $(1)/usr/libexec/rainbow/ +endef + +define Package/rainbow/conffiles +/etc/config/rainbow +endef + +Build/Compile:=: + +define Package/rainbow/postinst +#!/bin/sh +[ -n "$$IPKG_INSTROOT" ] || { + /etc/init.d/rainbow enable + /etc/init.d/rainbow restart +} +endef + +define Package/rainbow/prerm +#!/bin/sh +[ -n "$$IPKG_INSTROOT" ] || { + /etc/init.d/rainbow stop + /etc/init.d/rainbow disable + rainbow all auto white +} +endef + +$(eval $(call BuildPackage,rainbow)) +$(eval $(call BuildPackage,rainbow-button-sync)) +$(eval $(call BuildPackage,rainbow-animator)) diff --git a/hardware/rainbow/files/README.md b/hardware/rainbow/files/README.md new file mode 100644 index 000000000..f896dc9dd --- /dev/null +++ b/hardware/rainbow/files/README.md @@ -0,0 +1,134 @@ +# Rainbow + +Rainbow is RGB LEDs manipulation script. It is intended for runtime changes of +color and brightness of LEDs. The permanent configuration that is used as a +bases for runtime changes is stored in UCI (it has to be used directly to modify +it). + + +## UCI Configuration + +The primary UCI config of Rainbow is `rainbow`. At the same time this is not the +only config Rainbow honors and uses. There is also `system`. The reason for this +is because OpenWrt's native LEDs handling already supports some of the features +Rainbow implements and having configuration on multiple places results in +conflicts. Rainbow tries to resolve them by almost always preferring `system` +configuration. + +The `rainbow` config is expected to contain sections of type `led` where section +name is LED name or `all` for all LEDs. The available led names can be listed +using `rainbow -l`. + +The `rainbow.LED` (where `LED` is valid name of LED) sections can contain +options: + +* `color`: default color of LED (the default depends on specific LED). See + `rainbow all -h` for possible values. Note that inheritence is not supported + in UCI configuration as there is nowhere to iherit values from. +* `mode`: default mode of LED. Continue reading for all available modes (the + default is `auto`). + +The section `all` serves as global defaults for other LEDs. It has also one +additional option `brightness`. It is an integer between 0 and 255 specifying +global LEDs brightness. + +The `mode` option can have the following values: + +* `auto`: the default behavior for given LED. +* `disable`: disable LED, so it simply is not used. +* `enable`: enable LED, so it always shines when router is powered on. +* `activity`: the LED should blink with specific system events (see following + section for explanation and configuration). This is not available on all + platforms as it depends on Kernel support! +* `animate`: performs specified animation (see the following section for + explanation and configuration). +* `ignore`: instruct Rainbow that this LED should be ignored by it (not + touched). + +### Activity + +This mode allows LED to be controlled by events happening in the system (Kernel +events). This configuration is mostly possible in UCI config `system` and thus +we allow use that as well. When trigger is configured in both configs the +`sytem` is used. + +The activity is not available on all platforms due to implementation. The +notable omission are Turris 1.x routers. + +The supported activity triggers (UCI config `trigger` in appropriate LED section +in `rainbow` config): + +* `activity`: Flash led with system activity (faster blinking for higher + activity). This is default if no `trigger` option is specified. +* `activity-inverted`: Blink led with system activity (same as `activity` but in + default it LED is enabled). +* `heartbeat`: Variant on `activity` that blinks twice instead of once. +* `heartbeat-inverted`: Variant on `activity-inverted` for `heartbeat`. +* `disk-activity`: Blinks with disk activity (any disks activity is considered). +* `usbport`: Blinks with activity on any USB port. +* `netdev-NETDEV`: Blink with activity on specific network device where `NETDEV` + is the name of that device. +* `mmcX`: Flashes when writing or reading to and from specific MMC device where + `X` is index of that device. +* `phyX`: Blinks with activity on wireless interface where `X`is index of that + interface. +* `ataX`: Blink when writing or reading to and from specific ATA device where + `X` is index of that device. +* `netfilter-ID`: Flashes when firewall rule with specified `ID` is triggered. + +The full list of available activities is available by invoking: +`rainbow all activity -l`. + +### Animate + +This allows specification of animation pattern. The simplest variant is periodic +enable and disable of LED but more complex patterns can be specified as well. + +The animation is specified as set of color and time in nanoseconds pairs. The +color changes from one color to the next one in duration of specified time. + +In configuration animation is specified as a list of those color and time in +nanoseconds pairs. For example the following UCI list gradually dims and +light ups green color on power led: +`rainbow.all.animation='green 1000' 'black 1000'` + + +## Hardware integration + +This section describes how to implement hardware integration for rainbow and +what that actually means. This is here as developer documentation not expected +by end-users to read this. + +The integration has to be done by implementing shell file `backend.sh` and +installed in the same directory as the rest of the rainbow. This directory in +repository contains also a commented example backend file you can use to +understand what you have to implement to get Rainbow working on a new platform. + +### Button sync + +Some of the boards have hardware button that allows limited LED brightness +control. We have to sync the setting to configuration so the usage of button is +not wiped after reboot or any rainbow call. This is provided by +`rainbow-button-sync` service and shell script `button_sync.sh`. The +`button_sync.sh` uses the same backend file as `rainbow.sh` and needs only one +additional function to be present there `get_brightness`. The configuration is +saved to the UCI config `rainbow`. + +### Animator + +The animations are designed to work well with Kernel LED pattern trigger. The +issue is that not every board has kernel driver and not only that but also not +every kernel driver allows us to control color using the trigger. To allow +animations even on such platforms we have `rainbow-animator` service that runs +`animator.py`. To make this works you have to implement `animator_backend.py` +which is simplified backend implemented in Python. You also have to pause +animator in `preapply` function in `backend.sh` with +`/etc/init.d/rainbow-animator pause` and reload it in `postapply` function with +`/etc/init.d/rainbow-animator reload`. The example animator backend file with +commends is provided in this repository. + +The animator can be configured to limit number of updates which is highly +desirable to not load CPU with LED animations. In default number of updates in +seconds is set to 15. You can use UCI `rainbow.animation.ups` to set different +value if you want tweak it. + diff --git a/hardware/rainbow/files/animation.sh b/hardware/rainbow/files/animation.sh new file mode 100644 index 000000000..f23b1f46f --- /dev/null +++ b/hardware/rainbow/files/animation.sh @@ -0,0 +1,79 @@ +loadsrc color + +# Initialize animation variables +# Yes yes this is not nice as it uses global variables but iterative addition is +# the easies API considering the usage in uci.sh that I could come up with. +animation_init() { + animation="" + animation_color_r="?" + animation_color_g="?" + animation_color_b="?" +} + +animation_add() { + local color="$1" + local time="$2" + local color_r="$animation_color_r" + local color_g="$animation_color_g" + local color_b="$animation_color_b" + + parse_color "$1" inherit || return + echo "$time" | grep -qE "^[0-9]+$" || return + + animation="${animation}${animation:+ }$color_r $color_g $color_b $time" + animation_color_r="$color_r" + animation_color_g="$color_g" + animation_color_b="$color_b" +} + +animation_finish() { + if [ -n "$animation" ]; then + # The magic in the awk is that question mark is used only for values + # from initial steps and never after if there was some other value. This + # means that we can replace any occurrences of it by values from last + # state. The exception is when value was never specified, then the + # default that is inheritance is used. + animation="$(echo "$animation" | awk -F'\t' '{ + OFS="\t" + for (i = 0; i < 4; i++) { + c[i] = $(NF - 3 + i) + if (c[i] == "?") + c[i] = "-" + } + for (i = 1; i <= NF; i++) + if ($i == "?") + $i = c[(i - 1) % 4] + print + }')" + fi +} + +# Load animation sequence and format it to internal representation. +# Arguments are expected to be pairs of color and time. The result is saved to +# the animation variable. +# The number of consumed arguments is signaled by setting SHIFTARGS to the $#. +parse_animation() { + # Explanation about magic here.. + # The animation is sequence of values that repeats for inifinity. This means + # that color in next step modifies the previous one and the first one + # modifies the last one. As an example let's take sequence: + # R255 100 R0 100 G255 100 G0 100 + # In this sequence the initial two states do not specify green color but + # from sequence it is clear that it should be zero. The same applies for the + # red color in last two states. + # To assemble complete sequence we have to always modify previously + # specified value. This is achieved simply by updating previous color. The + # issue is that for initial steps the previous values are the last ones so + # we have to read all steps before we can propagate them back. + # The solution here is to use temporally value for any value we are not sure + # about and fill it later on when we read all arguments. + local animation_color_r animation_color_g animation_color_b + animation_init + # We always parse two arguments at once (color and time) + while [ $# -ge 2 ]; do + animation_add "$1" "$2" || break + shift 2 + done + animation_finish + SHIFTARGS=$# +} diff --git a/hardware/rainbow/files/animation.test.sh b/hardware/rainbow/files/animation.test.sh new file mode 100755 index 000000000..6d6053ca1 --- /dev/null +++ b/hardware/rainbow/files/animation.test.sh @@ -0,0 +1,37 @@ +#!/bin/ash +set -eu +loadsrc() { + . "${0%/*}/$1.sh" +} +loadsrc animation + +fail() { + echo "$@" >&2 + exit 1 +} + +check() { + local seq="$1" + local residue="$2" + shift 2 + parse_animation "$@" || fail "Parse failed for: $*" + [ "$animation" = "$seq" ] || fail "Invalid result '$animation' != '$seq' for: $*" + [ "$SHIFTARGS" = "$residue" ] || fail "Invalid number of residue for: $*" +} + + +check "" 0 +check \ + "0 0 0 1000 255 0 0 1000" \ + 0 \ + black 1000 red 1000 +check \ + "255 0 - 1000 0 0 - 1000 0 255 - 1000 0 0 - 1000" \ + 0 \ + R255 1000 R0 1000 G255 1000 G0 1000 + +check "" 2 something else +check \ + "0 0 0 1000 255 0 0 1000" \ + 2 \ + black 1000 red 1000 something else diff --git a/hardware/rainbow/files/animator.py b/hardware/rainbow/files/animator.py new file mode 100755 index 000000000..85862d62c --- /dev/null +++ b/hardware/rainbow/files/animator.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +import argparse +import fcntl +import importlib.util +import math +import os +import pathlib +import signal +import sys +import time +import typing + +DEFAULT_UPS = 15 # Configures how many updates we try to reach per second + +COLORS = ("r", "g", "b") + +# Load backend +backend_spec = importlib.util.spec_from_file_location("backend", pathlib.Path(__file__).parent / "animator_backend.py") +assert backend_spec +backend_module = importlib.util.module_from_spec(backend_spec) +backend_spec.loader.exec_module(backend_module) +backend = backend_module.Backend() + +rainbowdir = pathlib.Path("/var/run/rainbow") + +is_paused = False +configuration: dict[int, list[dict[str, int]]] = {} +state: dict[int, dict] = {} + + +def report(*args, **kwargs): + """The replacement for simple print that immediatelly flushes output. The issue here is with pipe setup in procd + that does not buffer simply to the end of line. The effect is that simple print is not immediatelly displayed in + logs without this flush. + """ + print(*args, **kwargs) + sys.stdout.flush() + + +def pause(): + """Pause animation. To resume the reload has to be called.""" + global is_paused + is_paused = True + report("Paused") + + +def _field2value(field: str) -> typing.Optional[int]: + """Converts field to integer or None in case of '-'.""" + if field == "-": + return None + return int(field) + + +def _base_color(base_color, i, color, new: str) -> typing.Optional[int]: + new = _field2value(new) + if new is None: + if i in base_color: + return base_color[i][color] + return new + + +def reload(): + """Load latest rainbow configuration and resume animation.""" + report("Reloading...") + configuration.clear() + try: + dirfd = os.open(rainbowdir, os.O_RDONLY | os.O_DIRECTORY) + except FileNotFoundError: + return # No configuration so just simply skip loading + fcntl.flock(dirfd, fcntl.LOCK_EX) + + base_color: dict[int, dict[str, typing.Optional[int]]] = {} + files = [pth for pth in rainbowdir.iterdir() if pth.is_file and not pth.name.startswith(".")] + files.sort() + for filepath in files: + with filepath.open() as file: + i = 0 + for line in file: + fields = line.rstrip().split("\t") + base_color[i] = {COLORS[y]: _base_color(base_color, i, COLORS[y], fields[y]) for y in range(3)} + if fields[3] == "animate" and backend.handled(i): + frames = [] + offset = 4 + while len(fields) - offset >= 4: + frame = {COLORS[y]: _field2value(fields[offset + y]) for y in range(3)} + frame["ns"] = int(fields[offset + 3]) * 1000000 + frames.append(frame) + offset += 4 + if frames: + configuration[i] = frames + elif i in configuration and fields[3] != "-": + # Some different configuration so make sure that we ignore the previous one + del configuration[i] + i += 1 + # Note: We have to collect base color from all levels first before we assign it as some higher level might change it + # without updating mode. + for ledid, points in configuration.items(): # Fill in base color + for point in points: + for color in COLORS: + point[color] = (base_color[ledid][color] or 0) if point[color] is None else point[color] + + state_keys = set(state.keys()) + configuration_keys = set(configuration.keys()) + for ledid in state_keys - configuration_keys: # Remove old states + del state[ledid] + for ledid in configuration_keys - state_keys: # Initialize new states + state[ledid] = { + "index": 0, + "start": time.monotonic_ns(), + } + + os.close(dirfd) # unlocks directory + global is_paused + is_paused = False + + +def _interpolate(a, b, point): + return int(a + ((b - a) * point)) + + +def interpolate(a, b, timeoff): + """Interpolates two colors to the given point between them for given time offset.""" + point = timeoff / a["ns"] + red = _interpolate(a["r"], b["r"], point) + green = _interpolate(a["g"], b["g"], point) + blue = _interpolate(a["b"], b["b"], point) + return red, green, blue + + +def update(): + """Animation function.""" + for ledid, points in configuration.items(): + curi = state[ledid]["index"] + nexti = curi + 1 + if len(points) <= nexti: + nexti = 0 + timeoff = time.monotonic_ns() - state[ledid]["start"] + while timeoff > points[curi]["ns"]: + timeoff -= points[curi]["ns"] + curi = nexti + nexti += 1 + if len(points) <= nexti: + nexti = 0 + red, green, blue = interpolate(points[curi], points[nexti], timeoff) + backend.update(ledid, red, green, blue) + backend.apply() + + +def parse_arguments(): + """Parse script arguments""" + parser = argparse.ArgumentParser( + description="Rainbow Animator. Helper for platforms that do not correctly support pattern trigger on LEDs." + ) + parser.add_argument("--ups", "-u", default=15, help="Set number of updates per-second.") + return parser.parse_args() + + +def main(): + args = parse_arguments() + reload() # Load the first configuration + signal.signal(signal.SIGUSR1, lambda s, f: pause()) + signal.signal(signal.SIGUSR2, lambda s, f: reload()) + nano = math.pow(10, 9) + frame = nano / args.ups + while True: + if not is_paused and configuration: + stime = time.monotonic_ns() + signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGUSR1, signal.SIGUSR2]) + update() + signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGUSR1, signal.SIGUSR2]) + rtime = time.monotonic_ns() - stime + if rtime < frame: + time.sleep((frame - rtime) / nano) # sleep for rest of the frame + else: + sig = signal.sigwait([signal.SIGUSR1, signal.SIGUSR2]) + if sig == signal.SIGUSR1: + pause() + elif sig == signal.SIGUSR2: + reload() + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/hardware/rainbow/files/animator_backend.example.py b/hardware/rainbow/files/animator_backend.example.py new file mode 100644 index 000000000..99a930a1b --- /dev/null +++ b/hardware/rainbow/files/animator_backend.example.py @@ -0,0 +1,14 @@ +class Backend: + """Handler for all leds we can control.""" + + def update(self, ledid: int, red: int, green: int, blue: int) -> None: + """Update color of led on given index.""" + # TODO + + def apply(self) -> None: + """Apply previous LEDs state updates if that is required.""" + # TODO + + def handled(self, ledid: int) -> bool: + """Informs animator if led animation for led on given index should be handled by it.""" + return False # TODO diff --git a/hardware/rainbow/files/backend.example.sh b/hardware/rainbow/files/backend.example.sh new file mode 100644 index 000000000..499fb38ce --- /dev/null +++ b/hardware/rainbow/files/backend.example.sh @@ -0,0 +1,82 @@ +# Backend for generic rainbow script EXAMPLE + +# This variable has to be always defined with full list of LED names. It is +# prefered to use self-describing but short names for those LEDs. It is prefered +# that those names match with labeling on the box. +LEDS="pwr lan0 lan1 lan2 wan" + +# This function is required only if leds are kontrolled by kernel driver and +# kernel managed triggers are available. When this function is defined and +# successfully provides path to sysfs for specified LED then it unlocks +# activity triggers for it. +led2sysfs() { + local led="$1" # one of leds from LEDS variable + false +} + +# Allows modification of defaults for specific leds (it is called with 'all' as +# well). +# It is called in environment where following variables are defined and +# available for modification: +# color_r: red color +# color_g: green color +# color_b: blue color +# mode: rainbow mode for the LED +# mode_args: additional arguments for the mode +led_defaults() { + local led="$1" + false +} + +# This function is optional and is required only for button_sync.sh. +# It is really needed only if there is a hardware way to modify brightness. +# It is expected to echo the current brightness level as integer between 0 and +# 255. +get_brightness() { + false +} + +# The optional function that is called before application of state using +# set_led. This should be used to prepare environment as well as to pause +# animator for example. +preapply() { + false +} + +# This function has to be defined in every backend. It is a core component of +# rainbow functionality. This applies configuration to one specific LED. +# It gets LED name, color as RGB components, mode and mode's arguments. +set_led() { + local led="$1" r="$2" g="$3" b="$4" mode="$5" + shift 5 + false +} + +# The optional function that is called after set_led was called for every led +# and thus state of them is set. This should be used to do cleanup and to resume +# animator for example. +postapply() { + false +} + + +# Perform boot sequence specific for this device. +# This function is only optional and does not have to be defined if there is no +# boot sequence. +boot_sequence() { + false + # Note: The current state is always applied in the end in when this function + # is called so we don't have to restore previous state here. +} + + +# Parse operations that were previously available with rainbow. +# This adds option to correctly replace previous rainbow command without being +# stuck with its argument parsing. +# This is for backward compatibility and you want to remove this function for +# new platforms. +compatibility() { + local operation="$1"; shift # Operation user specified and we try to parse here + false + SHIFTARGS=$# # This is required to inform called how many arguments we consumed. +} diff --git a/hardware/rainbow/files/brightness.sh b/hardware/rainbow/files/brightness.sh new file mode 100644 index 000000000..edc6c6870 --- /dev/null +++ b/hardware/rainbow/files/brightness.sh @@ -0,0 +1,73 @@ +loadsrc uci + +brightness_usage() { + echo "Usage: $0 brightness [OPTION].. " >&2 +} +brightness_help() { + brightness_usage + # Note: We use 0-8 as that is the number of levels Omnia can specify using + # button. The Turris 1.x has only 7 levels but we can use 7 and 8 as the + # same. The Mox has no levels thus we are free to choose what ever we want. + cat >&2 <<-EOF + Set maximum brightness (the global modifier for all controlled leds). + The brightness has to be specified as number between 0 and 8 (or 255 + if -p is used). + + Options: + -q Query for current setting instead of setting brightness + -p Higher precission for brightness + -h Print this help text and exit + EOF +} + +op_brightness() { + local query="f" + local precise="n" + while getopts "qh" opt; do + case "$opt" in + q) + query="y" + ;; + p) + precise="y" + ;; + h) + brightness_help + exit 0 + ;; + *) + brightness_usage + exit 2 + ;; + esac + done + if [ "$query" = "y" ]; then + brightness fetch + return $# + fi + + shift $((OPTIND - 1)) + [ $# -gt 0 ] || { + brightness_usage + exit 2 + } + brightness="$1" + if [ "$precise" = "y" ]; then + if [ "$brightness" -lt 0 ] || [ "$brightness" -gt 255 ]; then + echo "The value has to be a number from 0 to 255!" >&2 + brightness_usage + exit 2 + fi + else + if [ "$brightness" -lt 0 ] || [ "$brightness" -gt 8 ]; then + echo "The value has to be a number from 0 to 8!" >&2 + brightness_usage + exit 2 + fi + brightness=$((brightness * 32)) + fi + update_brightness "$brightness" + apply_needed="y" + + SHIFTARGS=$# +} diff --git a/hardware/rainbow/files/button_sync.sh b/hardware/rainbow/files/button_sync.sh new file mode 100755 index 000000000..b9684e5a4 --- /dev/null +++ b/hardware/rainbow/files/button_sync.sh @@ -0,0 +1,22 @@ +#!/bin/sh +set -eu +. "$(dirname "$(readlink -f "$0")")/utils.sh" + +loadsrc backend +loadsrc state + +type get_brightness >/dev/null \ + || fail "Button sync is not supported on this board!" + + +trap 'exit 0' INT QUIT TERM +while true; do + # TODO we might race here with rainbow configuration (uci changes but change + # is not yet propagated). It is very unlikelly but probably possible. + current_brightness="$(get_brightness)" + if [ "$(brightness fetch)" != "$current_brightness" ]; then + echo "Brightness update using button to: $current_brightness" + update_brightness "$current_brightness" + fi + sleep 2 +done diff --git a/hardware/rainbow/files/color.sh b/hardware/rainbow/files/color.sh new file mode 100644 index 000000000..5bc7ece3a --- /dev/null +++ b/hardware/rainbow/files/color.sh @@ -0,0 +1,115 @@ +# Canonize color to RGB 0-255 format. +# Sets three variables: color_r, color_g, color_b +parse_color() { + local r="" g="" b="" + _parse_color "$@" || return + [ -z "$r" ] || color_r="$r" + [ -z "$g" ] || color_g="$g" + [ -z "$b" ] || color_b="$b" +} + +_parse_value() { + local value="$1" + local mode="$2" + [ "${value#x}" == "$value" ] \ + || value="$((16#${value#x}))" + + if [ "$mode" = "inherit" ] && [ "$value" = "-" ]; then + true + elif [ -n "$value" ]; then + echo "$value" | grep -qE "^[0-9]*$" || return + [ "$value" -ge 0 ] || return + [ "$value" -le 255 ] || return + fi + + echo "$value" +} + +_parse_color() { + local value="$1" + local mode="${2:-}" + + if [ -z "$value" ]; then + return # Empty value means no modification + fi + + case "$value" in + -) + if [ "$mode" = "inherit" ]; then + r="-" g="-" b="-" + return + fi + ;; + red) + r=255 g=0 b=0 + return + ;; + green) + r=0 g=255 b=0 + return + ;; + blue) + r=0 g=0 b=255 + return + ;; + yellow) + r=255 g=255 b=0 + return + ;; + cyan) + r=0 g=255 b=255 + return + ;; + magenta) + r=255 g=0 b=255 + return + ;; + white) + r=255 g=255 b=255 + return + ;; + black) + r=0 g=0 b=0 + return + ;; + esac + + if echo "$value" | grep -qE "^[0-9-]*,[0-9-]*,[0-9-]*$"; then + matched="y" + r="$(_parse_value "${value%%,*}" "$mode")" || return + local inter="${value%,*}" + g="$(_parse_value "${inter#*,}" "$mode")" || return + b="$(_parse_value "${value##*,}" "$mode")" || return + return + fi + + if echo "$value" | grep -qE "^[RGB]([0-9]+|x[0-9a-fA-F]+)(,[RGB]([0-9]+|x[0-9a-fA-F]+))*$"; then + matched="y" + while read -r spec; do + case "$spec" in + R*) + r="$(_parse_value "${spec#R}" "$mode")" || return + ;; + G*) + g="$(_parse_value "${spec#G}" "$mode")" || return + ;; + B*) + b="$(_parse_value "${spec#B}" "$mode")" || return + ;; + esac + done <<-EOF + $(echo "$value" | tr ',' '\n') + EOF + return + fi + + # Hex format for backward compatibility + if echo "$value" | grep -qE "^[0-9a-fA-F]{6}$"; then + r="$(_parse_value "x${1:0:2}" "")" || return + g="$(_parse_value "x${1:2:2}" "")" || return + b="$(_parse_value "x${1:4:2}" "")" || return + return + fi + + return 1 +} diff --git a/hardware/rainbow/files/color.test.sh b/hardware/rainbow/files/color.test.sh new file mode 100755 index 000000000..cb3de14d1 --- /dev/null +++ b/hardware/rainbow/files/color.test.sh @@ -0,0 +1,75 @@ +#!/bin/ash +set -eu +. "${0%/*}/color.sh" + +fail() { + echo "$@" >&2 + exit 1 +} + +check() { + local repre="$1" + local r="$2" g="$3" b="$4" mode="${5:-}" + local color_r="?" color_g="?" color_b="?" + parse_color "$repre" "$mode" || fail "Parse failed for: $repre" + [ "$color_r" = "$r" ] || fail "Red color not matches $color_r != $r for: $repre" + [ "$color_g" = "$g" ] || fail "Green color not matches $color_g != $g for: $repre" + [ "$color_b" = "$b" ] || fail "Blue color not matches $color_b != $b for: $repre" +} + +check_fail() { + local repre="$1" mode="${2:-}" + local color_r="?" color_g="?" color_b="?" + ! parse_color "$repre" "$mode" || fail "Parse did not fail for: $repre" + [ "$color_r" = "?" ] || fail "Red color modified" + [ "$color_g" = "?" ] || fail "Green color modified" + [ "$color_b" = "?" ] || fail "Blue color modified" +} + +check "0,0,0" 0 0 0 +check "255,0,255" 255 0 255 +check "42,0,1" 42 0 1 +check "0,0,255" 0 0 255 +check "255,0,0" 255 0 0 +check ",0,0" "?" 0 0 +check "128,," 128 '?' '?' +check ",," '?' '?' '?' +check_fail "532,0,0" +check_fail "0,342,123" +check_fail "42,255,256" + +check "R0,G0,B0" 0 0 0 +check "R255,B255" 255 '?' 255 +check "R42,B1" 42 '?' 1 +check "B255" '?' '?' 255 +check "G255" '?' 255 '?' +check "" '?' '?' '?' + +check "Rx0,Gx0,Bx0" 0 0 0 +check "RxFF,BxFF" 255 '?' 255 +check "Rx2A,Bx1" 42 '?' 1 +check "Bxff" '?' '?' 255 +check "GxFf" '?' 255 '?' + +check "red" 255 0 0 +check "green" 0 255 0 +check "blue" 0 0 255 +check "yellow" 255 255 0 +check "cyan" 0 255 255 +check "magenta" 255 0 255 +check "white" 255 255 255 +check "black" 0 0 0 + +check "-" "-" "-" "-" inherit +check "-,255,-" "-" "255" "-" inherit +check "123,-,-" "123" "-" "-" inherit + +check_fail "ABCDF" +check_fail "ABCDF01" + + +check "000000" 0 0 0 +check "FF00FF" 255 0 255 +check "2A0001" 42 0 1 +check "0000ff" 0 0 255 +check "fF0000" 255 0 0 diff --git a/hardware/rainbow/files/compat.sh b/hardware/rainbow/files/compat.sh new file mode 100644 index 000000000..22fc9801a --- /dev/null +++ b/hardware/rainbow/files/compat.sh @@ -0,0 +1,64 @@ +# These are common arguments that were previously provided by rainbow but are +# now obsolete. They are implemented separately so backend can choose which it +# has to support (depending on rainbow introduction to the platform). + +compat_binmask() { + if [ $# -eq 0 ]; then + echo "Invalid binmask usage. Missing number." + exit 2 + fi + + _compat_binmask_modify() { + [ "$1" != "$led" ] \ + || mode="$2" + } + + local binmask="$1" + shift + [ "${binmask#0x}" == "$binmask" ] \ + || binmask="$((16#${binmask#0x}))" + + local mask=1 + for led in $LEDS; do + mask=$((mask << 1)) + done + + for led in $LEDS; do + mask=$((mask >> 1)) + local mode="disable" + [ "$((binmask & mask))" -eq 0 ] \ + || mode="enable" + call_for_leds_config_level "$priority" "$slot" _compat_binmask_modify \ + "$led" "$mode" + done + apply_needed="y" + SHIFTARGS=$# +} + + +compat_intensity() { + local max="$1"; shift + if [ $# -gt 0 ]; then + loadsrc uci + update_brightness "$(($1 * 255 / max))" + apply_needed="y" + shift + else + echo "Invalid intensity usage. Missing intensity value." >&2 + exit 2 + fi + SHIFTARGS=$# +} + + +compat_get() { + local max="$1"; shift + if [ "${1:-}" = "intensity" ]; then + echo "$(($2 * max / 255))" + shift 2 + else + echo "Invalid get usage. Missing 'intensity' argument." >&2 + exit 2 + fi + SHIFTARGS=$# +} diff --git a/hardware/rainbow/files/led.sh b/hardware/rainbow/files/led.sh new file mode 100644 index 000000000..5dacccdb5 --- /dev/null +++ b/hardware/rainbow/files/led.sh @@ -0,0 +1,154 @@ +loadsrc color +loadsrc ledid + +led_usage() { + echo "Usage: $0 $1 [OPTION].. [COLOR] [MODE]" >&2 +} +led_help() { + led_usage "$1" + cat >&2 <<-EOF + Configure '$1'. When no color or mode is given it prints current + configuration. The output is in tab separated columns with RGB color in + initial three columns, followed by mode and its arguments. + + Color: + Color can be specified by few different ways. You can simply use + specific color name (although supported set is small). + Another option is to specify RGB compounds. They are integer between 0 + and 255. They can be specified either in decimal format or in + hexadecimal if you prefix it with x (thus xFF is 255). Compounds are + expected to be joined by commad (such as 255,0,0 for red). You can + also specify compounds out of order by prefixing them with leter R for + red, G for green and B for blue (such as G0,B0,R255 for red). Not + every color compound has to be defined. They are filled with previous + value if you left some out (such as ,255, or G255) or with default + '-'. The '-' is the special value that specifies inheritence from + lower priority configuration. You can use it as replacement for any + color compound or allone to set inheritence. + Mode: + auto + Operate led status as designed (that is for example ethernet activity for lan leds) + disable + Disable led (keep it off) + enable + Enable led (keep it on) + EOF + loadsrc led_activity + if led_activities_supported "$1"; then + cat >&2 <<-EOF + activity + Blink with specific device activity (use -h right after for more info) + EOF + fi + cat >&2 <<-EOF + animate + Perform specific animation (use -h right after for more info) + inherit + Inherit status from lower priority level + ignore + Rainbow should ignore this led all together + Options: + -l Show configuration only for the current priority and slot + -h Print this help text and exit + EOF +} + +op_led() { + local id="$1"; shift + local localconf="n" + while getopts "lh" opt; do + case "$opt" in + l) + localconf="y" + ;; + h) + led_help "$id" + exit 0 + ;; + *) + led_usage "$id" + exit 2 + ;; + esac + done + shift $((OPTIND - 1)) + + local mode="" mode_args="" + local color_r="" color_g="" color_b="" + while [ $# -gt 0 ]; do + if [ -z "$color_r" ] && [ -z "$color_g" ] && [ -z "$color_b" ]; then + if parse_color "$1" inherit; then + shift + continue + fi + fi + if [ -z "$mode" ]; then + case "$1" in + auto|disable|enable|ignore) + mode="$1" + shift + continue + ;; + activity) + loadsrc led_activity + if led_activities_supported "$id"; then + mode="$1" + shift + loadsrc led_activity + op_led_activity "$id" "$@" + shift $(($# - SHIFTARGS)) + continue + fi + ;; + animate) + mode="$1" + shift + loadsrc led_animate + op_led_animate "$id" "$@" + shift $(($# - SHIFTARGS)) + continue + ;; + inherit) + mode="-" + ;; + esac + fi + break + done + SHIFTARGS=$# + + id="$(canonled "$id")" + + config() { + if [ "$id" = "all" ] || [ "$led" = "$id" ]; then + printf "%s\t%s\t%s\t%s\n" "$color_r" "$color_g" "$color_b" \ + "$mode${mode_args:+ }$mode_args" >&2 + fi + } + if [ -z "$mode" ] && [ -z "$color_r" ] && [ -z "$color_g" ] && [ -z "$color_b" ]; then + if [ "$localconf" = "y" ]; then + call_for_leds_config_level "$priority" "$slot" config + else + loadsrc uci + base_config + call_for_leds_config config + fi + return + fi + + modify() { + if [ "$id" = "all" ] || [ "$led" = "$id" ]; then + [ -z "$1" ] || color_r="$1" + [ -z "$2" ] || color_g="$2" + [ -z "$3" ] || color_b="$3" + [ -z "$4" ] || { + mode="$4" + mode_args="$5" + } + fi + } + call_for_leds_config_level "$priority" "$slot" modify \ + "$color_r" "$color_g" "$color_b" "$mode" "$mode_args" + + apply_needed="y" +} diff --git a/hardware/rainbow/files/led_activity.sh b/hardware/rainbow/files/led_activity.sh new file mode 100644 index 000000000..a0354ebcd --- /dev/null +++ b/hardware/rainbow/files/led_activity.sh @@ -0,0 +1,173 @@ +led_activity_usage() { + echo "Usage: $0 $1 activity [OPTION].. [TRIGGER]" >&2 +} +led_activity_help() { + led_usage "$1" + cat >&2 <<-EOF + Led mode '$1' that blinks with specific device activity. + + Options: + -l List available activity triggers for this led and exit + -h Print this help text and exit + EOF +} + +op_led_activity() { + local id="$1"; shift + local list="n" + while getopts "t:lih" opt; do + case "$opt" in + l) + list="y" + ;; + h) + led_activity_help "$id" + exit 0 + ;; + *) + led_activity_usage "$id" + exit 2 + ;; + esac + done + shift $((OPTIND - 1)) + + if [ "$list" = "y" ]; then + all_supported_activities "$id" + exit 0 + fi + + # Note: we do not support every single activity trigger. For example we only + # support trigger that do both TX and RX not the separate ones. + # Warning: this sets mode_args as this is expected to be called only from + # op_led function! + mode_args="" + case "${1:-}" in + "") + mode_args="activity" + shift || true + ;; + activity|activity-inverted|heartbeat|heatbeat-inverted|disk-activity|usbport) + mode_args="$1" + shift + ;; + activity-inverted|heartbeat-inverted) + ;; + netdev-*) + # We do not intentionally verify that device exists as this way the + # hotplug devices can be configured as well. + mode_args="netdev\t${1#netdev-}" + shift + ;; + mmc*|phy*|ata*|netfilter-*) + [ -n "$(supported_activities "$id" -xF "$1")" ] || { + echo "The requested activity is invalid: $1" >&2 + usage + exit 2 + } + mode_args="$1" + shift + ;; + esac + SHIFTARGS=$# +} + +# Provide supported activities that are filtered using grep +supported_activities() { + local led + led="$(canonled "$1")"; shift + if [ "$led" = "all" ]; then + for led in $LEDS; do + supported_activities "$led" "$@" \ + | sort -u + done + else + tr ' ' '\n' <"$(led2sysfs "$led")/trigger" \ + | grep "$@" || true + fi +} + +# Lists all supported activities for given led. +all_supported_activities() { + local led + led="$(canonled "$1")" + supported_activities "$led" -xF "activity" \ + && echo "activity-inverted" + supported_activities "$led" -xF "heartbeat" \ + && echo "heartbeat-inverted" + supported_activities "$led" \ + -E "^(disk-activity|usbport|(mmc|ata)[0-9]+|netfilter-.*)$" + supported_activities "$led" -E "^phy[0-9]+tpt$" | sed 's/tpt$//' + if [ -n "$(supported_activities "$led" -E "netdev")" ]; then + for netdev in /sys/class/net/*; do + [ -e "$netdev" ] || continue + echo "netdev-${netdev##*/}" + done + fi +} + +# Check if activities are even supported for given led. +led_activities_supported() { + type led2sysfs >/dev/null || return + local led="$1" + # The activities are supported only if we have kernel driver and that + if [ "$led" = "all" ]; then + for led in $LEDS; do + # We report that at least one led supports it here + led_activities_supported "$led" && return + done + else + led="$(canonled "$led")" + led2sysfs "$led" >/dev/null \ + && [ -n "$(all_supported_activities "$led")" ] + fi +} + + +# This function applies activity trigger to led. This is kind of non-standard as +# this is called from backend.sh (from set_led) but this is same for any +# activity capable led. +apply_activity() { + local led="$1"; shift + local trigger="${1:-}" + local sysfs + sysfs="$(led2sysfs "$led")" + case "$trigger" in + activity|activity-inverted|heartbeat|heatbeat-inverted) + local systrigger="${trigger%-inverted}" + echo "$systrigger" >"$sysfs/trigger" + grep -qF "[$systrigger]" "$sysfs/trigger" + if [ "${trigger%-inverted}" == "$trigger" ]; then + echo "0" >"$sysfs/invert" + else + echo "1" >"$sysfs/invert" + fi + ;; + disk-activity|mmc*|ata*|netfilter-*) + echo "$trigger" >"$sysfs/trigger" + grep -qF "[$trigger]" "$sysfs/trigger" + ;; + usbport) + echo "$trigger" >"$sysfs/trigger" + grep -qF "[$trigger]" "$sysfs/trigger" || return + for port in "$sysfs"/ports/*; do + echo 1 > "$port" + done + ;; + phy*) + echo "${trigger}tpt" >"$sysfs/trigger" + grep -qF "[${trigger}tpt]" "$sysfs/trigger" + ;; + netdev-*) + echo "netdev" >"$sysfs/trigger" + grep -qF '[netdev]' "$sysfs/trigger" || return + echo "${trigger#netdev-}" >"$sysfs/device_name" + echo 1 >"$sysfs/link" + echo 1 >"$sysfs/rx" + echo 1 >"$sysfs/tx" + ;; + *) + internal_error "Unsupported activity arguments:" "$trigger" + ;; + esac +} diff --git a/hardware/rainbow/files/led_animate.sh b/hardware/rainbow/files/led_animate.sh new file mode 100644 index 000000000..323c28f93 --- /dev/null +++ b/hardware/rainbow/files/led_animate.sh @@ -0,0 +1,42 @@ +loadsrc animation + +led_animate_usage() { + echo "Usage: $0 $1 animate [OPTION].. [COLOR TIME].." >&2 +} +led_animate_help() { + led_usage "$1" + cat >&2 <<-EOF + Led mode for '$1' that autonomously changes color based on pattern. + The pattern is combination of color and time. The time is in + miliseconds. The color is of the same format as when led color is being + specified. This includes the inheritence which refers to the color of + led outside of the animation. + + Options: + -h Print this help text and exit + EOF +} + +op_led_animate() { + local id="$1"; shift + while getopts "h" opt; do + case "$opt" in + h) + led_animate_help "$id" + exit 0 + ;; + *) + led_animate_usage "$id" + exit 2 + ;; + esac + done + shift $((OPTIND - 1)) + + local animation + parse_animation "$@" + shift $(($# - SHIFTARGS)) + mode_args="$animation" + + SHIFTARGS=$# +} diff --git a/hardware/rainbow/files/ledid.sh b/hardware/rainbow/files/ledid.sh new file mode 100644 index 000000000..fc649c47b --- /dev/null +++ b/hardware/rainbow/files/ledid.sh @@ -0,0 +1,38 @@ +# Check if provided led identifier is valid. +# This covers not only those fron LEDS but also ledX where X is number and all. +is_valid_led() { + local check_led="$1" + [ "$check_led" = "all" ] \ + && return 0 + local i=0 + for led in $LEDS; do + if [ "$check_led" = "$led" ]; then + return 0 + fi + i=$((i + 1)) + done + if [ "${check_led#led}" != "$check_led" ] \ + && [ "${check_led#led}" -gt 0 ] && [ "${check_led#led}" -le "$i" ]; then + return 0 + fi + return 1 +} + + +# Convert any other representation (current only ledX) to thos from LEDS +# variable or 'all'. +canonled() { + local value="$1" + local index=1 + local led + for led in $LEDS; do + if [ "$value" = "$led" ] || { + [ "${value#led}" != "$value" ] && [ "${value#led}" -eq $index ] + }; then + echo "$led" + return + fi + index=$((index + 1)) + done + echo "$value" +} diff --git a/hardware/rainbow/files/ledid.test.sh b/hardware/rainbow/files/ledid.test.sh new file mode 100755 index 000000000..6f18848ae --- /dev/null +++ b/hardware/rainbow/files/ledid.test.sh @@ -0,0 +1,49 @@ +#!/bin/ash +set -eu +. "${0%/*}/ledid.sh" + +fail() { + echo "$@" >&2 + exit 1 +} + +LEDS="pwr lan0 lan1 pci1 usr1" + +valid() { + is_valid_led "$1" || fail "Led should be valid but it is not: $1" +} + +not_valid() { + ! is_valid_led "$1" || fail "Led should not be valid but it is: $1" +} + +for led in $LEDS; do + valid "$led" +done +not_valid "power" +not_valid "usr" +not_valid "p" +not_valid "1" + +for i in $(seq 5); do + valid "led$i" +done +not_valid "led6" +not_valid "led7" + + +canon() { + local value="$1" + local expected="$2" + local res + res="$(canonled "$value")" || fail "Led canon failed for: $value" + [ "$res" = "$expected" ] || fail "Led canon is not correct: $res != $expected" +} + +index=1 +for led in $LEDS; do + canon "$led" "$led" + canon "led$index" "$led" + index=$((index + 1)) +done +canon "foo" "foo" diff --git a/hardware/rainbow/files/pci.sh b/hardware/rainbow/files/pci.sh new file mode 100644 index 000000000..e5fb03b91 --- /dev/null +++ b/hardware/rainbow/files/pci.sh @@ -0,0 +1,28 @@ +loadsrc led_activity + +# In some cases we need to detect what is inserted in the specific PCI slot and +# setup activity trigger from system according to that. This function tries to +# do the detection produces activity configuration. +# It expects path to sysfs to the PCI device to investigate. +# This echoes activity argument for activity status. +# If no device was detected or if activity for it is not supported it signals +# failure with returned value. +pci_device_activity_detect() { + local syspci="$1" + local led="$1" + + if [ -d "$syspci/ieee80211" ]; then + [ -n "$(supported_activities "$led" -xF 'netdev')" ] || return + echo "netdev-$(ls -1 "$syspci/net" | head -1)" + return + fi + + for ata in "$syspci"/ata*; do + [ -d "$ata" ] || continue + [ -n "$(supported_activities "$led" -xF "$ata")" ] || continue + echo "${ata##*/}" + return + done + + return 1 +} diff --git a/hardware/rainbow/files/rainbow-animator.init b/hardware/rainbow/files/rainbow-animator.init new file mode 100755 index 000000000..d30759fcd --- /dev/null +++ b/hardware/rainbow/files/rainbow-animator.init @@ -0,0 +1,27 @@ +#!/bin/sh /etc/rc.common + +START=98 +STOP=01 + +USE_PROCD=1 + +reload_service() { + procd_send_signal "$(basename "$(readlink -f "$initscript")")" '*' SIGUSR2 +} + +extra_command "pause" "Pause animator loop" +pause() { + procd_send_signal "$(basename "$(readlink -f "$initscript")")" '*' SIGUSR1 +} + +start_service() { + config_load rainbow + config_get animation_ups "animation" "ups" "15" + + procd_open_instance rainbow + procd_set_param command /usr/libexec/rainbow/animator.py --ups "$animation_ups" + procd_set_param respawn 600 5 5 + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_close_instance +} diff --git a/hardware/rainbow/files/rainbow-button-sync.init b/hardware/rainbow/files/rainbow-button-sync.init new file mode 100755 index 000000000..ab5ba0f1f --- /dev/null +++ b/hardware/rainbow/files/rainbow-button-sync.init @@ -0,0 +1,15 @@ +#!/bin/sh /etc/rc.common + +START=98 +STOP=01 + +USE_PROCD=1 + +start_service() { + procd_open_instance + procd_set_param command /usr/libexec/rainbow/button_sync.sh + procd_set_param respawn 600 5 5 + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_close_instance +} diff --git a/hardware/rainbow/files/rainbow.init b/hardware/rainbow/files/rainbow.init new file mode 100755 index 000000000..356d49a0c --- /dev/null +++ b/hardware/rainbow/files/rainbow.init @@ -0,0 +1,22 @@ +#!/bin/sh /etc/rc.common + +# Start at the end, so the colors change after the boot is done. +START=99 +STOP=00 + +reload() { + rainbow reset -n +} + +restart() { + rainbow reset -a +} + +start() { + rainbow reset -a -b & +} + +stop() { + # Reset to the default, so a reboot can be recognized. + rainbow -p 100 -n shutdown all enable white +} diff --git a/hardware/rainbow/files/rainbow.sh b/hardware/rainbow/files/rainbow.sh new file mode 100755 index 000000000..d1f1dae7e --- /dev/null +++ b/hardware/rainbow/files/rainbow.sh @@ -0,0 +1,152 @@ +#!/bin/sh +set -eu + +rainbowdir="/var/run/rainbow" +mkdir -p "$rainbowdir" +[ "${RAINBOW_DIRECTORY_LOCKED-:}" = "y" ] \ + || RAINBOW_DIRECTORY_LOCKED="y" exec flock -Fx "$rainbowdir" "$0" "$@" + +. "$(dirname "$(readlink -f "$0")")/utils.sh" +loadsrc state +loadsrc ledid +loadsrc backend + +################################################################################ +usage() { + echo "Usage: $0 [OPTION].. ..." >&2 +} +help() { + usage + cat >&2 <<-EOF + Turris leds manipulation. + + Options: + -p INT Set priority to given led setting (in default 50 is used) + -n STR Slot name ('default' used if not specified) + -l List all available LEDs by name + -x Run in debug mode + -h Print this help text and exit + Operations: + reset Reset rainbow state (wipe any runtime changes) + brightness Set upper limit on brightness of all leds + all Configure all available leds at once + ledX Where X is number (up to 12 on Omnia and 8 on 1.x) + Configure specific led by name: + $LEDS + EOF +} + +priority=50 +slot="default" +list_leds="n" +while getopts "p:n:lxh" opt; do + case "$opt" in + p) + if [ "$OPTARG" -ge 0 ] && [ "$OPTARG" -le 100 ]; then + priority="$OPTARG" + else + echo "Priority is not in range from 0 to 100: $OPTARG" >&2 + usage + fi + ;; + n) + if [ "${OPTARG#*/}" != "$OPTARG" ]; then + echo "The slot name can't contain '/'" >&2 + usage + exit 2 + fi + slot="$OPTARG" + ;; + l) + list_leds="y" + ;; + x) + set -x + ;; + h) + help + exit 0 + ;; + *) + usage + exit 2 + ;; + esac +done +shift $((OPTIND - 1)) + +if [ "$list_leds" = "y" ]; then + for led in $LEDS; do + echo "$led" + done + exit 0 +fi + +[ $# -gt 0 ] || { + usage + exit 2 +} + +apply_needed="n" +while [ $# -gt 0 ]; do + operation="${1:-}" + shift + case "$operation" in + reset) + loadsrc reset + op_reset "$@" + shift $SHIFTARGS + ;; + brightness) + loadsrc brightness + op_brightness "$@" + shift $(($# - SHIFTARGS)) + ;; + "") + usage + exit 2 + ;; + *) + if is_valid_led "$operation"; then + loadsrc led + op_led "$operation" "$@" + shift $(($# - SHIFTARGS)) + else + SHIFTARGS=$# + type compatibility >/dev/null \ + && compatibility "$operation" "$@" + if [ "$SHIFTARGS" -lt $# ]; then + shift $(($# - SHIFTARGS)) + else + echo "Invalid operation: $operation" >&2 + usage + exit 2 + fi + fi + ;; + esac +done + +################################################################################ +# Helper function that is used in combination with parse_leds_file. +# This takes variables and passes them as an appropriate arguments as set_led +# functions expect it. +call_set_led() { + [ "$mode" != "ignore" ] || return 0 + shift $# # we do not care about our arguments + set "$@" "$led" "$color_r" "$color_g" "$color_b" "$mode" + while read -r arg; do + set "$@" "$arg" + done <<-EOF + $(echo "$mode_args" | tr '\t' '\n') + EOF + set_led "$@" +} + +loadsrc uci +if [ "$apply_needed" = "y" ]; then + base_config + ! type preapply >/dev/null || preapply + call_for_leds_config call_set_led + ! type postapply >/dev/null || postapply +fi diff --git a/hardware/rainbow/files/reset.sh b/hardware/rainbow/files/reset.sh new file mode 100644 index 000000000..5a6e90979 --- /dev/null +++ b/hardware/rainbow/files/reset.sh @@ -0,0 +1,58 @@ +reset_usage() { + echo "Usage: $0 reset [OPTION].." >&2 +} +reset_help() { + reset_usage + cat >&2 <<-EOF + Reset current runtime configuration. + + Options: + -n Do not remove current configuration only apply it + -a Reset all priority levels and slots not just the current one + -b Run boot sequence as part of reset + -h Print this help text and exit + EOF +} + +op_reset() { + local nowipe="n" + local all="n" + local bootseq="n" + while getopts "nabh" opt; do + case "$opt" in + n) + nowipe="y" + ;; + a) + all="y" + ;; + b) + bootseq="y" + ;; + h) + reset_help + exit 0 + ;; + *) + reset_usage + exit 2 + ;; + esac + done + SHIFTARGS=$((OPTIND - 1)) + + if [ "$nowipe" = "n" ]; then + if [ "$all" = "y" ]; then + rm -f "$rainbowdir"/* + else + rm -f "$rainbowdir/$(printf "%03d" "$priority")-$slot" + fi + fi + + # Note: the boot sequence is silently ignored if there is none defined + [ "$bootseq" = "y" ] \ + && type boot_sequence >/dev/null \ + && boot_sequence + + apply_needed="y" +} diff --git a/hardware/rainbow/files/state.sh b/hardware/rainbow/files/state.sh new file mode 100644 index 000000000..4fcba2379 --- /dev/null +++ b/hardware/rainbow/files/state.sh @@ -0,0 +1,76 @@ +# State directory helper functions + +# Parses stdin where it expects to get lines for leds. The provided function is +# called with provided arguments. +# The function provides variables: +# led: with led name (from LEDS list) +# color_r: 0-255 value of red color +# color_g: 0-255 value of green color +# color_b: 0-255 value of blue color +# mode: mode of led +# mode_args: Any argument passed after mode (which are mode arguments) +# It echoes the possibly updated state after function call (feel free to discart +# output if you do not update variables). +parse_leds_file() { + local func="$1"; shift + local led + for led in $LEDS; do + local color_r="-" color_g="-" color_b="-" mode="-" mode_args + read -r color_r color_g color_b mode mode_args + "$func" "$@" &2 + printf "%s\t%s\t%s\t%s" "$color_r" "$color_g" "$color_b" "$mode" + [ -n "$mode_args" ] \ + && printf "\t%s" "$mode_args" + printf "\n" + done +} + +# Call provided function with configuration for specified priority. +# The provided variables are the same as parse_leds_file and their modification +# is saved back to the file. +call_for_leds_config_level() { + local priority="$1" + local slot="$2" + local fnc="$3" + shift 3 + local file="$rainbowdir/$(printf "%03d" "$priority")-$slot" + { + if [ -f "$file" ]; then + cat "$file" + else + for led in $LEDS; do + printf -- "-\t-\t-\t-\n" + done + fi + } | parse_leds_file "$fnc" "$@" > "$file.new" + if grep -qv '^-\t-\t-\t-' "$file.new"; then + mv "$file.new" "$file" + else + # The configuration is empty so we no longer need it + rm -f "$file" "$file.new" + fi +} + +# Calls provided function with assembled configuration (combination of all +# configured levels). The provided variables are the same as parse_leds_file. +call_for_leds_config() { + local fnc="$1"; shift + awk -F'\t' ' + { + for (i=1; i<=NF; i++) + if (fields[FNR,i] == "" || $i != "-") + fields[FNR,i]=$i + } + END { + OFS="\t" + for (x = 1; x <= FNR; x++) { + $0="" + y = 1 + while ((x,y) in fields) + $y=fields[x,y++] + print + } + } + ' "$rainbowdir"/[0-9][0-9][0-9]-* \ + | parse_leds_file "$fnc" "$@" >/dev/null +} diff --git a/hardware/rainbow/files/state.test.sh b/hardware/rainbow/files/state.test.sh new file mode 100755 index 000000000..e8608e021 --- /dev/null +++ b/hardware/rainbow/files/state.test.sh @@ -0,0 +1,119 @@ +#!/bin/ash +set -eu +loadsrc() { + . "${0%/*}/$1.sh" +} +loadsrc state + +rainbowdir="$(mktemp -d)" +trap 'rm -rf "$rainbowdir"' EXIT + +LEDS="pwr lan0 lan1 pci1 usr1" + +fail() { + echo "$@" >&2 + exit 1 +} + +cmp_file() ( + local level="$1" + local slot="$2" + cat >"$rainbowdir/.cmpfile" + trap 'rm -f "$rainbowdir/.cmpfile"' EXIT + diff "$rainbowdir/.cmpfile" "$rainbowdir/$(printf "%03d" "$level")-$slot" \ + || fail "Files are different" +) + + +default() { + true +} +call_for_leds_config_level 50 "default" default +cmp_file 50 "default" <"$rainbowdir/000-base" + + config_load system + for led in $LEDS; do + color_r="-" color_g="-" color_b="-" mode="-" mode_args="" + sysfs="$(led2sysfs "$led")"; sysfs="${sysfs##*/}" + config_foreach _uci_system_section "led" + echo "$color_r $color_g $color_b $mode${mode_args:+ }$mode_args" + done >"$rainbowdir/000-system" + ) + _base_config_updated="y" +} + +# Parse given led setting from UCI +_uci_rainbow_section() { + local led="$1" + color_r="$default_r" color_g="$default_g" color_b="$default_b" + mode="$default_mode" mode_args="$default_mode_args" + ! type led_defaults >/dev/null || led_defaults "$led" + + config_get rawcolor "$led" "color" "" + parse_color "$rawcolor" + + config_get rawmode "$led" "state" "" # backward compatibility + config_get rawmode "$led" "mode" "$rawmode" + [ -n "$rawmode" ] || return 0 + mode="$rawmode" + mode_args="" + if [ "$mode" = "activity" ]; then + config_get trigger "$led" "trigger" "activity" + mode_args="$trigger" + elif [ "$mode" = "animate" ]; then + local animation animation_color_r animation_color_g animation_color_b + animation_init + config_list_foreach "$led" "$animation" _for_uci_animation + animation_finish + mode_args="$animation" + fi +} + +_for_uci_animation() { + local value="$1" + read -r color time <<-EOF + $value + EOF + animation_add "$color" "$time" \ + || warning "Skipping invalid animation state for '$led': $1" +} + +_uci_system_section() { + local section="$1" + local cursysfs + config_get cursysfs "$section" "sysfs" + if [ "$cursysfs" = "$sysfs" ]; then + local default trigger + config_get_bool default "$section" "default" "0" + config_get trigger "$section" "trigger" "none" + case "$trigger" in + none) + [ "$default" != "1" ] \ + || mode="enable" + ;; + time) + local on off + config_get on "$section" "delayon" + config_get off "$section" "delayoff" + if [ -n "$on" ] && [ -n "$off" ]; then + mode="animate" + mode_args="black $off black 0 - $on - 0" + fi + ;; + default-on) + mode="enable" + ;; + heatbeat) + mode="activity" + if [ "$default" = "0" ]; then + mode_args="heatbeat" + else + mode_args="heatbeat-inverted" + fi + ;; + netdev) + local dev + config_get dev "$section" "dev" + if [ -n "$dev" ]; then + mode="activity" + mode_args="netdev-$dev" + fi + # We ignore mode as rainbow does not support it + ;; + phy*) + mode="activity" + mode_args="${trigger%${trigger#phy[0-9]}}" + ;; + usbdev|usbport) + mode="activity" + mode_args="usbport" + # We ignore dev as rainbow simply follows all USB devices + ;; + *) + warning "Unsupported trigger '$trigger' for: system.$section ($sysfs)" + ;; + esac + fi +} diff --git a/hardware/rainbow/files/utils.sh b/hardware/rainbow/files/utils.sh new file mode 100644 index 000000000..51d5bca8f --- /dev/null +++ b/hardware/rainbow/files/utils.sh @@ -0,0 +1,29 @@ +# Common utilities for rainbow scripts + +# All rainbow scripts should set this but by adding it here as well we make sure +set -eu + +warning() { + echo "Warnig:" "$@" >&2 +} + +fail() { + echo "$0:" "$@" >&2 + exit 1 +} + +internal_error() { + echo "$0: Internal error:" "$@" >&2 + exit 3 +} + +# This function should be used to load any other source files that are part of +# rainbow except of this one. +srcdir="$(dirname "$(readlink -f "$0")")" +loadsrc() { + local name="$1" + if ! echo "${_src_loaded:-}" | grep -qF "+$name+"; then + . "$srcdir/$name.sh" + _src_loaded="${_src_loaded:-}+$name+" + fi +} diff --git a/hardware/turris1x/turris1x-rainbow/Makefile b/hardware/turris1x/turris1x-rainbow/Makefile index aaac87bfc..4d3d3e2a4 100644 --- a/hardware/turris1x/turris1x-rainbow/Makefile +++ b/hardware/turris1x/turris1x-rainbow/Makefile @@ -1,5 +1,5 @@ # -## Copyright (C) 2013-2019 CZ.NIC z.s.p.o. (http://www.nic.cz/) +## Copyright (C) 2013-2021 CZ.NIC z.s.p.o. (http://www.nic.cz/) # ## This is free software, licensed under the GNU General Public License v3. # See /LICENSE for more information. @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=turris1x-rainbow PKG_VERSION:=17 -PKG_RELEASE:=3 +PKG_RELEASE:=4 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://gitlab.nic.cz/turris/rainbow.git @@ -25,30 +25,12 @@ include $(INCLUDE_DIR)/package.mk define Package/turris1x-rainbow TITLE:=turris1x-rainbow URL:=https://gitlab.nic.cz/turris/rainbow - PROVIDES:=turris-rainbow DEPENDS:=@KERNEL_DEVMEM @TARGET_mpc85xx_p2020_DEVICE_turris1x endef -define Package/turris1x-rainbow/conffiles -/etc/config/rainbow -endef - define Package/turris1x-rainbow/install $(INSTALL_DIR) $(1)/usr/bin/ - $(INSTALL_BIN) $(PKG_BUILD_DIR)/rainbow $(1)/usr/bin/ - $(INSTALL_DIR) $(1)/etc/init.d/ - $(INSTALL_BIN) ./files/rainbow.init $(1)/etc/init.d/rainbow - $(INSTALL_DIR) $(1)/etc/config/ - $(INSTALL_DATA) ./files/rainbow.config $(1)/etc/config/rainbow - $(INSTALL_DIR) $(1)/etc/cron.d - $(INSTALL_DATA) ./files/rainbow.cron $(1)/etc/cron.d/rainbow - $(INSTALL_BIN) ./files/rainbow_button_sync.sh $(1)/usr/bin/rainbow_button_sync.sh -endef - -define Package/turris1x-rainbow/prerm -#!/bin/sh -[ -n "$$IPKG_INSTROOT" ] || \ - rainbow all auto white + $(INSTALL_BIN) $(PKG_BUILD_DIR)/rainbow $(1)/usr/bin/turris1x-rainbow endef $(eval $(call BuildPackage,turris1x-rainbow)) diff --git a/hardware/turris1x/turris1x-rainbow/files/rainbow.config b/hardware/turris1x/turris1x-rainbow/files/rainbow.config deleted file mode 100644 index bbaceb1a2..000000000 --- a/hardware/turris1x/turris1x-rainbow/files/rainbow.config +++ /dev/null @@ -1,25 +0,0 @@ -# You can set all the leds at once. -config led 'all' - option color 'turris-default' - option status 'auto' - -# Any other config will overwrite the one set in all -#config led 'pwr' -# option color 'FF0066' -# option status 'auto' -# -#config led 'wifi' -# option color 'FFFF00' -# option status 'auto' -# -#config led 'lan' -# option color 'green' -# option status 'auto' -# -#config led 'wan' -# option color 'blue' -# option status 'auto' - -# The ones with lan can't set color and the status overwrites the one in the 'lan' section, if mentioned -#config led 'lan4' -# option status 'disabled' diff --git a/hardware/turris1x/turris1x-rainbow/files/rainbow.cron b/hardware/turris1x/turris1x-rainbow/files/rainbow.cron deleted file mode 100644 index cb4d5923d..000000000 --- a/hardware/turris1x/turris1x-rainbow/files/rainbow.cron +++ /dev/null @@ -1,2 +0,0 @@ -MAILTO="" -* * * * * root /usr/bin/rainbow_button_sync.sh diff --git a/hardware/turris1x/turris1x-rainbow/files/rainbow.init b/hardware/turris1x/turris1x-rainbow/files/rainbow.init deleted file mode 100644 index 94bf1a491..000000000 --- a/hardware/turris1x/turris1x-rainbow/files/rainbow.init +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh /etc/rc.common - -# Start at the end, so the colors change after the boot is done. -START=99 -STOP=00 - -PID_FILE="/var/run/rainbow.pid" - -start() { - # Signal the boot is finished - rainbow all enable blue - sleep 1 - rainbow all enable green - sleep 1 - # Set the colors based on user preferrences - uci show rainbow | sed -ne 's/rainbow\.\([^.]*\)=led/\1/p' | sort | while read SECTION ; do - STATUS=$(uci get rainbow.$SECTION.status) - COLOR=$(uci get rainbow.$SECTION.color) - rainbow $SECTION $STATUS $COLOR - done - # Resume last intensity settings - rainbow intensity $(cat /etc/rainbow.magic) - - # Setup LEDs if enabled - if [ -x /etc/init.d/setup_led ] && /etc/init.d/setup_led enabled; then - /etc/init.d/setup_led restart - fi - - # Start the daemon - if [ -s $PID_FILE ]; then - echo "Daemon is alredy running..." - else - rainbow --daemonize - fi -} - -stop() { - # Reset to the default, so a reboot can be recognized. - rainbow all enable white - kill -9 $(cat $PID_FILE) - rm $PID_FILE -} diff --git a/hardware/turris1x/turris1x-rainbow/files/rainbow_button_sync.sh b/hardware/turris1x/turris1x-rainbow/files/rainbow_button_sync.sh deleted file mode 100755 index 5f30d9f3c..000000000 --- a/hardware/turris1x/turris1x-rainbow/files/rainbow_button_sync.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -MAGIC_FILE="/etc/rainbow.magic" - -# Get actual intensity -ACT_INTENSITY=$(rainbow get intensity) - -# Get value from magic file -[ -f "$MAGIC_FILE" ] || echo 0 > "$MAGIC_FILE" -LAST_VALUE=$(cat "$MAGIC_FILE") - -# Compare them and if last value is different from stored one update it -if [ "$ACT_INTENSITY" != "$LAST_VALUE" ]; then - echo $ACT_INTENSITY > "$MAGIC_FILE" -fi -- GitLab From 1df0c5dca4da3f8efbfe0761dbc5edea28151a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Fri, 12 Nov 2021 11:01:40 +0100 Subject: [PATCH 2/7] turris1x-rainbow-backend: add Turris 1.x specific rainbow backend This uses original rainbow command as there is no kernel support for LEDs on Turris 1.x. --- .../turris1x-rainbow-backend/Makefile | 38 ++++++++ .../files/animator_backend.py | 27 ++++++ .../turris1x-rainbow-backend/files/backend.sh | 89 +++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 hardware/turris1x/turris1x-rainbow-backend/Makefile create mode 100644 hardware/turris1x/turris1x-rainbow-backend/files/animator_backend.py create mode 100644 hardware/turris1x/turris1x-rainbow-backend/files/backend.sh diff --git a/hardware/turris1x/turris1x-rainbow-backend/Makefile b/hardware/turris1x/turris1x-rainbow-backend/Makefile new file mode 100644 index 000000000..4d92a0726 --- /dev/null +++ b/hardware/turris1x/turris1x-rainbow-backend/Makefile @@ -0,0 +1,38 @@ +# +## Copyright (C) 2021 CZ.NIC z.s.p.o. (http://www.nic.cz/) +# +## This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=turris1x-rainbow-backend +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=CZ.NIC +PKG_LICENSE:=GPL-3.0-or-later +PKG_LICENSE_FILES:=LICENSE + +include $(INCLUDE_DIR)/package.mk + +define Package/turris1x-rainbow-backend + TITLE:=turris1x-rainbow-backend + DEPENDS:=\ + @KERNEL_DEVMEM @TARGET_mpc85xx_p2020_DEVICE_turris1x \ + +turris1x-rainbow \ + +rainbow-button-sync \ + +rainbow-animator + PROVIDES:=rainbow-backend +endef + +Build/Compile:=: + +define Package/turris1x-rainbow-backend/install + $(INSTALL_DIR) $(1)/usr/share + $(INSTALL_DATA) ./files/backend.sh $(1)/usr/libexec/rainbow/ + $(INSTALL_DATA) ./files/animator_backend.sh $(1)/usr/libexec/rainbow/ +endef + +$(eval $(call BuildPackage,turris1x-rainbow-backend)) diff --git a/hardware/turris1x/turris1x-rainbow-backend/files/animator_backend.py b/hardware/turris1x/turris1x-rainbow-backend/files/animator_backend.py new file mode 100644 index 000000000..f1c6598af --- /dev/null +++ b/hardware/turris1x/turris1x-rainbow-backend/files/animator_backend.py @@ -0,0 +1,27 @@ +import subprocess + + +class Backend: + """Handler for all leds we can control.""" + + # All available leds in order on the box + LEDS = ["wan", "lan1", "lan2", "lan3", "lan4", "lan5", "wifi", "pwr"] + + def __init__(self): + self.args = [] + + def update(self, ledid: int, red: int, green: int, blue: int) -> None: + """Update color of led on given index.""" + self.args.append(self.LEDS[ledid]) + self.args.append("%.2X%.2X%.2X" % red, green, blue) + + def apply(self) -> None: + """Apply LEDs state updates.""" + self.args.insert(0, "turris1x-rainbow") + subprocess.run(self.args, check=True) + self.args.clear() + + @staticmethod + def handled(ledid: int) -> bool: + """Informs animator if given led animation should be handled by it.""" + return True # On 1.x all leds have to be animated using animator diff --git a/hardware/turris1x/turris1x-rainbow-backend/files/backend.sh b/hardware/turris1x/turris1x-rainbow-backend/files/backend.sh new file mode 100644 index 000000000..20969f44b --- /dev/null +++ b/hardware/turris1x/turris1x-rainbow-backend/files/backend.sh @@ -0,0 +1,89 @@ +# Turris 1.x backend for generic rainbow script +LEDS="wan lan1 lan2 lan3 lan4 lan5 wifi pwr" + +led_defaults() { + local led="$1" + case "$led" in + pwr) + color_r=0 color_g=255 color_b=0 + ;; + lan*|wifi) + color_r=0 color_g=0 color_b=255 + ;; + wan) + color_r=0 color_g=255 color_b=255 + ;; + esac +} + +get_brightness() { + local cur + cur="$(turris1x-rainbow get intensity)" + echo $((cur * 255 / 7)) +} + +preapply() { + /etc/init.d/rainbow-animator pause + + local brightness_level + brightness_level="$(brightness)" + # TODO we might want to use brightness of leds to tweak brightness itself + turris1x_rainbow_args="intensity $((brightness_level * 7 / 255))" +} + +set_led() { + local led="$1" r="$2" g="$3" b="$4" mode="$5" + shift 5 + + turris1x_rainbow_args="${turris1x_rainbow_args}${turris1x_rainbow_args:+ }$led" + + turris1x_rainbow_args="${turris1x_rainbow_args} $(printf "%.2X%.2X%.2X" "$r" "$g" "$b")" + + case "$mode" in + auto|disable|enable) + turris1x_rainbow_args="${turris1x_rainbow_args} $mode" + ;; + animate) + turris1x_rainbow_args="${turris1x_rainbow_args} enable" + # For animation we we rely on animator + ;; + *) + internal_error "Unsupported mode:" "$mode" + ;; + esac +} + +postapply() { + [ -z "$turris1x_rainbow_args" ] \ + || turris1x-rainbow $turris1x_rainbow_args + /etc/init.d/rainbow-animator reload +} + + +boot_sequence() { + turris1x-rainbow all green enable + sleep 1 + turris1x-rainbow all blue enable + sleep 1 + # Note: we apply in the end so we don't have to restore previous state +} + + +# Parse operations that were previously available with rainbow +compatibility() { + local operation="$1"; shift + SHIFTARGS=$# + case "$operation" in + binmask) + compat_binmask "$@" + ;; + intensity) + compat_intensity 7 "$@" + ;; + get) + compat_get 7 "$@" + ;; + esac + shift $(($# - SHIFTARGS)) + SHIFTARGS=$# +} -- GitLab From 0004878c58838bffc39cfc8b9005aa7205617d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Fri, 12 Nov 2021 11:03:04 +0100 Subject: [PATCH 3/7] omnia-rainbow-backend: add rainbow backend for Omnia This can completely replace the original rainbow binary. It directly communicates with kernel. --- hardware/omnia/omnia-rainbow-backend/Makefile | 37 ++++ .../files/animator_backend.py | 24 +++ .../omnia-rainbow-backend/files/backend.sh | 158 ++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 hardware/omnia/omnia-rainbow-backend/Makefile create mode 100644 hardware/omnia/omnia-rainbow-backend/files/animator_backend.py create mode 100644 hardware/omnia/omnia-rainbow-backend/files/backend.sh diff --git a/hardware/omnia/omnia-rainbow-backend/Makefile b/hardware/omnia/omnia-rainbow-backend/Makefile new file mode 100644 index 000000000..ed0c8da33 --- /dev/null +++ b/hardware/omnia/omnia-rainbow-backend/Makefile @@ -0,0 +1,37 @@ +# +## Copyright (C) 2021 CZ.NIC z.s.p.o. (http://www.nic.cz/) +# +## This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=omnia-rainbow-backend +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=CZ.NIC +PKG_LICENSE:=GPL-3.0-or-later +PKG_LICENSE_FILES:=LICENSE + +include $(INCLUDE_DIR)/package.mk + +define Package/omnia-rainbow-backend + TITLE:=omnia-rainbow-backend + DEPENDS:=\ + @TARGET_mvebu_cortexa9_DEVICE_cznic_turris-omnia \ + +rainbow-button-sync \ + +rainbow-animator + PROVIDES:=rainbow-backend +endef + +Build/Compile:=: + +define Package/omnia-rainbow-backend/install + $(INSTALL_DIR) $(1)/usr/share + $(INSTALL_DATA) ./files/backend.sh $(1)/usr/libexec/rainbow/ + $(INSTALL_DATA) ./files/animator_backend.sh $(1)/usr/libexec/rainbow/ +endef + +$(eval $(call BuildPackage,omnia-rainbow-backend)) diff --git a/hardware/omnia/omnia-rainbow-backend/files/animator_backend.py b/hardware/omnia/omnia-rainbow-backend/files/animator_backend.py new file mode 100644 index 000000000..c6e6539de --- /dev/null +++ b/hardware/omnia/omnia-rainbow-backend/files/animator_backend.py @@ -0,0 +1,24 @@ +import os + + +class Backend: + """Handler for all leds we can control.""" + + # All available leds in order on the box + LEDS = ["power", "lan0", "lan1", "lan2", "lan3", "lan4", "wan", "pci1", "pci2", "pci3", "user1", "user2"] + + def __init__(self): + self._fds = tuple(os.open(f"/sys/class/leds/omnia-led:{led}/color", os.O_WRONLY) for led in self.LEDS) + + def update(self, ledid: int, red: int, green: int, blue: int) -> None: + """Update color of led on given index.""" + os.write(self._fds[ledid], f"{red} {green} {blue}".encode()) + + def apply(self) -> None: + """Apply previous LEDs state updates if that is required.""" + # We apply immediatelly so we do not need this. + + @staticmethod + def handled(ledid: int) -> bool: + """Informs animator if given led animation should be handled by it.""" + return True # On Omnia all leds have to be animated using animator diff --git a/hardware/omnia/omnia-rainbow-backend/files/backend.sh b/hardware/omnia/omnia-rainbow-backend/files/backend.sh new file mode 100644 index 000000000..6e5fa8fd9 --- /dev/null +++ b/hardware/omnia/omnia-rainbow-backend/files/backend.sh @@ -0,0 +1,158 @@ +# Turris Omnia backend for generic rainbow script +LEDS="pwr lan0 lan1 lan2 lan3 lan4 wan pci1 pci2 pci3 usr1 usr2" + +SYSFS="/sys/devices/platform/soc/soc:internal-regs/f1011000.i2c/i2c-0/i2c-1/1-002b" + +led2sysfs() { + local led="$1" + case "$led" in # Transform our names of leds to the driver names + pwr) + led="power" + ;; + usr*) + led="user${led#usr}" + ;; + esac + echo "$SYSFS/leds/omnia-led:$led" +} + +led_defaults() { + local led="$1" + case "$led" in + pwr) + color_r=0 color_g=255 color_b=0 + ;; + lan*|pci*) + color_r=0 color_g=0 color_b=255 + ;; + wan) + color_r=0 color_g=255 color_b=255 + ;; + esac +} + +get_brightness() { + local cur + cur="$(cat "$SYSFS/global_brightness")" + echo $((cur * 255 / 100)) +} + +preapply() { + /etc/init.d/rainbow-animator pause + + local brightness_level + brightness_level="$(brightness)" + # The full range is only 0-100 but that is good enough so we don't adjust + # more. We just loose the precision on Omnia but who can see it anyway. + echo "$((brightness_level * 100 / 255))" > "$SYSFS/global_brightness" +} + +set_led() { + local led="$1" r="$2" g="$3" b="$4" mode="$5" + shift 5 + + if [ "$mode" = "auto" ]; then # override auto mode for some + case "$led" in + pwd) + mode="enable" + ;; + pci*) + local trigger + loadsrc pci + if trigger="$(pci_device_activity_detect "$led" "/sys/bus/pci/devices/0000:0${led#pci}:00.0")"; then + mode="activity" + set "$trigger" + else + mode="disable" + fi + ;; + usr*) + mode="disable" + ;; + esac + fi + + local brightness=255 autonomous=0 trigger="none" + case "$mode" in + auto) + autonomous=1 + ;; + disable) + brightness=0 + ;; + enable) + ;; + activity) + trigger="activity" + ;; + animate) + # For animation we we rely on animator + ;; + *) + internal_error "Unsupported mode:" "$mode" + ;; + esac + + local sysfs + sysfs="$(led2sysfs "$led")" + # We have to disable trigger first to make sure that changes are correctly + # applied and not modified by this in the meantime. + echo "none" > "$sysfs/trigger" + echo "$brightness" > "$sysfs/brightness" + echo "$r $g $b" > "$sysfs/color" + echo "$autonomous" > "$sysfs/autonomous" + if [ "$trigger" = "activity" ]; then + apply_activity "$led" "$@" \ + || echo "Warning: activity setup failed for: $led" >&2 + else + echo "$trigger" > "$sysfs/trigger" + fi +} + +postapply() { + /etc/init.d/rainbow-animator reload +} + + +boot_sequence() { + local sysfs + + for led in $LEDS; do + sysfs="$(led2sysfs "$led")" + echo "none" > "$sysfs/trigger" + echo "0" > "$sysfs/autonomous" + echo "255" > "$sysfs/brightness" + echo "0 255 0" > "$sysfs/color" + done + sleep 1 + + for led in $LEDS; do + sysfs="$(led2sysfs "$led")" + echo "0" > "$sysfs/autonomous" + echo "none" > "$sysfs/trigger" + echo "255" > "$sysfs/brightness" + echo "0 0 255" > "$sysfs/color" + done + sleep 1 + # Note: we apply in the end so we don't have to restore previous state +} + + +# Parse operations that were previously available with rainbow +compatibility() { + local operation="$1"; shift + SHIFTARGS=$# + case "$operation" in + binmask) + compat_binmask "$@" + ;; + intensity) + compat_intensity 100 "$@" + ;; + get) + compat_get 100 "$@" + ;; + esac + shift $(($# - SHIFTARGS)) + SHIFTARGS=$# +} -- GitLab From 59393ff9a589212d5493fa3f84604d7ea79af40e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Fri, 12 Nov 2021 11:03:52 +0100 Subject: [PATCH 4/7] mox-rainbow-backend: add rainbow backend for Mox This has still one issue not solved that can't solved easily. That is brightness level functionality. It works as expected for all modes except activity. The kernel triggers use maximum brightness automatically without possibility to change that at the moment. --- hardware/mox/mox-rainbow-backend/Makefile | 37 +++++++++++ .../files/animator_backend.py | 25 ++++++++ .../mox/mox-rainbow-backend/files/backend.sh | 63 +++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 hardware/mox/mox-rainbow-backend/Makefile create mode 100644 hardware/mox/mox-rainbow-backend/files/animator_backend.py create mode 100644 hardware/mox/mox-rainbow-backend/files/backend.sh diff --git a/hardware/mox/mox-rainbow-backend/Makefile b/hardware/mox/mox-rainbow-backend/Makefile new file mode 100644 index 000000000..eaaada185 --- /dev/null +++ b/hardware/mox/mox-rainbow-backend/Makefile @@ -0,0 +1,37 @@ +# +## Copyright (C) 2021 CZ.NIC z.s.p.o. (http://www.nic.cz/) +# +## This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=mox-rainbow-backend +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=CZ.NIC +PKG_LICENSE:=GPL-3.0-or-later +PKG_LICENSE_FILES:=LICENSE + +include $(INCLUDE_DIR)/package.mk + +define Package/mox-rainbow-backend + TITLE:=mox-rainbow-backend + DEPENDS:=\ + @TARGET_mvebu_cortexa53_DEVICE_cznic-mox \ + +rainbow-animator \ + +python3-uci + PROVIDES:=rainbow-backend +endef + +Build/Compile:=: + +define Package/mox-rainbow-backend/install + $(INSTALL_DIR) $(1)/usr/share + $(INSTALL_DATA) ./files/backend.sh $(1)/usr/libexec/rainbow/ + $(INSTALL_DATA) ./files/animator_backend.sh $(1)/usr/libexec/rainbow/ +endef + +$(eval $(call BuildPackage,mox-rainbow-backend)) diff --git a/hardware/mox/mox-rainbow-backend/files/animator_backend.py b/hardware/mox/mox-rainbow-backend/files/animator_backend.py new file mode 100644 index 000000000..df13e80db --- /dev/null +++ b/hardware/mox/mox-rainbow-backend/files/animator_backend.py @@ -0,0 +1,25 @@ +import os +from euci import EUci + + +class Backend: + """Handler for all leds we can control.""" + + def __init__(self): + self._fd = os.open(f"/sys/class/leds/mox:red:activity/brightness", os.O_WRONLY) + self.uci = EUci() + + def update(self, ledid: int, red: int, green: int, blue: int) -> None: + """Update color of led on given index.""" + assert ledid == "activity" + brightness = self.uci.get("rainbow", "all", "brightness", dtype=int, default=255) + os.write(self._fd, f"{red * brightness / 255}".encode()) + + def apply(self) -> None: + """Apply previous LEDs state updates if that is required.""" + # We apply immediatelly so we do not need this. + + @staticmethod + def handled(ledid: int) -> bool: + """Informs animator if given led animation should be handled by it.""" + return True diff --git a/hardware/mox/mox-rainbow-backend/files/backend.sh b/hardware/mox/mox-rainbow-backend/files/backend.sh new file mode 100644 index 000000000..baa79541e --- /dev/null +++ b/hardware/mox/mox-rainbow-backend/files/backend.sh @@ -0,0 +1,63 @@ +# Turris Mox backend for generic rainbow script +LEDS="activity" + +SYSFS="/sys/class/leds/mox:red:activity" + +led2sysfs() { + local led="$1" + [ "$led" = "activity" ] || return + echo "$SYSFS" +} + +preapply() { + # TODO we can use pattern trigger to replace animator + /etc/init.d/rainbow-animator pause +} + +set_led() { + local led="$1" r="$2" g="$3" b="$4" mode="$5" + shift 5 + [ "$led" = "activity" ] || return + + if [ "$mode" = "auto" ]; then # override auto mode + mode="activity" + mode_args="heartbeat" + fi + local global_brightness + global_brightness="$(brightness)" + + local brightness=255 trigger="none" + case "$mode" in + disable) + brightness=0 + ;; + enable) + ;; + activity) + brightness=0 + trigger="activity" + ;; + animate) + # For animation we we rely on animator + ;; + *) + internal_error "Unsupported mode:" "$mode" + ;; + esac + brightness=$(((brightness * global_brightness * r) / (255 * 255))) + + # We have to disable trigger first to make sure that changes are correctly + # applied and not modified by this in the meantime. + echo "none" > "$SYSFS/trigger" + echo "$brightness" > "$SYSFS/brightness" + if [ "$trigger" = "activity" ]; then + apply_activity "$led" "$@" \ + || echo "Warning: activity setup failed for: $led" >&2 + else + echo "$trigger" > "$SYSFS/trigger" + fi +} + +postapply() { + /etc/init.d/rainbow-animator reload +} -- GitLab From 1aa3939dc0e26484bdc000c746c4b8090c84a3e3 Mon Sep 17 00:00:00 2001 From: Michal Hrusecky Date: Thu, 28 Jul 2022 00:10:07 +0200 Subject: [PATCH 5/7] Apply 1 suggestion(s) to 1 file(s) --- hardware/rainbow/files/utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/rainbow/files/utils.sh b/hardware/rainbow/files/utils.sh index 51d5bca8f..b1a8fd698 100644 --- a/hardware/rainbow/files/utils.sh +++ b/hardware/rainbow/files/utils.sh @@ -4,7 +4,7 @@ set -eu warning() { - echo "Warnig:" "$@" >&2 + echo "Warning:" "$@" >&2 } fail() { -- GitLab From 8af4be7c59fc830cd94ac1ef88871310acf68e33 Mon Sep 17 00:00:00 2001 From: Michal Hrusecky Date: Thu, 28 Jul 2022 00:10:12 +0200 Subject: [PATCH 6/7] Apply 1 suggestion(s) to 1 file(s) --- hardware/rainbow/files/backend.example.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/rainbow/files/backend.example.sh b/hardware/rainbow/files/backend.example.sh index 499fb38ce..4eb9dcd2a 100644 --- a/hardware/rainbow/files/backend.example.sh +++ b/hardware/rainbow/files/backend.example.sh @@ -5,7 +5,7 @@ # that those names match with labeling on the box. LEDS="pwr lan0 lan1 lan2 wan" -# This function is required only if leds are kontrolled by kernel driver and +# This function is required only if leds are controlled by kernel driver and # kernel managed triggers are available. When this function is defined and # successfully provides path to sysfs for specified LED then it unlocks # activity triggers for it. -- GitLab From 7266cdea22008b84de0b7a83be222ec9da5de141 Mon Sep 17 00:00:00 2001 From: Michal Hrusecky Date: Thu, 28 Jul 2022 00:10:16 +0200 Subject: [PATCH 7/7] Apply 1 suggestion(s) to 1 file(s) --- hardware/omnia/omnia-rainbow-backend/files/animator_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/omnia/omnia-rainbow-backend/files/animator_backend.py b/hardware/omnia/omnia-rainbow-backend/files/animator_backend.py index c6e6539de..c866641f2 100644 --- a/hardware/omnia/omnia-rainbow-backend/files/animator_backend.py +++ b/hardware/omnia/omnia-rainbow-backend/files/animator_backend.py @@ -21,4 +21,4 @@ class Backend: @staticmethod def handled(ledid: int) -> bool: """Informs animator if given led animation should be handled by it.""" - return True # On Omnia all leds have to be animated using animator + return True # On Omnia all leds could be animated using animator -- GitLab