From 95fa296a948dffefb0b3532470377f7347a2c639 Mon Sep 17 00:00:00 2001 From: ppokorny Date: Fri, 27 Apr 2001 01:37:17 +0000 Subject: [PATCH] This is the support library for glk.c (the GLK driver). It manages the connection to the GLK and sets up the serial port correctly. --- server/drivers/glkproto.c | 514 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 514 insertions(+) create mode 100644 server/drivers/glkproto.c diff --git a/server/drivers/glkproto.c b/server/drivers/glkproto.c new file mode 100644 index 0000000..e4b06a8 --- /dev/null +++ b/server/drivers/glkproto.c @@ -0,0 +1,514 @@ +/* glkproto + * + * Routines to support downloading to the GLK + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "glkproto.h" + +#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 & CBAUD) == 0 ) { + /* Invalid arguments */ + errno = EINVAL ; + return( NULL ); + }; + + fd = open( name, O_RDWR | O_NOCTTY | O_SYNC ); + 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, and select two stop bits + * to slow things down 10%. Therefore, we IGNORE + * framing errors incase the module transmits back + * to back with only one stop bit. + */ + cfmakeraw( &new ); /* Sets CS8 */ + new.c_iflag |= IGNPAR ; /* Ignore framing/parity errors */ + new.c_cflag &= ~(CBAUD | CRTSCTS ) ; + new.c_cflag |= speed | CREAD | CLOCAL | CSTOPB ; + new.c_cc[VMIN] = 0 ; + new.c_cc[VTIME] = GLK_TIMEOUT ; + + /* 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 ); +}