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

glkproto.c

/*  This is the LCDproc driver for MatrixOrbital GLK Graphic Displays
                                         http://www.matrixorbital.com

    Copyright (C) 2001, Philip Pokorny
              2001, Chris Debenham
              2001, David Glaude

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 */

/* glkproto
 *
 * Routines to support downloading to the GLK
 */

#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/poll.h>
#include "glkproto.h"
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#define INLINE  inline
#undef MANUAL_FLOWCONTROL
#undef PAUSE_AFTER_STRINGS

/* Protocol values */
unsigned char  GLKCommand = 0xfe ;
unsigned char  GLKConfirm = 0x01 ;
unsigned char  GLKDeny = 0x08 ;

unsigned char  GLKBufferFull = 0xfe ;
unsigned char  GLKBufferEmpty = 0xff ;


/* glkopen
 *
 * Open and configure a serial port for communication with
 *   a Matrix Orbital module (GLK or otherwise)
 */
GLKDisplay *
   glkopen(
      char *  name,
      tcflag_t  speed
   )
{
   int  fd ;
   struct termios  new ;
   GLKDisplay *  retval ;

   if( name == NULL || speed == 0 ) {
      /* Invalid arguments */
      errno = EINVAL ;
      return( NULL );
   };

   fd = open( name, O_RDWR | O_NOCTTY );
   if( fd < 0 ) {
      return( NULL );
   };

   /* Get current settings */
   if( tcgetattr( fd, &new ) < 0 ) {
      int errsave = errno ;
      close( fd );
      errno = errsave ;
      return( NULL );
   };

   retval = malloc( sizeof( GLKDisplay ) );
   if( retval == NULL ) {
      errno = ENOMEM ;
      return( NULL );
   };
   retval->fd = fd ;
   retval->saved = new ;
   retval->ungetin = retval->ungetout = 0 ;
   retval->timeout = GLK_TIMEOUT ;
   retval->flow = GLKFLOW_OK ;

   /* Make new settings */

   /* We use RAW mode */
#ifdef HAVE_CFMAKERAW
   /* The easy way */
   cfmakeraw( &new );
#else
   /* The hard way */
   new.c_iflag &= ~( IGNBRK | BRKINT | PARMRK | ISTRIP
                     | INLCR | IGNCR | ICRNL | IXON );
   new.c_oflag &= ~OPOST;
   new.c_lflag &= ~( ECHO | ECHONL | ICANON | ISIG | IEXTEN );
   new.c_cflag &= ~( CSIZE | PARENB | CRTSCTS );
   new.c_cflag |= CS8 | CREAD | CLOCAL ;
#endif

   /* Set default MIN and TIMEOUT values */
   new.c_cc[VMIN] = 0 ;
   new.c_cc[VTIME] = GLK_TIMEOUT ;

   /* Set the out and in speeds */
   cfsetospeed( &new, speed );
   cfsetispeed( &new, B0 );  /* Input equals output speed */

   /* Configure the port */
   tcflush( fd, TCIOFLUSH );
   if( tcsetattr( fd, TCSANOW, &new ) < 0 ) {
      int errsave = errno ;
      glkclose( retval );
      errno = errsave ;
      return( NULL );
   };

   return( retval );
}

/* glktimeout
 *
 * Set the intercharacter timeout.  This determines how long
 *    a read will block (glkget) waiting on data.
 */
int
   glktimeout(
      GLKDisplay *  fd,
      int  timeout
   )
{
   struct termios  t ;

   if( timeout < 0 || timeout > 255 ) {
      errno = EINVAL ;   /* Invalid argument */
      return( 1 ); /* Failure */
   };

   if( tcgetattr( fd->fd, &t ) < 0 ) {
      return( 1 );
   };

   fd->timeout = timeout ;
   t.c_cc[VTIME] = timeout ;

   if( tcsetattr( fd->fd, TCSANOW, &t ) < 0 ) {
      return( 1 );
   };

   return( 0 );  /* Success */
}

/* glkflow
 *
 * Turn flow control processing for the port on/off and
 *    set the high/low water marks.  NOTE: This assumes
 *    the GLK12232-25SM which has a 96 byte buffer.
 */
