Logo Search packages:      
Sourcecode: ledcontrol version File versions  Download package

led.c

/*
 * The actual low-level LED handling.
 */

#include "ledd.h"

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/kd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <glob.h>


/* GLOBAL CONSTANTS: */

/* This has to match the sequence of LED_X in ledd.h! */
const gint led_flags[LED_MAX]={LED_SCR,LED_CAP,LED_NUM};

/* This must contain all the flags or'ed together. */
const gint led_flags_all=LED_SCR|LED_CAP|LED_NUM;


/* Static functions */
static void led_blink(LedControl *led);
static void led_set_all(options *opts,gint andflag,gint orflag);
static void led_log_error(gchar *str,gchar *arg);

/*
 * Checks whole opts->leds table for stuff to do and does it.
 * If animation is on, then do that at end.
 * Returns always TRUE (continue looping).
 */
gboolean led_do_your_thing(options *opts) {
      gint andflag,orflag;
      LedName led;
      gint priority;

      andflag=led_flags_all;
      orflag=0;
      for (led=0; led<LED_MAX; led++) {
            for (priority=PRIORITY_MAX; priority>=0; priority--) {
                  /* Why can't you use enums in a switch ?!?!? */
                  if (opts->leds[led][priority]->type==LEDTYPE_NORMAL)
                        continue;
                  if (opts->leds[led][priority]->type==LEDTYPE_OFF)
                        led_flag_set(&andflag,&orflag,led,LEDTYPE_OFF);
                  if (opts->leds[led][priority]->type==LEDTYPE_ON)
                        led_flag_set(&andflag,&orflag,led,LEDTYPE_ON);
                  if (opts->leds[led][priority]->type==LEDTYPE_BLINK) {
                        led_blink(opts->leds[led][priority]);
                        if (opts->leds[led][priority]->mode)
                              led_flag_set(&andflag,&orflag,
                                         led,LEDTYPE_ON);
                        else
                              led_flag_set(&andflag,&orflag,
                                         led,LEDTYPE_OFF);
                  }
                  break;
            }
      }

      /* If we are doing an animation... */
      if (opts->anim!=NULL) {
            /* Animation time!!! */
            led_do_anim(opts);
            /* These are justified in the file LOGICS: */
            andflag=andflag & opts->anim_and;
            orflag=(orflag & opts->anim_and) | opts->anim_or;
      }

      led_set_all(opts,andflag,orflag);
      return TRUE;
}


/*
 * AND's and OR's the flags into the current LED settings on tty *name.
 * Now optimized for the bare minimum of ioctl's. It re-opens the ttys every
 * 0.2 seconds (that shouldn't bother anyone during console-switch).
 * Returns flags actually set or -1 on error.
 */
gint led_set(File *f,gint and, gint or) {
      char current;
      char ref;
      char new;
      int i;

      if (f->timer==NULL || f->fd<0 || g_timer_elapsed(f->timer,NULL)>=0.2) {
            /* Re-open the tty */

            if (f->fd>=0)
                  close(f->fd);

            f->fd=open(f->name,O_RDONLY);
            if (f->fd==-1) {
                  led_log_error("cannot open tty %s (%s)",f->name);
                  return -1;
            }


            /* Get text/graphics mode */
            if (ioctl(f->fd,KDGETMODE,&i)) {
                  led_log_error("KDGETMODE on tty %s failed (%s)",
                              f->name);
                  close(f->fd);
                  f->fd=-1;
                  return -1;
            }
            if (i==KD_GRAPHICS)
                  f->type=TYPE_GRAPHICS;
            else
                  f->type=TYPE_TEXT;

            /* Set the timer */
            if (f->timer==NULL) {
                  f->timer=g_timer_new();
                  g_timer_start(f->timer);
            }
            g_timer_reset(f->timer);
      }

      /* f->fd now holds the file descriptor,
       * do your stuff and leave it open... */


      /* Get the tty status */
      if (f->type==TYPE_GRAPHICS) {
            /* In X we assume LEDs to be in "correct" state */
            if (ioctl(f->fd,KDGETLED,&current)) {
                  led_log_error("KDGETLED on tty %s failed (%s)",
                              f->name);
                  return -1;
            }
            ref=current;
      } else {
            if (ioctl(f->fd,KDGETLED,&current)) {
                  led_log_error("KDGETLED on tty %s failed (%s)",
                              f->name);
                  return -1;
            }
            if (ioctl(f->fd,KDGKBLED,&ref)) {
                  led_log_error("KDGKBLED on tty %s failed (%s)",
                              f->name);
                  return -1;
            }
      }

      new=(ref & and) | or;

      if (new!=current) {
            if (ioctl(f->fd,KDSETLED,new)) {
                  led_log_error("KDSETLED on tty %s failed (%s)",
                              f->name);
                  return -1;
            }
      }

      return new;
}



/*
 * Set LEDs on all tty's specified to the normal state. Does not
 * function in X.
 * Returns amount of tty's for which the normal setting failed.
 *
 * tty-change
 */
