FuN LeD - HaPpy cHriStmAs
Pubblicato da syscalo il 25/12/2000
Livello intermedio

Introduzione

Buon Natale a tutti ;-) E per chi magari non lo festeggia, buone feste comunque!!
Quello che presento oggi e' un programma per gestire i led della tastiera. Concettualmente si tratta di aprire il device /dev/console e di sfruttare le potenzialita' della funzione ioctl per leggere/settare lo stato dei led.
A basso livello, si tratterebbe di andare a leggere la porta hardware corrispondente, inviando i giusti comandi per leggere e scrivere sulla porta, occupandosi anche di attendere che l'operazione venga "eseguita" fisicamente sulla porta stessa.
Questo pero' sara' argomento o di un nuovo tutorial, o di una estensione di questo (in fondo anche io voglio godermi le vacanze ;-p)

Programmi usati

Iniziamo

Come mi piace fare nei miei ultimi tutorial, ho pensato ad un esempio simpatico per spiegarvi questo argomento? Perche' faccio questo? Bhe, perche' a me verrebbe spontaneo chiedermi "Cosa me ne faccio di un programma per gestire i led della tastiera?" ed io vi rispondo "Siamo a Natale, le citta' sono piene di luci, rendiamo un po' piu' simpatico il nostro pc facendolo luccicare un po' ;-) Createvi i vostri giochi di luci!".
Qualche piccola spiegazione tecnica e poi vi lascio immediatamente al sorgente C. Si, l'esempio e' scritto in C ;-p Ma come promesso prossimamente scendero' a livello piu' basso, magari arrivando a comandare direttamente la porta hardware!
Per riferirci alla tastiera del nostro pc utilizziamo il device /dev/console, e lo apriamo tramite la funzione open (man 2 open).
Come anticipato prima, per gestire i led della tastiera sfruttiamo la funzione ioctl (man 2 ioctl e man 2 ioctl_list per la lista delle operazioni permesse dalla ioctl). In particolare usiamo l'operazione KDGETLED per leggere lo stato dei led, e KDSETLED per settare lo stato dei led.

La funzione ioctl sara' usata nel seguente modo:
Per leggere: ioctl(descrittore_device__aperto, operazione_richiesta, indirizzo_variabile_in_cui_salvare_stato)
Per scrivere: ioctl(descrittore_device__aperto, operazione_richiesta, variabile__con_stato_da_settare)

Bhe penso sia tutto, il resto del programma si occupa di far eseguire la sequenza da noi indicata nel buffer buf.

Le funzioni scritte sono ben commentate, quindi l'unica cosa che resta da spiegare e' come scrivere la sequenza da eseguire.
Riempite semplicemente il buffer buf nel seguente modo:

Alla fine trovate un makefile (quasi inutile, ma forse utile):
"make" per compilare
"make exec" per eseguire
"make debug" per eseguire vedendo visualizzati i messaggi inviati allo stderr
"make clean" per eliminare l'eseguibile

Buona lettura del sorgente, che a molti magari risultera' piu' chiaro delle mie parole 8-)


/* file ledGame.h */
/* file header per ledGame.c */

/* header inclusi */
#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/kd.h>

/* costante per identificare la tastiera */
#define KEYBOARDDEVICE  "/dev/console"

/* definizione costanti per valori di ritorno*/
/* programma terminato correttamente */
#define SUCCESS 0

/*** funzione bkpLed ***/
/* terminazione corretta */
#define BKP_SUCC 0
/* errore nella lettura dello stato dei led */
#define BKP_READ_ER -1

/*** funzione resLed ***/
/* terminazione corretta */
#define RES_SUCC 0
/* errore nel ripristino dello stato dei led */
#define RES_WRITE_ER -1

/*** funzione lightOn ***/
/* terminazione corretta */
#define LO_SUCC 0
/* errore nell'accensione del led */
#define LO_ER -1
/* l'identificativo del led non e' valido */
#define LO_CASE_ER -2

/* Prototipi delle funzioni */
int bkpLed(int, unsigned long *);
int resLed(int, unsigned long);
int lightOn(int, unsigned int);
/* FINE */
Ed ecco finalmente il codice sorgente:

/* file ledGame.c */
#include "ledGame.h"

/*
 * funzione bkpLed
 * effettua il backup dello stato attuale dei led
 * Riceve:
 *  keybDev = device della tastiera
 *  *bkp = puntatore alla variabile in cui effettuare il backup
 * Ritorna:
 *  BKP_SUCC = backup effettuato con successo
 *  BKP_READ_ER = errore di lettura dello stato dei led
 */

