diff options
Diffstat (limited to 'packages/openslug-init/openslug-init-0.10/leds.c')
-rw-r--r-- | packages/openslug-init/openslug-init-0.10/leds.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/packages/openslug-init/openslug-init-0.10/leds.c b/packages/openslug-init/openslug-init-0.10/leds.c index e69de29bb2..e2120ae5d6 100644 --- a/packages/openslug-init/openslug-init-0.10/leds.c +++ b/packages/openslug-init/openslug-init-0.10/leds.c @@ -0,0 +1,190 @@ + #include <stdio.h> + #include <stdlib.h> + #include <math.h> + #include <errno.h> + #include <string.h> + #include <endian.h> + #include <unistd.h> + #include <fcntl.h> + #include <sys/ioctl.h> + #include "leds.h" + + static int leds; + static int reset; + static int verbose = 0; + enum { + off=0, on=1, blink, unknown, transition=unknown + }; + + + void init_leds(void) + { + int i; + if ((leds = open("/dev/leds", O_RDWR)) < 0) { + int e1 = errno; + if (e1 != ENOENT) { + + fprintf(stderr,"Error: Could not open LEDS device file '/dev/leds' : %s\n", + strerror(e1)); + if(e1 == EACCES) + fprintf(stderr,"Run as root\n"); + exit(1); + } + } + + if (verbose) + printf("leds: initialized.\n"); + } + + void led_ioctl( int cmd, int num ) + { + int i, st; + + if (ioctl(leds, cmd, num) < 0) { + int e1 = errno; + fprintf(stderr, "leds: ioctl(%d,%d): failed to set leds: %s\n", + cmd, num, strerror(e1)); + exit(1); + } + } + + void led_set( int led, int state ) + { + switch (state) { + case off: if (!reset) led_ioctl(N2_LM_OFF, led); break; + case on: led_ioctl(N2_LM_ON, led); break; + case blink: /* Ensure any previous timer gets deleted first and that + * the LED is in a well known state. + */ + if (!reset) led_ioctl(N2_LM_OFF, led); + led_ioctl(N2_LM_BLINK, led); break; + } + } + + int led( int ch ) { + switch (ch) { + case 'r': return LED_RS_RED; + case 'g': return LED_RS_GRN; + case '1': return LED_DISK1; + case '2': return LED_DISK2; + case 'A': reset = 1; return LED_ALL; + default: fprintf(stderr, "leds: %c: unknown LED (use r,g,0,1 or A)\n", ch); + exit(1); + } + } + + int main( int argc, char **argv ) + { + /* Default: switch green on, red off (-A +g). */ + if (argc == 1) { + verbose = 1; + init_leds(); + led_ioctl(N2_LM_ALL_OFF, 0); + led_ioctl(N2_LM_ON, LED_RS_GRN); + } else { + int i, alt=0, state[PHYS_LEDS]; + for(i=0; i<PHYS_LEDS; ++i) + state[i] = unknown; + reset = 0; + + while (--argc > 0) { + char *arg = *++argv; + int st; + if (strcmp(arg, "-v") == 0) { + ++verbose; + continue; + } + + switch (*arg) { + case '+': st = on; break; + case '-': st = off; break; + case '!': st = blink; break; + case '/': st = transition; break; + default: fprintf(stderr, "leds: %c: unknown option\n", *arg); + exit(1); + } + + if (st != transition) { + while (*++arg) { + i = led(*arg); + if (i == LED_ALL) + for (i=0; i<PHYS_LEDS; ++i) state[i] = st; + else + state[i] = st; + } + } else { + int done, newstate[PHYS_LEDS]; + for(i=0; i<PHYS_LEDS; ++i) + newstate[i] = off; + while (*++arg) { + i = led(*arg); + if (i == LED_ALL) + for (i=0; i<PHYS_LEDS; ++i) newstate[i] = on; + else + newstate[i] = on; + } + + /* Merge the newstate back in. This sets 'alt' if going + * from an old state of just red to a new of just green + * or vice versa (and this is the only way of getting + * 'alt') + */ + /* Blink anything which changes from off to on or from + * on to off (this ignores anything already blinking). + */ + for (done=i=0; i<PHYS_LEDS; ++i) { + if (state[i] == !newstate[i]) { + done = 1; + state[i] = blink; + } + } + + /* Is anything (new) blinking? If it is then deal + * with the red/green case - blinking red,green is + * amber, is that what we want? This could be + * improved by a better kernel interface - it would + * be nice just to specify on/off times and a start + * time for each LED. + */ + if (done) { + if (state[LED_RS_RED] == blink && state[LED_RS_GRN] == blink && + newstate[LED_RS_RED] == !newstate[LED_RS_GRN]) { + /* Kernel bug: must switch off r and g first. */ + alt = 1; + } + } else { + for (i=0; i<PHYS_LEDS; ++i) { + if (newstate[i] == on) { + state[i] = blink; + } + } + } + } + } + + /* Go through the list making the required settings. 'alt' is + * special. 'reset' means A was given and all the settings are + * known. + */ + init_leds(); + if (reset) + led_ioctl(N2_LM_ALL_OFF, 0); + if (alt) { + /* Turn the leds off first to get to a known state. */ + led_set(LED_RS_GRN, off); + led_set(LED_RS_RED, off); + led_ioctl(N2_LM_ALT, LED_RS_RED); + } else { + /* KERNEL BUG: setting the green timer zaps the red behaviour + * to toggle the green, therefore if red blink is set before + * green blink no blink will happen! + */ + led_set(LED_RS_GRN, state[LED_RS_GRN]); + led_set(LED_RS_RED, state[LED_RS_RED]); + } + led_set(LED_DISK1, state[LED_DISK1]); + led_set(LED_DISK2, state[LED_DISK2]); + } + + return 0; + } |