#define GLKBUF (96)
int
   glkflow(
      GLKDisplay *  fd,
      int  full,
      int  empty
   )
{
   struct termios  t ;

   if( full > GLKBUF -1
       || empty > GLKBUF -1
       || full + empty > GLKBUF -1
     ) {
      errno = EINVAL ;
      return( 1 );
   };

   if( tcgetattr( fd->fd, &t ) < 0 ) {
      return( 1 );  /* Failure */
   };

   if( full < 0 || empty < 0 ) {
      /* Turn it off */
      glkputl( fd, GLKCommand, 0x3b, EOF );
      t.c_iflag &= ~(IXON | IXANY | IXOFF) ;
      t.c_cc[VSTART] = GLKBufferEmpty ;
      t.c_cc[VSTOP] = GLKBufferFull ;
      fd->flow = GLKFLOW_DISABLE ;
   } else {
      /* Turn it on */
      glkputl( fd, GLKCommand, 0x3a, full, empty, EOF );
      /* Control what we send */
      t.c_iflag |= IXON ;
      /* Only start on VSTART (IXANY),            *
       * Don't control what we receive (IXOFF)    */
      t.c_iflag &= ~(IXANY | IXOFF) ;
      t.c_cc[VSTART] = GLKBufferEmpty ;
      t.c_cc[VSTOP] = GLKBufferFull ;
      fd->flow = GLKFLOW_OK ;
   };

   if( tcsetattr( fd->fd, TCSANOW, &t ) < 0 ) {
      return( 1 );
   };

   return( 0 );  /* Success */
}

/* glkclose
 *
 * Close a serial port and restore settings if provided.
 */
int
   glkclose(
      GLKDisplay *  fd
   )
{
   int retval = 0 ;

   if( fd->fd < 0 ) {
      /* Probably already closed */
      return( 0 );  /* Success? */
   };

   /* Trash any unread data */
   tcflush( fd->fd, TCIFLUSH );

   /* Restore settings */
   tcsetattr( fd->fd, TCSANOW, &fd->saved );

   retval = close( fd->fd );

   fd->fd = -1 ;
   free( fd );

   return( retval );
}


/* glkput
 *
 * Send a character to the GLK
 */
int  INLINE
   glkput(
      GLKDisplay *  fd,
      int  c
   )
{
   unsigned char  val ;
   ssize_t  retval ;

   /* Output the byte */
   val = c ;
   retval = write( fd->fd, &val, 1 );

   /* retval <= 0  =>  FAILURE  =>  1
    * retval == 1  =>  SUCCESS  =>  0
    */
   return( retval <= 0 );
}

/* glkunget
 *
 * Put a read character "back" into the input queue.
 *    NOTE:  This queue holds characters between the
 *          low level glkget interface and the higher
 *          glkgetc interface.  In this case, the queue
 *          holds any characters read that were not
 *          flow control signals
 */
int
   glkunget(
      GLKDisplay *  fd,
      int  c
   )
{
   /* Is buffer already full? */
   if( ((fd->ungetin + 1) & (~UNGETBUFSIZE)) == fd->ungetout ) {
      return( 1 );  /* Failure */
   };

   fd->ungetbuf[fd->ungetin] = c ;
   fd->ungetin = (fd->ungetin + 1) & (~UNGETBUFSIZE) ;

   return( 0 );
}

/* glkget
 *
 * Read a character from the GLK
 */
int  INLINE
   glkget(
      GLKDisplay *  fd
   )
{
   unsigned char  c ;
   ssize_t  retval ;

   retval = read( fd->fd, &c, 1 );
   if( retval <= 0 ) {
      return( -1 );
   } else {
      return( (int) c );
   };
}

/* glkpoll
 *
 * Test to see if data is availble to read from the
 *    GLK.
 */
int
   glkpoll(
      GLKDisplay *  fd,
      int  timeout
   )
{
   struct pollfd  fds ;
   int  retval ;

   fds.fd = fd->fd ;
   fds.events = POLLIN ;
   fds.revents = 0 ;
   retval = poll( &fds, 1, timeout );
   if( retval < 0 ) {
      /* Some error.  Ignore it, and return "no data" */
      retval = 0 ;
   };

   return( retval );
}

