Files
lcdproc/server/drivers/glkproto.c
T
ppokorny 95fa296a94 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.
2001-04-27 01:37:17 +00:00

515 lines
9.6 KiB
C

/* 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"
#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 );
}