/* * * IRBOX, an Ivy driver for infra-red remote controls * * Copyright 1998-1999 * Centre d'Etudes de la Navigation Aerienne * * Device driver * * $Id$ * */ #include #include #include #include #include #include #include #include #include #include #include #include "irdev.h" #define CHECKEDWRITE(fd , buff , len ) \ if (write( fd, buff, len) != len) { perror("IR write"); return -1; } #define NBITS 8 #define SET(n, p) ((p)[(n)/NBITS] |= ((unsigned)1 << ((n) % NBITS))) #define CLR(n, p) ((p)[(n)/NBITS] &= ~((unsigned)1 << ((n) % NBITS))) #define ISSET(n, p) ((p)[(n)/NBITS] & ((unsigned)1 << ((n) % NBITS))) char BTVUP[] = { 0xf2, 0xf0, 0xd0, 0xfc, 0xbc, 0x34 }; struct ir_state { int fd; IrEvtCallback callback; IrTimerSetter set_timeout; IrTimerCanceller cancel_timeout; /* state machine stuff */ char state; char value[6]; void *checkinit_id; /* misc. std. driver stuff */ unsigned char initialized;/* true if data structures are set */ /* and init sequence has been sent to */ /* device */ unsigned char ready; /* true if we've received confirmation */ /* of the init sequence from the device */ unsigned char errorCount;/* count of bad messages for recovery */ }; static void IrDefaultCallback (IrState *ir, IrEvent event, const char *value) { switch (event ) { case EVENT_BTN_PRESS: fprintf (stderr, "IR default callback EVENT_BTN_PRESS %s\n", value ); break; case EVENT_BTN_RELEASE: fprintf (stderr, "IR default callback EVENT_BTN_PRESS %s\n", value ); break; case EVENT_BTN_PRESS_TV_VOL_UP: fprintf (stderr, "IR default callback EVENT_BTN_PRESS_TV_VOL_UP %s\n", value ); break; } } static int IrInitDevice (IrState*); int IrInit (IrState *ir, IrEvtCallback cb, IrTimerSetter st, IrTimerCanceller ct) { ir->callback = cb; ir->set_timeout = st; ir->cancel_timeout = ct; return IrInitDevice (ir); } static void IrCheckInit (void * id, IrState *ir, int delta) { if ((!ir->ready) && (ir->checkinit_id)) { if (ir->errorCount++<5) { IrInitDevice (ir); return; } else { fprintf(stderr,"Ir box not responding.\n"); } } ir->checkinit_id= 0; return; } #define SPEED B9600 /* ARGSUSED */ static int IrInitDevice (IrState *ir) { unsigned char dcmd[2]; struct termios terms; /*initialisation du port serie */ if (tcgetattr (ir->fd, &terms) == -1) { perror ( "Cant get device configuration for IR box"); return 0; } /* change the modes */ cfmakeraw (&terms); terms.c_lflag = 0; if (cfsetospeed (&terms, SPEED) < 0 ) { perror ("Can't set ouput speed for IR box"); return 0; } if (cfsetispeed (&terms, SPEED) < 0 ) { perror ("Can't set input speed for IR box"); return 0; } if (tcsetattr (ir->fd, TCSANOW, &terms) < 0) { perror ("Can't change device configuration for IR box"); return 0; } /* discard all unread or unwritten data */ tcflush (ir->fd, TCIOFLUSH ); fprintf (stderr, "IR box initialization in progress.....\n"); dcmd[0] = 'I'; dcmd[1] = 'R'; CHECKEDWRITE(ir->fd, dcmd, sizeof(dcmd)); ir->initialized= 1; if (ir->set_timeout) ir->checkinit_id = (*ir->set_timeout)(IrCheckInit, 1000, ir); return 1; } static void IrSetup (IrState *ir) { ir->ready=1; if (ir->cancel_timeout) (*ir->cancel_timeout)(ir->checkinit_id); ir->errorCount= 0; return; } static void IrDecodeInit (IrState *ir, char ch) { /* on doit attendre les caracteres "OK" */ switch (ir->state) { case 0: if ( ch == 'O' ) ir->state++; break; case 1: if ( ch == 'K' ) { ir->state = 0; fprintf (stderr,"IR box initialized OK.\n"); IrSetup (ir); ir->errorCount = 0; } else { fprintf (stderr, "unexpected char %d from IR box\n", ch); if (ir->errorCount++ > 5) { /* try to reinitialize */ fprintf (stderr,"Reinitializing IR box\n"); IrInitDevice (ir); } } break; default: fprintf (stderr,"Impossible state %d in ir_intr.\n", ir->state); ir->state = 0; break; } } static void IrDecodeFrame (IrState *ir, char ch) { /* trame normale de 6 carateres */ if (ir->state == 5) { /* fin de trame */ #ifdef DEBUG_RECEIVE fprintf (stdout, "Valeur BTVUP: %s\n",BTVUP); fprintf (stdout, "Valeur recue: %s\n",ir->value); #endif if (strcmp (ir->value,BTVUP) == 0) (*ir->callback)(ir, EVENT_BTN_PRESS_TV_VOL_UP, ir->value ); else (*ir->callback)(ir, EVENT_BTN_PRESS, ir->value ); ir->state = 0; } else { /* un caractere de la trame */ ir->value[(int)(ir->state++)] = ch; } } void IrIntr (IrState *ir) { unsigned char buf[128]; unsigned char *str; int len; register unsigned char ch; #ifdef DEBUG_RECEIVE int i; #endif if ((len = read(ir->fd, buf, sizeof(buf))) < 0) return; #ifdef DEBUG_RECEIVE fprintf(stderr, "receive %d bytes from device \n",len); for ( i = 0; i < len ; i ++ ) fprintf(stderr, "0x%02x ",buf[i]); fprintf(stderr, "\n"); #endif str = buf; while (len > 0) { ch = *str++; if (!ir->ready) /* on doit attendre les caracteres OK */ IrDecodeInit (ir,ch); else /* on doit attendre la fin de la trame ( 6 car ) */ IrDecodeFrame (ir, ch); len--; } return; } /* ARGSUSED */ IrState* IrOpen (const char *name ) { register IrState *ir; if ( !(ir = (IrState *)malloc(sizeof *ir))) return NULL; /* Set defaults -- totally arbitrary, my choice */ memset( ir, 0, sizeof *ir ) ; if ((ir->fd = open(name, O_RDWR/*|O_NONBLOCK*/)) < 0) { fprintf(stderr, "Couldn't open %s\n", name); free( ir ); return NULL; } ir->initialized = 1; ir->errorCount = 0; ir->state = 0; ir->callback = IrDefaultCallback; return ir; } /* ARGSUSED */ void IrClose (IrState *ir) { if (ir->checkinit_id) { if (ir->cancel_timeout ) (*ir->cancel_timeout)( ir->checkinit_id ); ir->checkinit_id= 0; } free (ir); } int IrGetFd (IrState *ir) { return ir->fd; }