/* glkgetc
 *
 * Read a character from the GLK.  This will generally be used
 *    to read keypad characters from the GLK.  RAW protocol
 *    implementations may want to use glkget.  glkget will return
 *    GLKBufferFull and GLKBufferEmpty whereas glkgetc wil NOT.
 */
int
   glkgetc(
      GLKDisplay *  fd
   )
{
   int  c ;

   /* Are there characters in the unget buffer? */
   if( fd->ungetin == fd->ungetout ) {
      /* Nope, so read one */
      for( ; ; ) {  /* loop until we get a non-flow control value */
         c = glkget( fd );

         if( fd->flow != GLKFLOW_DISABLE ) {
            if( c == GLKBufferFull ) {
               fd->flow = GLKFLOW_STOPPED ;
               continue ;
            } else if( c == GLKBufferEmpty ) {
               fd->flow = GLKFLOW_OK ;
               continue ;
            };
         };

         /* Must be what we're looking for */
         break ;
      };

   } else {

      /* Get one of the "ungotten" char's */
      c = fd->ungetbuf[fd->ungetout] ;
      fd->ungetout = (fd->ungetout + 1) & (~UNGETBUFSIZE) ;

   };

   return( c );
}

/* glkput_confirm
 *
 * Send a character to the GLK on fd with echo and
 *    confirmation of echo
 */
int
   glkput_confirm(
      GLKDisplay *  fd,
      int  c
   )
{
   int  echo ;

   /* Output the byte */
   if( glkput( fd, c ) ) {
      return( 1 );  /* Failure */
   }

   /* Look for the echo */
   echo = glkget( fd );
   if( echo < 0 ) {
      return( 1 );  /* Failure */
   };

   if( echo == c ) {
      glkput( fd, GLKConfirm );
      return( 0 ); /* Success */
   } else {
      glkput( fd, GLKDeny );
      return( 1 );
   }
}

/* glkputa_confirm
 *
 * Send an array of characters all with echo and
 *    confirmation.
 */
int
   glkputa_confirm(
      GLKDisplay *  fd,
      int  len,
      unsigned char *  str
   )
{
   int  i ;
   int  retval ;

   retval = 0 ;  /* Assume Success */
   for( i = len ; !retval && i ; ++str, --i ) {
      retval = glkput_confirm( fd, *str );
   };

   return( retval );
}

/* glkput_echo
 *
 * Send a character to the GLK on fd with echo expected
 */
int
   glkput_echo(
      GLKDisplay *  fd,
      int  c
   )
{
   int  echo ;

   /* Output the byte */
   if( glkput( fd, c ) ) {
      return( 1 );  /* Failure */
   }

   /* Look for the echo */
   echo = glkget( fd );
   if( echo < 0 ) {
      return( 1 );  /* Failure */
   };

   if( echo == c ) {
      return( 0 );  /* Success */
   } else {
      return( 1 );  /* Failure */
   }
}

/* glkputl
 *
 * Send a list of characters to the GLK.  The list is
 *    terminated by an EOF valued argument.
 */
int
   glkputl(
      GLKDisplay *  fd,
      ...
   )
{
   va_list  ap ;
   int  value ;
   int  retval ;

   va_start( ap, fd );
   retval = 0 ;  /* Success */
   value = va_arg( ap, int );

   while( !retval && value != EOF ) {
      retval = glkput( fd, value );
      value = va_arg( ap, int );
   };

   return( retval );
}

/* glkputs
 *
 * Send a null terminated string of characters to the
 *    GLK.
 */
int
   glkputs(
      GLKDisplay *  fd,
      char *  str
   )
{
   register  char *  p = str ;
   int  retval ;

   retval = 0 ;  /* Success */
   for( p = str ; !retval && *p ; ++p ) {
      retval = glkput( fd, *p );
   };

   return( retval );
}

/* glkputa
 *
 * Send an array of characters.
 */
int
   glkputa(
      GLKDisplay *  fd,
      int  len,
      unsigned char *  str
   )
{
   register  unsigned char *  p = str ;
   int  i ;
   int  retval ;

   retval = 0 ;  /* Assume Success */
   for( i = len ; !retval && i ; ++p, --i ) {
      retval = glkput( fd, *p );
   };

   return( retval );
}

Generated by  Doxygen 1.6.0   Back to index