serial port side. Also fixed problems with ioperm on the parallel port side. Added --waittime option to the server to specify how long the server should leave a screen up before changing to the next screen.
529 lines
9.9 KiB
C
529 lines
9.9 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"
|
|
#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 );
|
|
}
|