gint led_set_all_normal(options *opts) {
      GSList *list;
      gint ret=0;
      glob_t globbuf;
      File *f;
      int i;

      for (list=opts->fixttys; list; list=g_slist_next(list)) {
            if (list->data==NULL)
                  continue;
            f=list->data;

            /* Do the globbing */
            if (glob(f->name,GLOB_ERR,NULL,&globbuf)) {
                  g_warning("cannot determine files matching %s",
                          f->name);
                  /* TODO: Should I globfree()? We're exiting now,
                   * so it doesn't really matter... */
                  ret++;
                  continue;
            }

            for (i=0; i<globbuf.gl_pathc; i++)
                  if (led_set_normal(globbuf.gl_pathv[i]))
                        ret++;
            globfree(&globbuf);
      }
      return ret;
}


/*
 * Set LEDs on tty defined in *name to the normal state. Does not
 * function in X.
 */
gint led_set_normal(gchar *name) {
      char c;
      int fd;

      fd=open(name,O_RDONLY);
      if (fd==-1) {
            /* Called only on exit. */
            g_warning("cannot open tty %s (%s)",name,STRERROR);
            return -1;
      }

      c=~0;     /* No, that's not a perl regexp. ;) */

      if (ioctl(fd,KDSETLED,c)) {
            g_warning("KDSETLED on tty %s failed (%s)",name,STRERROR);
            close(fd);
            return -1;
      }
      close(fd);
      return 0;
}



/*
 * Sets the led flags to a specified type.
 */
void led_flag_set(gint *andflag,gint *orflag,LedName led,LedType type) {
      /* Why can't enums be used in a switch ?!?!? */
      if (type==LEDTYPE_NORMAL) {   /* AND on, OR off */
            *andflag|=led_flags[led];
            *orflag&=(~led_flags[led]);
      }
      if (type==LEDTYPE_ON) {       /* AND on, OR on */
            *andflag|=led_flags[led];
            *orflag|=led_flags[led];
      }
      if (type==LEDTYPE_OFF) {      /* AND off, OR off */
            *andflag&=(~led_flags[led]);
            *orflag&=(~led_flags[led]);
      }
      return;
}



/*
 * Frees all possible stuff in led. Does NOT free led.
 */
void led_free(LedControl *led) {
      if (led->timer) {
            g_timer_destroy(led->timer);
            led->timer=NULL;
      }
      if (led->blink) {
            g_slist_free(led->blink);
            led->blink=NULL;
      }
      return;
}


/*
 * Blinks one led.
 */
static void led_blink(LedControl *led) {
      if (led->type!=LEDTYPE_BLINK)
            return;
      if (GPOINTER_TO_INT(led->blink->data) <=
          (gint)(g_timer_elapsed(led->timer,NULL)*1000)) {
            led->mode=!led->mode;
            g_timer_reset(led->timer);
            /* Rotate the list */
            led->blink=gslist_rotate(led->blink);
      }
      return;
}



/*
 * Sets led status on all ttys according to andflag and orflag.
 */
static void led_set_all(options *opts,gint andflag,gint orflag) {
      GSList *list;

      for (list=opts->ttys; list; list=g_slist_next(list)) {
            File *f;
            if (list->data==NULL)
                  continue;

            f=list->data;
            led_set(f,andflag,orflag); /* Logs error itself. */
      }
      return;
}

/*
 * Makes all changes to opts->anim_(and|or) -flags before next
 * wait command.
 */
void led_do_anim(options *opts) {
      GSList *list;
      AnimAction *act;
      gboolean met_loop=FALSE;


      /* The first one must always be defined and a timeout. */
      if (ANIM(opts->anim->data)->data >=
          (gint)(g_timer_elapsed(opts->anim_timer,NULL)*1000)) 
            return;

      /* Move and / or rotate */
      if (opts->anim_loop)
            opts->anim=gslist_rotate(opts->anim);
      else
            opts->anim=gslist_next_free(opts->anim);
      g_timer_reset(opts->anim_timer);


      list=opts->anim;
      while (list) {
            if (list->data==NULL) {
                  /* We don't need NULLs */
                  list=gslist_next_free(list);
                  continue;
            }
            act=ANIM(list->data);
            if (act->type==ANIM_WAIT)
                  break;
            if (act->type==ANIM_LOOP) {
                  /* Don't allow loop twice per round, that would
                   * mean infinite loop -> animation bad. */
                  if (met_loop) {
                        gslist_free_all(opts->anim);
                        opts->anim=NULL;
                        return;
                  }

                  met_loop=TRUE;
                  opts->anim_loop=TRUE;
            } else {
                  led_flag_set(&opts->anim_and,&opts->anim_or,
                             act->data,act->type);
            }
            if (opts->anim_loop)
                  list=gslist_rotate(list);
            else
                  list=gslist_next_free(list);
      }
      opts->anim=list;

      return;
}

/*
 * Log an error, but don't flood with them. str is assumed to be
 * a printf-like format string with two "%s", the first one of which is
 * given as arg and the second one is set to be the current error.
 * Logs at warning level.
 */
static void led_log_error(gchar *str,gchar *arg) {
      /* TODO: this is a really naive flood-stopper... */
      static GTimer *timer=NULL;

      if (timer==NULL) {
            timer=g_timer_new();
            g_timer_start(timer);
            g_warning(str,arg,STRERROR);
            return;
      }
      if (g_timer_elapsed(timer,NULL)>10) {
            g_timer_reset(timer);
            g_warning(str,arg,STRERROR);
            return;
      }
      /* Don't flood it. */
      return;
}


Generated by  Doxygen 1.6.0   Back to index