#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; }