new package: rssileds daemon
authorGabor Juhos <juhosg@openwrt.org>
Mon, 13 Aug 2012 14:01:34 +0000 (14:01 +0000)
committerGabor Juhos <juhosg@openwrt.org>
Mon, 13 Aug 2012 14:01:34 +0000 (14:01 +0000)
rssileds is a small user-space process to control LEDs by polling the
signal quality reported by a WiFi interface. By using the iwinfo library,
rssileds is independent of the WiFi driver used.
It supports pwm controlled LEDs and may by used to nicely fade through
all colors in real-time of the rainbow while only wasting very little CPU
time and a small constant amount of system memory.
An example configuration for the ALL0258N will follow in the next patch.

This is a slightly improved version of rssileds, now quality values are
in percent and stuff is written to syslog.

Signed-off-by: Daniel Golle <dgolle@allnet.de>
SVN-Revision: 33163

package/rssileds/Makefile [new file with mode: 0644]
package/rssileds/files/rssileds.init [new file with mode: 0644]
package/rssileds/src/rssileds.c [new file with mode: 0644]

diff --git a/package/rssileds/Makefile b/package/rssileds/Makefile
new file mode 100644 (file)
index 0000000..27354f0
--- /dev/null
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 2011-2012 Daniel Golle <dgolle@allnet.de>
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=rssileds
+PKG_VERSION:=0.1
+PKG_RELEASE:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/rssileds
+  SECTION:=net
+  CATEGORY:=Network
+  TITLE:=RSSI real-time LED indicator
+  DEPENDS:=libiwinfo
+endef
+
+define Package/rssileds/description
+  A small process written in C to update the signal-strength indicator LEDs
+endef
+
+define Build/Prepare
+       mkdir -p $(PKG_BUILD_DIR)
+       $(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+       $(TARGET_CC) $(TARGET_CFLAGS) -Wall -liwinfo \
+               -o $(PKG_BUILD_DIR)/rssileds $(PKG_BUILD_DIR)/rssileds.c
+endef
+
+define Package/rssileds/install
+       $(INSTALL_DIR) $(1)/etc/init.d
+       $(INSTALL_BIN) ./files/rssileds.init $(1)/etc/init.d/rssileds
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/rssileds $(1)/usr/sbin/
+endef
+
+$(eval $(call BuildPackage,rssileds))
diff --git a/package/rssileds/files/rssileds.init b/package/rssileds/files/rssileds.init
new file mode 100644 (file)
index 0000000..b0d2627
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/sh /etc/rc.common
+# (C) 2012 Daniel Golle, Allnet GmbH <dgolle@allnet.de>
+
+START=96
+STOP=96
+RSSILEDS_BIN="/usr/sbin/rssileds"
+
+SERVICE_DAEMONIZE=1
+SERVICE_WRITE_PID=1
+
+start_rssid() {
+       local name
+       local dev
+       local threshold
+       local refresh
+       local leds
+       config_get name $1 name
+       config_get dev $1 dev
+       config_get threshold $1 threshold
+       config_get refresh $1 refresh
+       leds="$( cur_iface=$1 ; config_foreach get_led led )"
+       SERVICE_PID_FILE=/var/run/rssileds-$dev.pid
+       service_start $RSSILEDS_BIN $dev $refresh $threshold $leds
+}
+
+stop_rssid() {
+       local dev
+       config_get dev $1 dev
+       SERVICE_PID_FILE=/var/run/rssileds-$dev.pid
+       service_stop $RSSILEDS_BIN
+}
+
+get_led() {
+       local name
+       local sysfs
+       local trigger
+       local iface
+       config_get sysfs $1 sysfs
+       config_get name $1 name "$sysfs"
+       config_get trigger $1 trigger "none"
+       config_get iface $1 iface
+       config_get minq $1 minq
+       config_get maxq $1 maxq
+       config_get offset $1 offset
+       config_get factor $1 factor
+       [ "$trigger" = "rssi" ] || return
+       [ "$iface" = "$cur_iface" ] || return
+       [ ! "$minq" ] || [ ! "$maxq" ] || [ ! "$offset" ] || [ ! "$factor" ] && return
+       echo "none" > /sys/class/leds/$sysfs/trigger
+       echo "$sysfs $minq $maxq $offset $factor"
+}
+
+off_led() {
+       local name
+       local sysfs
+       local trigger
+       config_get sysfs $1 sysfs
+       config_get name $1 name "$sysfs"
+       config_get trigger $1 trigger "none"
+       [ "$trigger" = "rssi" ] || return
+       echo "0" > /sys/class/leds/$sysfs/brightness
+}
+
+start() {
+       [ -e /sys/class/leds/ ] && [ -x "$RSSILEDS_BIN" ] && {
+               config_load system
+               config_foreach start_rssid rssid
+       }
+}
+
+stop() {
+       config_load system
+       config_foreach stop_rssid rssid
+       config_foreach off_led led
+}
diff --git a/package/rssileds/src/rssileds.c b/package/rssileds/src/rssileds.c
new file mode 100644 (file)
index 0000000..2f25c84
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * configurable RSSI LED control daemon for OpenWrt
+ *  (c) 2012 Allnet GmbH, Daniel Golle <dgolle@allnet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * The author may be reached as dgolle@allnet.de, or
+ * ALLNET GmbH
+ * Maistr. 2
+ * D-82110 Germering
+ * Germany
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include "iwinfo.h"
+
+#define RUN_DIR                        "/var/run"
+#define LEDS_BASEPATH          "/sys/class/leds/"
+#define BACKEND_RETRY_DELAY    500000
+
+char *ifname;
+int qual_max;
+
+struct led {
+       char *sysfspath;
+       FILE *controlfd;
+       unsigned char state;
+};
+
+typedef struct rule rule_t;
+struct rule {
+       struct led *led;
+       int minq;
+       int maxq;
+       int boffset;
+       int bfactor;
+       rule_t *next;
+};
+
+void log_rules(rule_t *rules)
+{
+       rule_t *rule = rules;
+       while (rule)
+       {
+               syslog(LOG_INFO, " %s r: %d..%d, o: %d, f: %d\n",
+                       rule->led->sysfspath,
+                       rule->minq, rule->maxq,
+                       rule->boffset, rule->bfactor);
+               rule = rule->next;
+       }
+}
+
+int init_led(struct led **led, char *ledname)
+{
+       struct led *newled;
+       struct stat statbuffer;
+       int status;
+       char *bp;
+       FILE *bfp;
+
+       bp = calloc(sizeof(char), strlen(ledname) + strlen(LEDS_BASEPATH) + 12);
+       if ( ! bp )
+               goto return_error;
+
+       sprintf(bp, "%s%s/brightness", LEDS_BASEPATH, ledname);
+
+       status = stat(bp, &statbuffer);
+       if ( status )
+               goto cleanup_fname;
+
+       bfp = fopen( bp, "w" );
+       if ( !bfp )
+               goto cleanup_fname;
+
+       if ( ferror(bfp) )
+               goto cleanup_fp;
+
+       /* sysfs path exists and, allocate LED struct */
+       newled = calloc(sizeof(struct led),1);
+       if ( !newled )
+               goto cleanup_fp;
+
+       newled->sysfspath = bp;
+       newled->controlfd = bfp;
+       
+       *led = newled;
+       return 0;
+
+cleanup_fp:
+       fclose(bfp);
+cleanup_fname:
+       free(bp);
+return_error:
+       syslog(LOG_CRIT, "can't open LED %s\n", ledname);
+       *led = NULL;
+       return -1;
+}
+
+void close_led(struct led **led)
+{
+       fclose((*led)->controlfd);
+       free((*led)->sysfspath);
+       free((*led));
+       (*led)=NULL;
+}
+
+int set_led(struct led *led, unsigned char value)
+{
+       char buf[8];
+
+       if ( ! led )
+               return -1;
+
+       if ( ! led->controlfd )
+               return -1;
+
+       snprintf(buf, 8, "%d", value);
+
+       rewind(led->controlfd);
+
+       if ( ! fwrite(buf, sizeof(char), strlen(buf), led->controlfd) )
+               return -2;
+
+       fflush(led->controlfd);
+       led->state=value;
+
+       return 0;
+}
+
+
+int quality(const struct iwinfo_ops *iw, const char *ifname)
+{
+       int qual;
+
+       if ( ! iw ) return -1;
+
+       if (qual_max < 1)
+               if (iw->quality_max(ifname, &qual_max))
+                       return -1;
+
+       if (iw->quality(ifname, &qual))
+               return -1;
+
+       return ( qual * 100 ) / qual_max ;
+}
+
+int open_backend(const struct iwinfo_ops **iw, const char *ifname)
+{
+       *iw = iwinfo_backend(ifname);
+
+       if (!(*iw))
+               return 1;
+
+       return 0;
+}
+
+void update_leds(rule_t *rules, int q)
+{
+       rule_t *rule = rules;
+       while (rule)
+       {
+               int b;
+               /* offset and factore correction according to rule */
+               b = ( q + rule->boffset ) * rule->bfactor;
+               if ( b < 0 )
+                       b=0;
+               if ( b > 255 )
+                       b=255;
+
+               if ( q >= rule->minq && q <= rule->maxq )
+                       set_led(rule->led, (unsigned char)b);
+               else
+                       set_led(rule->led, 0);
+
+               rule = rule->next;
+       }
+}
+
+int main(int argc, char **argv)
+{
+       int i,q,q0,r,s;
+       const struct iwinfo_ops *iw = NULL;
+       rule_t *headrule = NULL, *currentrule = NULL;
+
+       if (argc < 9 || ( (argc-4) % 5 != 0 ) )
+       {
+               printf("syntax: %s (ifname) (refresh) (threshold) (rule) [rule] ...\n", argv[0]);
+               printf("  rule: (sysfs-name) (minq) (maxq) (offset) (factore)\n");
+               return 1;
+       }
+
+       ifname = argv[1];
+
+       /* refresh interval */
+       if ( sscanf(argv[2], "%d", &r) != 1 )
+               return 1;
+
+       /* sustain threshold */
+       if ( sscanf(argv[3], "%d", &s) != 1 )
+               return 1;
+
+       openlog("rssileds", LOG_PID, LOG_DAEMON);
+       syslog(LOG_INFO, "monitoring %s, refresh rate %d, threshold %d\n", ifname, r, s);
+
+       currentrule = headrule;
+       for (i=4; i<argc; i=i+5) {
+               if (! currentrule)
+               {
+                       /* first element in the list */
+                       currentrule = calloc(sizeof(rule_t),1);
+                       headrule = currentrule;
+               }
+               else
+               {
+                       /* follow-up element */
+                       currentrule->next = calloc(sizeof(rule_t),1);
+                       currentrule = currentrule->next;
+               }
+
+               if ( init_led(&(currentrule->led), argv[i]) )
+                       return 1;
+               
+               if ( sscanf(argv[i+1], "%d", &(currentrule->minq)) != 1 )
+                       return 1;
+
+               if ( sscanf(argv[i+2], "%d", &(currentrule->maxq)) != 1 )
+                       return 1;
+               
+               if ( sscanf(argv[i+3], "%d", &(currentrule->boffset)) != 1 )
+                       return 1;
+               
+               if ( sscanf(argv[i+4], "%d", &(currentrule->bfactor)) != 1 )
+                       return 1;
+       }
+       log_rules(headrule);
+
+       q0 = -1;
+       do {
+               q = quality(iw, ifname);
+               if ( q < q0 - s || q > q0 + s ) {
+                       update_leds(headrule, q);
+                       q0=q;
+               };
+               // re-open backend...
+               if ( q == -1 && q0 == -1 ) {
+                       if (iw) {
+                               iwinfo_finish();
+                               iw=NULL;
+                               usleep(BACKEND_RETRY_DELAY);
+                       }
+                       while (open_backend(&iw, ifname))
+                               usleep(BACKEND_RETRY_DELAY);
+               }
+               usleep(r);
+       } while(1);
+
+       iwinfo_finish();
+
+       return 0;
+}