int bkpLed(int keybDev, unsigned long *bkp)
{
 unsigned int ledState;

 if (ioctl(keybDev, KDGETLED, &ledState))
 {
  return(BKP_READ_ER);
 }
 *bkp=(unsigned int)(ledState&(LED_NUM|LED_CAP|LED_SCR));
 return(BKP_SUCC); 
}


/*
 * funzione resLed
 * effettua il reset dei led allo stato iniziale
 * Riceve:
 *  keybDev = device della tastiera
 *  value = valore da ripristinare
 * Ritorna:
 *  RES_SUCC = reset effettuato con successo
 *  RES_WRITE_ER = errore di scrittura dello stato dei led
 */

int resLed(int keybDev, unsigned long value)
{
 if (ioctl(keybDev, KDSETLED, value))
 {
  return(RES_WRITE_ER);
 }
 return(RES_SUCC); 
}


/*
 * funzione lightOn
 * accende il led indicato
 * Riceve:
 *  keybDev = device della tastiera
 *  led = led da accendere
 * Ritorna:
 *  LO_SUCC = accensione effettuata con successo
 *  LO_ER = errore nell'accensione
 *  LO_CASE_ER = identificativo led non valido
 */

int lightOn(int keybDev, unsigned int led)
{
  if('N'==led)
   led=LED_NUM;
  else
  if('C'==led)
   led=LED_CAP;
  else
  if('S'==led)
   led=LED_SCR;
  else
   return(LO_CASE_ER);

 if (ioctl(keybDev, KDSETLED, led))
 {
  return(LO_ER);
 }

 sleep(1);
 return(LO_SUCC);

}

/* funzione main */

int
main(int argc, char **argv)
{

 unsigned int i, j, n, LOOP; /* var per i cicli */
 unsigned long bkp; /* valori dei led */
 int keyboardDevice; /* device aperto */

 unsigned char led; /* led da accendere */

/* buffer da cui legge la sequenza dei led da accendere
 * sequenze di test
 * unsigned char buf[]={2, 'N', 'C', 'S', 'C', 'N', 'C', 'S', 'C', 0};
 * unsigned char buf[]={0,'N','C','S',0};
 */
 unsigned char buf[]={3,'N','S','N','S','C','N','C','N','S','S','C',0};
 unsigned int bufL; /* lunghezza del buffer buf */

 if (-1 == (keyboardDevice = open(KEYBOARDDEVICE, O_RDONLY)))
 {
  fprintf(stderr, "%s: Errore apertura %s\n", argv[0], KEYBOARDDEVICE);
  exit(1);
 }

 if(BKP_SUCC==bkpLed(keyboardDevice, &bkp))
  fprintf(stderr, "%s: Backup led effettuato con successo!\n", argv[0]);
 else
  fprintf(stderr, "%s: Errore backup led!\n", argv[0]);


 if(0==buf[0])
 {
  n=1;
  buf[0]=1;
  bufL=strlen(buf);
  LOOP=1;
 }
 else
 {
  n=buf[0];
  bufL=strlen(buf);
  LOOP=0;
 }

 do
 {
 for(i=1; i<=n; i++)
 {
  for(j=1; j<bufL && buf[j]!=0; j++)
  {
   led=buf[j];
   if(0>lightOn(keyboardDevice, led))
    fprintf(stderr, "%s: errore nella sequenza!\n", argv[0]);
  }
 }
 } while(LOOP);


 if(RES_SUCC==resLed(keyboardDevice, bkp))
  fprintf(stderr, "%s: Ripristino led effettuato con successo!\n", argv[0]);
 else
  fprintf(stderr, "%s: Errore ripristino led!\n", argv[0]);
 
 return(SUCCESS);
}

Ed ecco il Makefile, da eseguire come utente root!


#Makefile per il programma ledGame
all: ledGame.c ledGame.h
	gcc -o ledGame ledGame.c
	strip --strip-all ledGame
	chown root ledGame
	chmod +s ledGame

#redirige lo stderr su /dev/null e avvia il programma in background
exec:
	./ledGame 2> /dev/null &

debug:
	./ledGame

clean:
	rm -f ledGame

#Fine

Conclusioni

Fine! Adesso potete divertirvi a creare le vostre sequenze di luci, e farle eseguire all'infinito... fossi in voi farei qualcos'altro 8-D