/* 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);
}
va_end(ap);
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);
}
syntax highlighted by Code2HTML, v. 0.9.1