/*
 *	sermond.c -- thingie to talk to my serial device :)
 *	Copyright (C) 2002-2004 Fred Barnes <frmb2@ukc.ac.uk>
 *
 *	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
 *	(at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*{{{  includes, defines, etc.*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/mman.h>


#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif

#define VERSION "0.1.0"

#define TCRATIO 10.0
#define TCIDELTA 5.0
#define TCODELTA 0.0

#define DEFAULT_STATUS_FILE "/tmp/temp"

#define MAXRS422_DEVICES	128
/*}}}*/
/*{{{  statics, typedecls, etc.*/
static char *progname = NULL;
static int glob_escape = 0;	/* interpretation of escape sequences */



typedef struct {
	char *device;		/* "/dev/tty.." */
	int fd;			/* descriptor of above */
	char *outfile;		/* output file */
	int verbose;		/* be verbose ? */
	int interactive;	/* interactive mode (forces verbose) */
	int stdin_fd;		/* descriptor of stdin in interactive mode */
	int no_timeout;		/* disable device timeout (needed for non-asynchronous devices) */

	struct termios s_ser;	/* saved serial-line state */
	struct termios s_kyb;	/* saved keyboard state */

	int rs422_active[MAXRS422_DEVICES];	/* active states of devices (0 = not present, 1 = idle, 2 = waiting for pong, 3 = waiting for temp) */
	int rs422_waitdev;			/* device number which has some stuff for us (-1 = not waiting) */
	int rs422_runstate;			/* run state, 0 = not active, 1 = scanning for active devices, 2 = scanning for temperature, 3 = write out data */
	int rs422_mindev;			/* low device range */
	int rs422_maxdev;			/* high device range */

	int arg_mindev;		/* for non-interactive mode, command-line args for -n and -m */
	int arg_maxdev;

	int tclastvals[MAXRS422_DEVICES];	/* actual values read */
	double tcratio[MAXRS422_DEVICES];	/* temperature ratios (nominal 10) */
	double tcdelta[MAXRS422_DEVICES];	/* pre-conversion delta (nominal 0) */
} sermon_t;
/*}}}*/

#define microdelay(X) do { struct timeval p__tv = {((X) / 1000000), ((X) % 1000000)}; select (0, NULL, NULL, NULL, &p__tv); } while (0);

/*{{{  static int decode_hex_byte (char b1, char b2, unsigned char *tptr)*/
/*
 *	turns 2 hex characters into an unsigned byte.  returns 0 on success, -1 on error
 */
static int decode_hex_byte (char b1, char b2, unsigned char *tptr)
{
	*tptr = 0;
	if ((b1 >= '0') && (b1 <= '9')) {
		*tptr = ((b1 - '0') << 4);
	} else if ((b1 >= 'a') && (b1 <= 'f')) {
		*tptr = (((b1 - 'a') + 10) << 4);
	} else if ((b1 >= 'A') && (b1 <= 'F')) {
		*tptr = (((b1 - 'A') + 10) << 4);
	} else {
		return -1;
	}
	if ((b2 >= '0') && (b2 <= '9')) {
		*tptr |= ((b2 - '0') & 0x0f);
	} else if ((b2 >= 'a') && (b2 <= 'f')) {
		*tptr |= (((b2 - 'a') + 10) & 0x0f);
	} else if ((b2 >= 'A') && (b2 <= 'F')) {
		*tptr |= (((b2 - 'A') + 10) & 0x0f);
	} else {
		return -1;
	}
	return 0;
}
/*}}}*/
/*{{{  static void show_help (FILE *stream)*/
/*
 *	shows the help/usage-info
 */
static void show_help (FILE *stream)
{
	fprintf (stream, "%s -- serial device monitoring program (for the pinboard contraption!)\n", progname);
	fprintf (stream, "Usage: %s [options] <serial-device>\n", progname);
	fprintf (stream, "where options are:\n");
	fprintf (stream, "\t-h | --help            show this help\n");
	fprintf (stream, "\t-V | --version         show version and exit\n");
	fprintf (stream, "\t-v | --verbose         be verbose (prevents detatch from terminal)\n");
	fprintf (stream, "\t-o | --output  <file>  set output filename (default: " DEFAULT_STATUS_FILE ")\n");
	fprintf (stream, "\t-i | --interactive     set interactive mode (forces verbose)\n");
	fprintf (stream, "\t-t | --no-timeout      no device timeouts (for non-asynchronous things)\n");
	fprintf (stream, "\t-n | --min  <num>      minimum device ID on bus\n");
	fprintf (stream, "\t-m | --max  <num>      maximum device ID on bus\n");
	fprintf (stream, "\t-c <id> <tcr> <tcd>    set TCRATIO and TCDELTA for device <id>\n");
	return;
}
/*}}}*/
/*{{{  static int parse_uint16hex (char *ch)*/
/*
 *	parses 4 hex digits to form an unsigned 16-bit number.  returns in a 32-bit word
 */
static int parse_uint16hex (char *ch)
{
	int w = 0;
	unsigned char v;

	if (decode_hex_byte (ch[0], ch[1], &v)) {
		return 0;
	}
	w |= v;
	w <<= 8;
	if (decode_hex_byte (ch[2], ch[3], &v)) {
		return 0;
	}
	w |= v;
	return w;
}
/*}}}*/
/*{{{  static void show_version (FILE *stream)*/
/*
 *	shows the version
 */
static void show_version (FILE *stream)
{
	fprintf (stream, "sermond " VERSION "\n");
	return;
}
/*}}}*/
/*{{{  static int set_serial_state (sermon_t *sminfo)*/
/*
 *	sets the serial port speed
 */
static int set_serial_state (sermon_t *sminfo)
{
	struct termios term;
	speed_t baud;

	tcgetattr (sminfo->fd, &sminfo->s_ser);
	if (tcgetattr (sminfo->fd, &term) < 0) {
		return -1;
	}
	baud = B19200;
	if (cfsetospeed (&term, baud) < 0) {
		return -1;
	}
	if (cfsetispeed (&term, baud) < 0) {
		return -1;
	}
	/* cfmakeraw() */
	term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
	term.c_oflag &= ~OPOST;
	term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
	term.c_cflag &= ~(CSIZE|PARENB);
	term.c_cflag |= CS8;

	if (tcsetattr (sminfo->fd, TCSAFLUSH, &term) < 0) {
		return -1;
	}
	return 0;
}
/*}}}*/
/*{{{  static int restore_serial_state (sermon_t *sminfo) */
/*
 *	restores the serial-line state
 */
static int restore_serial_state (sermon_t *sminfo)
{
	return tcsetattr (sminfo->fd, TCSAFLUSH, &sminfo->s_ser);
}
/*}}}*/
/*{{{  static int set_stdin_for_interactive (sermon_t *sminfo) */
/*
 *	sticks keyboard in raw mode
 */
static int set_stdin_for_interactive (sermon_t *sminfo)
{
	struct termios term;

	if (!isatty (sminfo->stdin_fd)) {
		/* um, better not go interactive then.. :) */
		return -1;
	}
	tcgetattr (sminfo->stdin_fd, &sminfo->s_kyb);
	if (tcgetattr (sminfo->stdin_fd, &term) < 0) {
		return -1;
	}
	/* cfmakeraw() */
	term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
	term.c_oflag &= ~OPOST;
	term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
	term.c_cflag &= ~(CSIZE|PARENB);
	term.c_cflag |= CS8;

	if (tcsetattr (sminfo->stdin_fd, TCSANOW, &term) < 0) {
		return -1;
	}
	return 0;
}
/*}}}*/
/*{{{  static int restore_stdin_from_interactive (sermon_t *sminfo)*/
/*
 *	returns keyboard to normal mode
 */
static int restore_stdin_from_interactive (sermon_t *sminfo)
{
	return tcsetattr (sminfo->stdin_fd, TCSANOW, &sminfo->s_kyb);
}
/*}}}*/
/*{{{  static int serial_write (int fd, unsigned char *buffer, int len, int retry, int interval)*/
/*
 *	writes stuff to the serial port
 */
static int serial_write (int fd, unsigned char *buffer, int len, int retry, int interval)
{
	int gone = 0;
	int tries = retry;

	while (gone < len) {
		int n = write (fd, buffer + gone, len - gone);

		if ((n < 0) && (errno == EAGAIN)) {
			if (!tries) {
				return -1;
			}
			microdelay (interval);
			tries--;
			continue;
		} else if (n < 0) {
			return -1;
		}
		tries = retry;
		gone += n;
	}
	return gone;
}
/*}}}*/
/*{{{  static int serial_write_line (int fd, unsigned char *buffer, int len, int retry, int interval)*/
/*
 *	writes stuff to the serial port, but frames it first
 *	sensitive to glob_escape
 */
static int serial_write_line (int fd, unsigned char *buffer, int len, int retry, int interval)
{
	static unsigned char lbuf[128];
	int ilen, r;

	sprintf (lbuf, "%c%c:", '0' + ((len + 3) / 10), '0' + ((len + 3) % 10));
	ilen = 3;
	if (glob_escape) {
		unsigned char *ch = lbuf + ilen;
		unsigned char *dh = buffer;
		int vlen = len;

		while (vlen) {
			if (*dh == '\\') {
				/* expect hex digit pair */
				if (dh[1] == '\\') {
					/* really a backslash */
					*ch = '\\';
					ch++, ilen++;
					dh += 2;
					vlen -= 2;
				} else if (decode_hex_byte (dh[1], dh[2], ch)) {
					/* oops */
					printf ("something wrong in an escape sequence there..\r\n");
					return -1;
				} else {
					ch++, ilen++;
					dh += 3;
					vlen -= 3;
				}
			} else {
				*ch = *dh;
				ch++, dh++, ilen++;
				vlen--;
			}
		}
	} else {
		memcpy (lbuf + ilen, buffer, len);
		ilen += len;
	}
	lbuf[ilen] = 0x0d;
	ilen++;
	lbuf[ilen] = '\0';
	r = serial_write (fd, lbuf, ilen, retry, interval);
	if (r == ilen) {
		return len;
	}
	return r;
}
/*}}}*/
/*{{{  static int serial_read_line (int fd, unsigned char *buffer, int maxlen, int retry, int interval)*/
/*
 *	reads a whole line (using protocol) from the serial port
 */
static int serial_read_line (int fd, unsigned char *buffer, int maxlen, int retry, int interval)
{
	int got = 0;
	int tries = retry * 2;
	int n = 0, m = 0;
	char lbuf[3];

	/* device sends a 2-byte base-10 count first, followed by a colon */
	while (tries) {
		n = read (fd, lbuf, 3);
		if ((n < 0) && (errno == EAGAIN)) {
			tries--;
			microdelay (100000);
		} else if (n != 3) {
			return -1;
		} else {
			break;
		}
	}
	if (!tries && (n != 3)) {
		return -1;
	}
	if ((lbuf[0] < '0') || (lbuf[0] > '9') || (lbuf[1] < '0') || (lbuf[1] > '9') || (lbuf[2] != ':')) {
		return -1;
	}
	tries = retry;
	n = ((lbuf[0] - '0') * 10) + (lbuf[1] - '0');
	n -= 3;						/* already have these 3 */

	while (got < n) {
		m = read (fd, buffer + got, n - got);
		if ((m < 0) && (errno == EAGAIN)) {
			if (!tries) {
				return -1;
			}
			microdelay (interval);
			tries--;
			continue;
		} else if (n < 0) {
			return -1;
		}
		tries = retry;
		got += m;
	}
	if (got != n) {
		/* shouldn't ever happen..! */
		return -1;
	}

	/* next character should be 0x0d */
	tries = retry;
	while (tries) {
		m = read (fd, lbuf, 1);
		if ((m < 0) && (errno == EAGAIN)) {
			tries--;
			microdelay (interval);
		} else if (m < 0) {
			return -1;
		} else {
			break;
		}
	}
	if (!tries && (m < 0)) {
		return -1;
	}
	if (lbuf[0] != 0x0d) {
		return -1;
	}
	return n;
}
/*}}}*/
/*{{{  static int stock_initialisation (sermon_t *sminfo)*/
/*
 *	initialises the device
 */
static int stock_initialisation (sermon_t *sminfo)
{
	int n;
	static unsigned char pbuf[128];
	int cleft = 10;

	if ((n = serial_write (sminfo->fd, "XXX", 3, 20, 1000)) != 3) {
		return -1;
	}
	/* oki, read response..  finishes with a blank line */
	for (;;) {
		if (!cleft || (n = serial_read_line (sminfo->fd, pbuf, 127, 10, 1000)) < 0) {
			/* wait a moment */
			microdelay (10000);
			serial_write (sminfo->fd, "\x0d", 1, 20, 1000);
			microdelay (10000);
			serial_read_line (sminfo->fd, pbuf, 127, 20, 1000);
			microdelay (10000);
			/* send reset command */
			if (serial_write (sminfo->fd, "05:R:\x0d", 6, 20, 1000) == 6) {
				fprintf (stderr, "... resetting device ...\n");
				microdelay (2000000);
			}
			if ((n = serial_write (sminfo->fd, "XXX", 3, 20, 1000)) != 3) {
				return -1;
			}
			cleft = 10;
			continue;	/* in for() */
		}
		if (n == 0) {
			if (sminfo->verbose) {
				fprintf (stderr, "dev-end-text.\n");
			}
			break;
		}
		pbuf[n] = '\0';
		if (sminfo->verbose) {
			fprintf (stderr, "dev: %s\n", pbuf);
		}
		cleft--;
	}

	/* data should just sort of pop out from here on :) */
	return 0;
}
/*}}}*/
/*{{{  static int program_remote_flash (sermon_t *sminfo, char *fname)*/
/*
 *	programs flash from the remote programmer.
 */
static int program_remote_flash (sermon_t *sminfo, char *fname)
{
	struct stat st_buf;
	int fd;
	unsigned char *mapped_addr;
	int mapped_size;
	unsigned int i, v;
	static unsigned char pbuf[128];
	int retry = 20;

	if (stat (fname, &st_buf) < 0) {
		fprintf (stderr, "%s: failed to stat %s\n", progname, fname);
		return -1;
	}
	fd = open (fname, O_RDONLY);
	if (fd < 0) {
		fprintf (stderr, "%s: failed to open %s\n", progname, fname);
		return -1;
	}
	mapped_size = st_buf.st_size;
	if ((mapped_addr = (unsigned char *)mmap ((void *)0, mapped_size, PROT_READ, MAP_SHARED, fd, 0)) == (unsigned char *)MAP_FAILED) {
		fprintf (stderr, "%s: failed to mmap %s\n", progname, fname);
		close (fd);
		return -1;
	}
	
	/* oki, send data at the device */
	v = 0;
	retry = 20;
	for (i=0; i<(mapped_size / 4); i++) {
		static unsigned char xbuf[16];
		int n;

		microdelay (10000);
		xbuf[0] = 'P';
		xbuf[1] = ':';
		xbuf[2] = 'F';
		/* modified 24/03/2004 -- need high byte.. */
		xbuf[3] = (unsigned char)((i >> 8) & 0xff);
		xbuf[4] = (unsigned char)(i & 0xff);
		xbuf[5] = mapped_addr[v+0];
		xbuf[6] = mapped_addr[v+1];
		xbuf[7] = mapped_addr[v+2];
		xbuf[8] = mapped_addr[v+3];
		v += 4;
		xbuf[9] = '\0';
		if (serial_write_line (sminfo->fd, xbuf, 9, 20, 2000) != 8) {
			printf ("failed to write command for block %d.\r\n", i);
			goto get_out_of_here;
		}
		do {
			n = serial_read_line (sminfo->fd, pbuf, 127, 10, 50000);
			if (n < 0) {
				printf ("error reading from device for block %d.\r\n", i);
				goto get_out_of_here;
			}
			pbuf[n] = '\0';
			if (!strncmp (pbuf, "P:FP:OK", 7)) {
				printf ("block %d OK.  [%s]\r\n", i, pbuf);
				retry = 20;
				break;	/* from do/while() */
			} else if (!strncmp (pbuf, "P:FP:FAIL", 9)) {
				retry--;
				if (!retry) {
					printf ("block %d FAILED [%s].  giving up.\r\n", i, pbuf);
					goto get_out_of_here;
				} else {
					printf ("block %d FAILED [%s].  retrying..\r\n", i, pbuf);
				}
				i--;
				v -= 4;
				break;	/* from do/while() */
			} else {
				fprintf (stderr, "dev: %s\r\n", pbuf);
			}
		} while (1);
	}
	while (v < mapped_size) {
		static unsigned char xbuf[16];
		int n;

		microdelay (10000);
		xbuf[0] = 'P';
		xbuf[1] = ':';
		xbuf[2] = 'F';
		/* modified 24/03/2004 -- need high byte.. */
		xbuf[3] = (unsigned char)((i >> 8) & 0xff);
		xbuf[4] = (unsigned char)(i & 0xff);
		xbuf[5] = mapped_addr[v+0];
		xbuf[6] = mapped_addr[v+1];
		v += 2;
		if (v < mapped_size) {
			xbuf[7] = mapped_addr[v+0];
			xbuf[8] = mapped_addr[v+1];
		} else {
			xbuf[7] = 0x00;
			xbuf[8] = 0x00;
		}
		v += 2;
		xbuf[9] = '\0';
		if (serial_write_line (sminfo->fd, xbuf, 9, 20, 2000) != 8) {
			goto get_out_of_here;
		}
		do {
			n = serial_read_line (sminfo->fd, pbuf, 127, 20, 1024);
			if (n < 0) {
				goto get_out_of_here;
			}
			pbuf[n] = '\0';
			if (!strncmp (pbuf, "P:FP:OK", 6)) {
				printf ("block %d OK. [%s]\r\n", i, pbuf);
				retry = 20;
				break;	/* from do/while() */
			} else if (!strncmp (pbuf, "P:FP:FAIL", 8)) {
				retry--;
				if (!retry) {
					printf ("block %d FAILED [%s].  giving up.\r\n", i, pbuf);
					goto get_out_of_here;
				} else {
					printf ("block %d FAILED [%s].  retrying..\r\n", i, pbuf);
				}
				v-=4;
				break;	/* from do/while() */
			} else {
				fprintf (stderr, "dev: %s\r\n", pbuf);
			}
		} while (1);
	}
	printf ("yay, done :)\r\n");

	munmap ((void *)mapped_addr, mapped_size);
	close (fd);
	return 0;
get_out_of_here:
	munmap ((void *)mapped_addr, mapped_size);
	close (fd);
	return -1;
}
/*}}}*/
/*{{{  static int program_remote_eeprom (sermon_t *sminfo, char *fname)*/
/*
 *	programs eeprom through the remote programmer
 */
static int program_remote_eeprom (sermon_t *sminfo, char *fname)
{
	struct stat st_buf;
	int fd;
	unsigned char *mapped_addr;
	int mapped_size;
	unsigned int i, v;
	static unsigned char pbuf[128];
	int retry = 20;

	if (stat (fname, &st_buf) < 0) {
		fprintf (stderr, "%s: failed to stat %s\n", progname, fname);
		return -1;
	}
	fd = open (fname, O_RDONLY);
	if (fd < 0) {
		fprintf (stderr, "%s: failed to open %s\n", progname, fname);
		return -1;
	}
	mapped_size = st_buf.st_size;
	if ((mapped_addr = (unsigned char *)mmap ((void *)0, mapped_size, PROT_READ, MAP_SHARED, fd, 0)) == (unsigned char *)MAP_FAILED) {
		fprintf (stderr, "%s: failed to mmap %s\n", progname, fname);
		close (fd);
		return -1;
	}
	
	/* oki, send data at the device */
	v = 0;
	retry = 20;
	for (i=0; i<(mapped_size / 4); i++) {
		static unsigned char xbuf[16];
		int n;

		microdelay (10000);
		xbuf[0] = 'P';
		xbuf[1] = ':';
		xbuf[2] = 'E';
		xbuf[3] = (unsigned char)i;
		xbuf[4] = mapped_addr[v+0];
		xbuf[5] = mapped_addr[v+1];
		xbuf[6] = mapped_addr[v+2];
		xbuf[7] = mapped_addr[v+3];
		v += 4;
		xbuf[8] = '\0';
		if (serial_write_line (sminfo->fd, xbuf, 8, 20, 2000) != 8) {
			printf ("failed to write command for block %d.\r\n", i);
			goto get_out_of_here;
		}
		do {
			n = serial_read_line (sminfo->fd, pbuf, 127, 10, 50000);
			if (n < 0) {
				printf ("error reading from device for block %d.\r\n", i);
				goto get_out_of_here;
			}
			pbuf[n] = '\0';
			if (!strncmp (pbuf, "P:EP:OK", 7)) {
				printf ("block %d OK.  [%s]\r\n", i, pbuf);
				retry = 20;
				break;	/* from do/while() */
			} else if (!strncmp (pbuf, "P:EP:FAIL", 9)) {
				retry--;
				if (!retry) {
					printf ("block %d FAILED [%s].  giving up.\r\n", i, pbuf);
					goto get_out_of_here;
				} else {
					printf ("block %d FAILED [%s].  retrying..\r\n", i, pbuf);
				}
				i--;
				v -= 4;
				break;	/* from do/while() */
			} else {
				fprintf (stderr, "dev: %s\r\n", pbuf);
			}
		} while (1);
	}
	while (v < mapped_size) {
		static unsigned char xbuf[16];
		int n;

		microdelay (10000);
		xbuf[0] = 'P';
		xbuf[1] = ':';
		xbuf[2] = 'E';
		xbuf[3] = (unsigned char)i;
		xbuf[4] = mapped_addr[v+0];
		xbuf[5] = mapped_addr[v+1];
		v += 2;
		if (v < mapped_size) {
			xbuf[6] = mapped_addr[v+0];
			xbuf[7] = mapped_addr[v+1];
		} else {
			xbuf[6] = 0x00;
			xbuf[7] = 0x00;
		}
		v += 2;
		xbuf[8] = '\0';
		if (serial_write_line (sminfo->fd, xbuf, 8, 20, 2000) != 8) {
			goto get_out_of_here;
		}
		do {
			n = serial_read_line (sminfo->fd, pbuf, 127, 20, 1024);
			if (n < 0) {
				goto get_out_of_here;
			}
			pbuf[n] = '\0';
			if (!strncmp (pbuf, "P:EP:OK", 6)) {
				printf ("block %d OK. [%s]\r\n", i, pbuf);
				retry = 20;
				break;	/* from do/while() */
			} else if (!strncmp (pbuf, "P:EP:FAIL", 8)) {
				retry--;
				if (!retry) {
					printf ("block %d FAILED [%s].  giving up.\r\n", i, pbuf);
					goto get_out_of_here;
				} else {
					printf ("block %d FAILED [%s].  retrying..\r\n", i, pbuf);
				}
				v-=4;
				break;	/* from do/while() */
			} else {
				fprintf (stderr, "dev: %s\r\n", pbuf);
			}
		} while (1);
	}
	printf ("yay, done :)\r\n");

	munmap ((void *)mapped_addr, mapped_size);
	close (fd);
	return 0;
get_out_of_here:
	munmap ((void *)mapped_addr, mapped_size);
	close (fd);
	return -1;

	return -1;
}
/*}}}*/
/*{{{  int main (int argc, char **argv)*/
/*
 *	start here
 */
int main (int argc, char **argv)
{
	int i;
	char **walk;
	sermon_t sminfo;
	fd_set read_set;
	struct timeval tv;
	int high_fd;
	int last_tval = 0;
	int exitcode = EXIT_SUCCESS;
	int longfailcount = 10;

	for (progname = *argv + (strlen (*argv) - 1); (progname > *argv) && (*progname != '/'); progname--);
	progname++;

	sminfo.device = NULL;
	sminfo.fd = -1;
	sminfo.outfile = NULL;
	sminfo.verbose = 0;
	sminfo.interactive = 0;
	sminfo.stdin_fd = -1;
	sminfo.no_timeout = 0;

	sminfo.arg_mindev = 1;
	sminfo.arg_maxdev = MAXRS422_DEVICES;

	for (i=0; i<MAXRS422_DEVICES; i++) {
		sminfo.rs422_active[i] = 0;
		sminfo.tcratio[i] = 10.0;
		sminfo.tcdelta[i] = 0.0;
		sminfo.tclastvals[i] = 0;
	}
	sminfo.rs422_waitdev = -1;
	sminfo.rs422_runstate = 0;
	sminfo.rs422_mindev = 1;
	sminfo.rs422_maxdev = MAXRS422_DEVICES;

	/*{{{  process process arguments (really, the command-line parameters)*/
	for (i=1, walk = argv+1; (i < argc) && *walk; i++, walk++) {
		if (!strcmp (*walk, "--help") || !strcmp (*walk, "-h")) {
			show_help (stdout);
			exit (EXIT_SUCCESS);
		}
		if (!strcmp (*walk, "--version") || !strcmp (*walk, "-V")) {
			show_version (stdout);
			exit (EXIT_SUCCESS);
		}
		if (!strcmp (*walk, "--verbose") || !strcmp (*walk, "-v")) {
			sminfo.verbose = 1;
			continue;
		}
		if (!strcmp (*walk, "-c") || !strcmp (*walk, "--character")) {
			int isneg = 0;
			int devid = 0;
			char *arg;

			walk++, i++;
			if ((i == argc) || !*walk) {
				fprintf (stderr, "%s: option %s expects three arguments.  --help for usage information.\n", progname, walk[-1]);
				exit (EXIT_FAILURE);
			}
			if (sscanf (*walk, "%d", &devid) != 1) {
				fprintf (stderr, "%s: option %s expects first argument to be an integer.  --help for usage information.\n", progname, walk[-1]);
				exit (EXIT_FAILURE);
			}
			if ((devid < 0) || (devid >= MAXRS422_DEVICES)) {
				fprintf (stderr, "%s: option %s expects first argument to a valid device ID.  --help for usage information.\n", progname, walk[-1]);
				exit (EXIT_FAILURE);
			}

			walk++, i++;
			if ((i == argc) || !*walk) {
				fprintf (stderr, "%s: option %s expects three arguments.  --help for usage information.\n", progname, walk[-2]);
				exit (EXIT_FAILURE);
			}
			arg = *walk;
			if (arg[0] == '-') {
				arg++, isneg = 1;
			}
			if (sscanf (arg, "%lf", &sminfo.tcdelta[devid]) != 1) {
				fprintf (stderr, "%s: option %s expects second argument to be a double.  --help for usage information.\n", progname, walk[-2]);
				exit (EXIT_FAILURE);
			}
			if (isneg) {
				sminfo.tcdelta[devid] *= -1.0;
			}

			walk++, i++;
			if ((i == argc) || !*walk) {
				fprintf (stderr, "%s: option %s expects three arguments.  --help for usage information.\n", progname, walk[-3]);
				exit (EXIT_FAILURE);
			}
			arg = *walk;
			if (sscanf (arg, "%lf", &sminfo.tcratio[devid]) != 1) {
				fprintf (stderr, "%s: option %s expects third argument to be a double.  --help for usage information.\n", progname, walk[-3]);
				exit (EXIT_FAILURE);
			}

			if (sminfo.verbose) {
				fprintf (stderr, "%s: setting device %d tcdelta to %.1f, tcratio to %.1f\n", progname, devid, sminfo.tcdelta[devid], sminfo.tcratio[devid]);
			}
			continue;
		}
		if (!strcmp (*walk, "-o") || !strcmp (*walk, "--output")) {
			int fd;

			walk++, i++;
			if ((i == argc) || !*walk) {
				fprintf (stderr, "%s: option %s expects an argument.  --help for usage information.\n", progname, walk[-1]);
				exit (EXIT_FAILURE);
			}
			fd = open (*walk, O_CREAT | O_RDWR, 0644);
			if (fd < 0) {
				fprintf (stderr, "%s: cannot write to %s\n", progname, *walk);
				exit (EXIT_FAILURE);
			}
			close (fd);
			sminfo.outfile = *walk;
			continue;
		}
		if (!strcmp (*walk, "-i") || !strcmp (*walk, "--interactive")) {
			sminfo.interactive = 1;
			sminfo.verbose = 1;
			continue;
		}
		if (!strcmp (*walk, "-t") || !strcmp (*walk, "--no-timeout")) {
			sminfo.no_timeout = 1;
			continue;
		}
		if (!strcmp (*walk, "-n") || !strcmp (*walk, "--min")) {
			walk++, i++;
			if ((i == argc) || !*walk) {
				fprintf (stderr, "%s: option %s expects an argument.  --help for usage information.\n", progname, walk[-1]);
				exit (EXIT_FAILURE);
			}
			if (sscanf (*walk, "%d", &sminfo.arg_mindev) != 1) {
				fprintf (stderr, "%s: option %s expects an integer argument.  --help for usage information.\n", progname, walk[-1]);
				exit (EXIT_FAILURE);
			}
			if ((sminfo.arg_mindev < 1) || (sminfo.arg_mindev > 127)) {
				fprintf (stderr, "%s: option %s expects an integer argument in the range [1..127].  --help for usage information.\n", progname, walk[-1]);
				exit (EXIT_FAILURE);
			}
			continue;
		}
		if (!strcmp (*walk, "-m") || !strcmp (*walk, "--max")) {
			walk++, i++;
			if ((i == argc) || !*walk) {
				fprintf (stderr, "%s: option %s expects an argument.  --help for usage information.\n", progname, walk[-1]);
				exit (EXIT_FAILURE);
			}
			if (sscanf (*walk, "%d", &sminfo.arg_maxdev) != 1) {
				fprintf (stderr, "%s: option %s expects an integer argument.  --help for usage information.\n", progname, walk[-1]);
				exit (EXIT_FAILURE);
			}
			if ((sminfo.arg_maxdev < 1) || (sminfo.arg_maxdev > 127)) {
				fprintf (stderr, "%s: option %s expects an integer argument in the range [1..127].  --help for usage information.\n", progname, walk[-1]);
				exit (EXIT_FAILURE);
			}
			continue;
		}
		if (!sminfo.device) {
			if (access (*walk, R_OK | W_OK)) {
				fprintf (stderr, "%s: cannot open %s for read/write\n", progname, *walk);
				exit (EXIT_FAILURE);
			}
			sminfo.device = *walk;
			sminfo.fd = open (sminfo.device, O_RDWR | O_NONBLOCK);
			if (sminfo.fd < 0) {
				fprintf (stderr, "%s: cannot open %s for read/write\n", progname, *walk);
				exit (EXIT_FAILURE);
			}
			continue;
		}
		fprintf (stderr, "%s: warning: unrecognised option %s\n", progname, *walk);
	}
	/*}}}*/
	/*{{{  check sanity and open device*/
	if (sminfo.arg_mindev > sminfo.arg_maxdev) {
		int tmp = sminfo.arg_mindev;

		if (sminfo.verbose) {
			fprintf (stderr, "%s: mindev/maxdev confusion, swapping them round\n", progname);
		}
		sminfo.arg_mindev = sminfo.arg_maxdev;
		sminfo.arg_maxdev = tmp;
	}
	if (!sminfo.device) {
		show_help (stderr);
		exit (EXIT_FAILURE);
	}
	if (!sminfo.outfile) {
		int fd;

		sminfo.outfile = (char *)malloc (strlen (DEFAULT_STATUS_FILE) + 1);
		if (!sminfo.outfile) {
			fprintf (stderr, "%s: oom!\n", progname);
			if (sminfo.fd >= 0) {
				close (sminfo.fd);
			}
			exit (EXIT_FAILURE);
		}
		strcpy (sminfo.outfile, DEFAULT_STATUS_FILE);
		fd = open (sminfo.outfile, O_CREAT | O_RDWR, 0644);
		if (fd < 0) {
			fprintf (stderr, "%s: cannot write to %s\n", progname, sminfo.outfile);
			exit (EXIT_FAILURE);
		}
		close (fd);
	}
	/*}}}*/
	/*{{{  set serial line up and pause*/
	/* oki, device open, set to 19200-8-N-1 */
	/* FIXME: make this an option from the command line */

	if (set_serial_state (&sminfo)) {
		fprintf (stderr, "%s: unable to set serial state to 2400-8-N-1\n", progname);
		exit (EXIT_FAILURE);
	}
	if (sminfo.verbose) {
		fprintf (stderr, "%s: serial device %s set to 19200-8-N-1.  Pausing for things to settle.\n", progname, sminfo.device);
	}
	microdelay (100000);
	/*}}}*/
	/*{{{  initialisation*/
	/* run through some stock things */
	if (stock_initialisation (&sminfo)) {
		fprintf (stderr, "%s: failed to get sensible data from the device..\n", progname);
		exit (EXIT_FAILURE);
	}
	/*}}}*/
	/*{{{  if not verbose, daemonise*/
	/* if not verbose then daemonise */
	if (!sminfo.verbose) {
		fflush (stdout);
		fflush (stderr);

		switch (fork ()) {
		case -1:
			fprintf (stderr, "%s: fork() failed with %s\n", progname, strerror (errno));
			exit (EXIT_FAILURE);
			break;
		case 0:
			/* child process, become process group and session leader */
			setsid ();
			break;
		default:
			/* this is the parent process, exit */
			_exit (0);
			break;
		}
		/* fork again to let group leader exit */
		switch (fork ()) {
		case -1:
			fprintf (stderr, "%s: fork() failed with %s\n", progname, strerror (errno));
			exit (EXIT_FAILURE);
			break;
		case 0:
			/* child */
			break;
		default:
			/* parent, exit */
			_exit (0);
			break;
		}
		/* can't ever regain a controlling terminal from here on in */
		chdir ("/");
		umask (0);

		/* get rid of old descriptors */
		close (0);
		close (1);
		close (2);
		/* just get rid of stdin, stdout and stderr */
		{
			int i;

			/* paranoia code */
			for (i=0; i<3; i++) {
				if (i != sminfo.fd) {
					close (i);
				}
			}
		}
	}
	/*}}}*/
	/* go into forever loop */
	/*{{{  bit more setup*/
	FD_ZERO(&read_set);
	FD_SET(sminfo.fd, &read_set);
	high_fd = sminfo.fd;
	if (sminfo.interactive) {
		/* still have keyboard -- NOT using stdin here, but it should still be line-buffered */
		sminfo.stdin_fd = fileno (stdin);
		FD_SET(sminfo.stdin_fd, &read_set);
		high_fd = (sminfo.stdin_fd > sminfo.fd) ? sminfo.stdin_fd : sminfo.fd;
		if (set_stdin_for_interactive (&sminfo) < 0) {
			fprintf (stderr, "%s: failed to sort stdin out.  sorry dude.\n", progname);
		} else {
			fcntl (sminfo.stdin_fd, F_SETFL, O_NONBLOCK);
		}
	} else {
		sminfo.stdin_fd = -1;
	}
	/*}}}*/
	for (;;) {
		fd_set r_set;
		int i;
		static char pbuf[1024];
		static char ibuf[1024];
		static int pbuflen = 0;
		static int ibuflen = 0;
		static int command_ready = 0;
		static int did_autoscan = 0;

		tv.tv_sec = 5;
		tv.tv_usec = 0;
		memcpy ((char *)&r_set, (char *)&read_set, sizeof (fd_set));

		if (sminfo.interactive) {
			ibuf[ibuflen] = '\0';
			printf ("[0x%4.4X] INTERACTIVE: %s", last_tval, ibuf);
			fflush (stdout);
		}
		/* wait for something to happen */
		if (sminfo.no_timeout) {
			i = select (high_fd + 1, &r_set, NULL, NULL, NULL);
		} else {
			i = select (high_fd + 1, &r_set, NULL, NULL, &tv);
		}
		if (i == 0) {
			/*{{{  timeout*/
			if (sminfo.interactive) {
				printf ("\r\n");
				fflush (stdout);
			}
			if (sminfo.verbose) {
				fprintf (stderr, "%s: timeout!  reading data\r\n", progname);
			}
			/* oki, run through active devices and do stuff -- only if not doing something else though (!) */
			if (sminfo.rs422_runstate == 3) {
				/*{{{  write out gathered temperatures to disk*/
				int v;
				double tval;
				FILE *temp = fopen ("/tmp/.sermond_tempfile", "w");

				if (!temp) {
					if (sminfo.verbose) {
						fprintf (stderr, "%s: unable to open %s for writing..\n", progname, "/tmp/.sermond_tempfile");
					}
					exitcode = EXIT_FAILURE;
					fclose (temp);
					unlink ("/tmp/.sermond_tempfile");
					break;	/* from for() */
				}
				for (v = sminfo.arg_mindev; v <= sminfo.arg_maxdev; v++) {
					if (sminfo.rs422_active[v] == 1) {
						fprintf (temp, "freq%d: %d\n", v, sminfo.tclastvals[v]);
						tval = ((double)(sminfo.tclastvals[v]) + sminfo.tcdelta[v]) / sminfo.tcratio[v];
						fprintf (temp, "temp%d: %.2f\n", v, tval);
					}
				}
				fclose (temp);
				rename ("/tmp/.sermond_tempfile", sminfo.outfile);

				sminfo.rs422_runstate = 0;	/* inactive now */
				/*}}}*/
			} else if (!sminfo.interactive && !did_autoscan && !sminfo.rs422_runstate) {
				/*{{{  trigger device scan*/
				unsigned char cbuf[16];
				int devnum;

				/* trigger a device scan */
				if (sminfo.verbose) {
					fprintf (stderr, "sending command to synchronise the RS422 bus...\r\n");
				}
				if (serial_write_line (sminfo.fd, "F:S", 3, 20, 2000) != 3) {
					if (sminfo.verbose) {
						fprintf (stderr, "failed.\r\n");
					}
					goto blip_flibble;
				}
				microdelay (100000);	/* 100ms delay */

				sminfo.rs422_mindev = sminfo.arg_mindev;
				sminfo.rs422_maxdev = sminfo.arg_maxdev;
				devnum = sminfo.rs422_mindev;

				cbuf[0] = 'F';
				cbuf[1] = ':';
				cbuf[2] = 'R';
				cbuf[3] = (unsigned char)devnum;
				cbuf[4] = 0x55;				/* ping command */
				cbuf[5] = 0x00;				/* our device ID (00) */
				cbuf[6] = '\0';

				if (sminfo.verbose) {
					fprintf (stderr, "(scan) sending ping device command to device 0x%2.2x (data = 0x%2.2x)\r\n", cbuf[3], cbuf[5]);
				}
				if (serial_write_line (sminfo.fd, cbuf, 6, 20, 2000) != 6) {
					if (sminfo.verbose) {
						fprintf (stderr, "failed.\r\n");
					}
				} else {
					/* setup auto-receive stuff */
					sminfo.rs422_runstate = 1;
					sminfo.rs422_waitdev = devnum;
					sminfo.rs422_active[sminfo.rs422_waitdev] = 2;	/* waiting for PONG */
					sminfo.rs422_maxdev++;				/* because it stops after incrementing it */
					did_autoscan = 1;				/* in progress */
				}
				/*}}}*/
			} else if (!sminfo.interactive && (did_autoscan == 2) && !sminfo.rs422_runstate) {
				/*{{{  trigger temperature scan*/
				unsigned char cbuf[16];
				int devnum;

				/* trigger temperature scan */
				printf ("sending command to synchronise the RS422 bus...\r\n");
				if (serial_write_line (sminfo.fd, "F:S", 3, 20, 2000) != 3) {
					if (sminfo.verbose) {
						fprintf (stderr, "failed.\r\n");
					}
					goto blip_flibble;
				}
				microdelay (100000);	/* 100ms delay */

				sminfo.rs422_mindev = sminfo.arg_mindev;
				sminfo.rs422_maxdev = sminfo.arg_maxdev;
				devnum = sminfo.rs422_mindev;

				while ((sminfo.rs422_active[devnum] != 1) && (devnum <= sminfo.rs422_maxdev)) {
					devnum++;
				}
				if (devnum > sminfo.rs422_maxdev) {
					if (sminfo.verbose) {
						fprintf (stderr, "no devices to scan!.\r\n");
					}
					goto blip_flibble;
				}

				cbuf[0] = 'F';
				cbuf[1] = ':';
				cbuf[2] = 'R';
				cbuf[3] = (unsigned char)devnum;
				cbuf[4] = 0x73;				/* read temp command */
				cbuf[5] = 0x00;				/* our device ID (00) */
				cbuf[6] = '\0';

				if (sminfo.verbose) {
					fprintf (stderr, "(scan) sending read-temp device command to device 0x%2.2x (data = 0x%2.2x)\r\n", cbuf[3], cbuf[5]);
				}
				if (serial_write_line (sminfo.fd, cbuf, 6, 20, 2000) != 6) {
					if (sminfo.verbose) {
						fprintf (stderr, "failed.\r\n");
					}
				} else {
					/* setup auto-receive stuff */
					sminfo.rs422_runstate = 2;		/* temp scan */
					sminfo.rs422_waitdev = devnum;
					sminfo.rs422_active[sminfo.rs422_waitdev] = 3;	/* waiting for TEMP */
					sminfo.rs422_maxdev++;				/* because it stops after incrementing it */
				}
				/*}}}*/
			}
			/*}}}*/
		} else if ((i < 0) && (errno == EINTR)) {
			/*{{{  interrupted*/
			/* got interrupted, oh well, just continue */
			if (sminfo.interactive) {
				printf ("<interrupted>\r\n");
				fflush (stdout);
			}
			if (sminfo.verbose) {
				fprintf (stderr, "%s: interrupted.\n", progname);
			}
			exitcode = EXIT_FAILURE;
			break;	/* from for() */
			/*}}}*/
		} else if (i < 0) {
			/*{{{  select() failed*/
			if (sminfo.interactive) {
				printf ("\r\n");
				fflush (stdout);
			}
			if (sminfo.verbose) {
				fprintf (stderr, "%s: select failed with %s\n", progname, strerror (errno));
			}
			exitcode = EXIT_FAILURE;
			break;	/* from for() */
			/*}}}*/
		} else if (FD_ISSET (sminfo.fd, &r_set)) {
			/*{{{  device sending/sent something*/
			int n;

			n = serial_read_line (sminfo.fd, pbuf, 1023, 20, 1024);
			if (n < 0) {
				if (sminfo.interactive) {
					printf ("\r\n");
					fflush (stdout);
				}
				if (sminfo.verbose) {
					fprintf (stderr, "%s: error reading from device.\r\n", progname);
				}
				longfailcount--;
				if (!longfailcount) {
					exitcode = EXIT_FAILURE;
					break;	/* from for () */
				}
			} else {
				longfailcount = 20;
			}
			pbuflen = n;
			pbuf[pbuflen] = '\0';

			/* oki, well, got something :) */
			if (sminfo.interactive) {
				printf ("\r");
				fflush (stdout);
			}

			if ((pbuf[0] == 'T') && (pbuf[1] == ':')) {
				/* temperature reading */
				int v;
				double tval;
				FILE *temp = fopen ("/tmp/.sermond_tempfile", "w");

				if (!temp) {
					if (sminfo.verbose) {
						fprintf (stderr, "%s: unable to open %s for writing..\n", progname, "/tmp/.sermond_tempfile");
					}
					exitcode = EXIT_FAILURE;
					fclose (temp);
					unlink ("/tmp/.sermond_tempfile");
					break;	/* from for() */
				}
				if (sscanf (pbuf + 2, "%x", &v) != 1) {
					if (sminfo.verbose) {
						fprintf (stderr, "%s: error parsing temperature data from device.. [%s]\n", progname, pbuf);
					}
					exitcode = EXIT_FAILURE;
					fclose (temp);
					unlink ("/tmp/.sermond_tempfile");
					break;	/* from for() */
				}
				/* stuff data in output file */
				fprintf (temp, "freq: %d\n", v);
				tval = ((double)v + TCIDELTA);
				tval /= TCRATIO;
				tval += TCODELTA;
				fprintf (temp, "temp: %.2f\n", tval);
				fclose (temp);
				last_tval = v;
				rename ("/tmp/.sermond_tempfile", sminfo.outfile);
			} else if ((pbuf[0] == 'F') && (pbuf[1] == ':')) {
				/* some response from the RS422 bus */
				if (sminfo.rs422_waitdev == -1) {
					fprintf (stderr, "unexpected response from RS422 bus: [%s]\r\n", pbuf + 2);
				} else {
					/* we were waiting for something */
					switch (sminfo.rs422_active[sminfo.rs422_waitdev]) {
					case 2:		/* waiting for pong */
						if (sminfo.rs422_runstate == 1) {
							/*{{{  scanning for active devices*/
							/* scanning for active devices */
							if (!strncmp (pbuf + 2, "DATA:0056", 9)) {
								if (sminfo.verbose) {
									fprintf (stderr, "FOUND: RS422 bus device ID 0x%2.2X\r\n", sminfo.rs422_waitdev);
								}
								sminfo.rs422_active[sminfo.rs422_waitdev] = 1;	/* idle */
							} else if (!strncmp (pbuf + 2, "NO REPLY", 8)) {
								if (sminfo.verbose) {
									fprintf (stderr, "TIMEOUT: no device responding to ID 0x%2.2X\r\n", sminfo.rs422_waitdev);
								}
								sminfo.rs422_active[sminfo.rs422_waitdev] = 0;	/* not present */
							}
							sminfo.rs422_waitdev++;
							if (sminfo.rs422_waitdev == sminfo.rs422_maxdev) {
								/* scanned them all */
								if (sminfo.verbose) {
									fprintf (stderr, "finished scanning RS422 bus :)\r\n");
								}
								if (!sminfo.interactive) {
									did_autoscan = 2;
								}
								sminfo.rs422_runstate = 0;
								sminfo.rs422_waitdev = -1;
							} else {
								int devnum = sminfo.rs422_waitdev;
								unsigned char cbuf[15];

								/* initiate scan for next device */
								microdelay (100000);		/* 100ms delay */

								if (sminfo.verbose) {
									fprintf (stderr, "sending command to synchronise the RS422 bus...\r\n");
								}
								if (serial_write_line (sminfo.fd, "F:S", 3, 20, 2000) != 3) {
									if (sminfo.verbose) {
										fprintf (stderr, "failed.\r\n");
									}
								}
								microdelay (100000);	/* 100ms delay */

								cbuf[0] = 'F';
								cbuf[1] = ':';
								cbuf[2] = 'R';
								cbuf[3] = (unsigned char)devnum;
								cbuf[4] = 0x55;				/* ping command */
								cbuf[5] = 0x00;				/* our device ID (00) */
								cbuf[6] = '\0';

								if (sminfo.verbose) {
									fprintf (stderr, "sending ping device command to device 0x%2.2x (data = 0x%2.2x)\r\n", cbuf[3], cbuf[5]);
								}
								if (serial_write_line (sminfo.fd, cbuf, 6, 20, 2000) != 6) {
									if (sminfo.verbose) {
										fprintf (stderr, "failed.  aborting scan.\r\n");
									}
									sminfo.rs422_runstate = 0;
								} else {
									sminfo.rs422_active[sminfo.rs422_waitdev] = 2;	/* waiting for PONG */
								}
							}
							/*}}}*/
						} else {
							if (sminfo.verbose) {
								fprintf (stderr, "unexpected response from RS422 bus: [%s]\r\n", pbuf + 2);
							}
						}
						break;
					case 3:		/* waiting for temperature */
						if (sminfo.rs422_runstate == 2) {
							/*{{{  scanning for temperature*/
							/* scanning for temperature */
							if (!strncmp (pbuf + 2, "DATA:00", 7)) {
								if (sminfo.verbose) {
									fprintf (stderr, "GOT: temperature from device ID 0x%2.2X, temp-freq = 0x%s\r\n", sminfo.rs422_waitdev, pbuf + 9);
								}
								sminfo.rs422_active[sminfo.rs422_waitdev] = 1;	/* idle */
								/* parse + store this! */
								sminfo.tclastvals[sminfo.rs422_waitdev] = parse_uint16hex (pbuf + 9);
							} else if (!strncmp (pbuf + 2, "NO REPLY", 8)) {
								if (sminfo.verbose) {
									fprintf (stderr, "TIMEOUT: while waiting for temperature from device ID 0x%2.2X.\r\n", sminfo.rs422_waitdev);
								}
								sminfo.rs422_active[sminfo.rs422_waitdev] = 0;	/* vanished */
							}
							/* scan for next idle device */
							sminfo.rs422_waitdev++;
							while ((sminfo.rs422_active[sminfo.rs422_waitdev] != 1) && (sminfo.rs422_waitdev < sminfo.rs422_maxdev)) {
								sminfo.rs422_waitdev++;
							}
							if (sminfo.rs422_waitdev == sminfo.rs422_maxdev) {
								/* scanned all idle ones */
								if (sminfo.verbose) {
									fprintf (stderr, "finished scanning RS422 bus :)\r\n");
								}
								sminfo.rs422_runstate = 3;	/* this triggers writing of data to disk */
								sminfo.rs422_waitdev = -1;
							} else {
								int devnum = sminfo.rs422_waitdev;
								unsigned char cbuf[16];

								microdelay (100000);

								if (sminfo.verbose) {
									fprintf (stderr, "sending command to synchronise the RS422 bus...\r\n");
								}
								if (serial_write_line (sminfo.fd, "F:S", 3, 20, 2000) != 3) {
									if (sminfo.verbose) {
										fprintf (stderr, "failed.\r\n");
									}
								}
								microdelay (100000);

								cbuf[0] = 'F';
								cbuf[1] = ':';
								cbuf[2] = 'R';
								cbuf[3] = (unsigned char)devnum;
								cbuf[4] = 0x73;				/* get temp command */
								cbuf[5] = 0x00;				/* our device ID (00) */
								cbuf[6] = '\0';

								if (sminfo.verbose) {
									fprintf (stderr, "sending read temperature device command to device 0x%2.2x (data = 0x%2.2x)\r\n", cbuf[3], cbuf[5]);
								}
								if (serial_write_line (sminfo.fd, cbuf, 6, 20, 2000) != 6) {
									if (sminfo.verbose) {
										fprintf (stderr, "failed.  aborting scan.\r\n");
									}
									sminfo.rs422_runstate = 0;
								} else {
									sminfo.rs422_active[sminfo.rs422_waitdev] = 3;	/* waiting for TEMP */
								}
							}
							/*}}}*/
						} else {
							if (sminfo.verbose) {
								fprintf (stderr, "unexpected response from RS422 bus: [%s]\r\n", pbuf + 2);
							}
						}
						break;
					default:	/* dunno.. */
						fprintf (stderr, "unexpected response from RS422 bus; [%s]\r\n", pbuf + 2);
						break;
					}
				}
			} else {
				if (sminfo.verbose) {
					fprintf (stderr, "unhandled data: [%s]\r\n", pbuf);
				}
			}
			/*}}}*/
		} else if (sminfo.interactive && FD_ISSET (sminfo.stdin_fd, &r_set)) {
			/*{{{  keyboard data*/
			int n;

			n = read (sminfo.stdin_fd, ibuf + ibuflen, 1);
			if (n < 0) {
				printf ("\r\n");
				fflush (stdout);
				if (sminfo.verbose) {
					fprintf (stderr, "%s: error reading keyboard: %s\n", progname, strerror (errno));
				}
				exitcode = EXIT_FAILURE;
				break;	/* from for() */
			} else if (!n) {
				/* EOF */
				printf ("\r\n");
				fflush (stdout);
				exitcode = EXIT_SUCCESS;
				break;	/* from for() */
			}
			switch (ibuf[ibuflen]) {
			case 0x0d:	/* return */
				ibuf[ibuflen] = '\0';
				command_ready = 1;
				break;
			case 0x08:	/* backspace */
				if (ibuflen > 0) {
					ibuflen--;
					printf ("\b    ");
				} else {
					printf ("\a");	/* beep! */
					fflush (stdout);
				}
				break;
			default:
				ibuflen++;
				break;
			}
			printf ("\r");
			fflush (stdout);
			/*}}}*/
		}
		tcflush (sminfo.fd, TCIFLUSH);
		if (command_ready) {
			/*{{{  process command*/
			printf ("\r\n");
			/*{{{  quit/exit/q/x*/
			if (!strcmp (ibuf, "quit") || !strcmp (ibuf, "exit") || !strcmp (ibuf, "q") || !strcmp (ibuf, "x")) {
				exitcode = EXIT_SUCCESS;
				break;	/* from for() */
			}
			/*}}}*/
			if (!strcmp (ibuf, "help")) {
				/*{{{  help*/
				printf ("sermond: AVR serial interface program.\r\n");
				printf ("    quit, exit             exit the program\r\n");
				printf ("    help                   this help\r\n");
				printf ("    piinit                 initialise remote programming interface\r\n");
				printf ("    pioff                  deinitialise remote programming interface\r\n");
				printf ("    reset                  send reset command\r\n");
				printf ("    version                send version command\r\n");
				printf ("    pienable               enable remote device (sends serial-prog enable command)\r\n");
				printf ("    pisig                  send remote device ID command\r\n");
				printf ("    pierase                send remote device erase command\r\n");
				printf ("    pitest                 send remote device test command\r\n");
				printf ("    piflash <file>         program specified file into remote flash\r\n");
				printf ("    pieeprom <file>        program specified file into remote eeprom\r\n");
				printf ("    rdata <data>           send a bit of data to the RS422 bus\r\n");
				printf ("    ridle                  tell RS422 bus to go (transmit) idle\r\n");
				printf ("    rzero                  tell RS422 bus to go (transmit) zero\r\n");
				printf ("    rstartstop             tell RS422 bus to send start/stop\r\n");
				printf ("    r0                     send a single zero\r\n");
				printf ("    r1                     send a single one\r\n");
				printf ("    raux <device> <data>   send some aux data (0x43,<data>) at <device> (autosync)\r\n");
				printf ("    rping <device>         ping a device (autosync)\r\n");
				printf ("    rtemp <device>         read temperature from a device (autosync)\r\n");
				printf ("    rsync                  synchronise the bus (sends a long sequence of 1 bits)\r\n");
				printf ("    rscan                  scan RS422 bus for active devices\r\n");
				printf ("    rscan <low> <high>     scan RS422 bus for active devices from <low> to <high> inclusive\r\n");
				/*}}}*/
			} else if (!strcmp (ibuf, "piinit")) {
				/*{{{  piinit*/
				printf ("sending [P:I] command...\r\n");
				if (serial_write_line (sminfo.fd, "P:I", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "pioff")) {
				/*{{{  pioff*/
				printf ("sending [P:Z] command...\r\n");
				if (serial_write_line (sminfo.fd, "P:Z", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "reset")) {
				/*{{{  reset*/
				printf ("sending reset command...\r\n");
				if (serial_write_line (sminfo.fd, "R:", 2, 20, 2000) != 2) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "version")) {
				/*{{{  version*/
				printf ("sending version command..\r\n");
				if (serial_write_line (sminfo.fd, "V:", 2, 20, 2000) != 2) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "pienable")) {
				/*{{{  pienable*/
				printf ("sending remote programming enable command..\r\n");
				if (serial_write_line (sminfo.fd, "P:E", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "pisig")) {
				/*{{{  pisig*/
				printf ("sending read remote signature bytes command..\r\n");
				if (serial_write_line (sminfo.fd, "P:S", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "pierase")) {
				/*{{{  pierase*/
				printf ("sending remote chip erase command..\r\n");
				if (serial_write_line (sminfo.fd, "P:X", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "pitest")) {
				/*{{{  pitest*/
				printf ("sending remote chip test command..\r\n");
				if (serial_write_line (sminfo.fd, "P:T", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strncmp (ibuf, "piflash ", 8)) {
				/*{{{  piflash <filename>*/
				char *filename = ibuf + 8;
				
				printf ("programming remote flash..\r\n");
				if (program_remote_flash (&sminfo, filename) < 0) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strncmp (ibuf, "pieeprom ", 9)) {
				/*{{{  pieeprom <filename>*/
				char *filename = ibuf + 9;

				printf ("programming remote eeprom..\r\n");
				if (program_remote_eeprom (&sminfo, filename) < 0) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strncmp (ibuf, "rdata ", 6)) {
				/*{{{  rdata <...>*/
				char *data = ibuf + 6;
				int tsize = (strlen (ibuf) - 6);

				if (!tsize) {
					printf ("not sending nothing.\r\n");
				} else {
					printf ("sending RS422 data..\r\n");
					/* butcher ibuf */
					data[-1] = 'T';
					data[-2] = ':';
					data[-3] = 'F';
					/* glob_escape = 1; */	/* i'm sure this is bust */
					if (serial_write_line (sminfo.fd, data - 3, tsize + 3, 20, 2000) != (tsize + 3)) {
						printf ("failed.\r\n");
					}
					glob_escape = 0;
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "ridle")) {
				/*{{{  ridle*/
				printf ("sending remote idle command..\r\n");
				if (serial_write_line (sminfo.fd, "F:I", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "rzero")) {
				/*{{{  rzero*/
				printf ("sending remote zero command..\r\n");
				if (serial_write_line (sminfo.fd, "F:Z", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "rstartstop")) {
				/*{{{  rstartstop*/
				printf ("sending remote start-stop command..\r\n");
				if (serial_write_line (sminfo.fd, "F:Y", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "r0")) {
				/*{{{  r0*/
				printf ("sending 0 down the RS422 bus..\r\n");
				if (serial_write_line (sminfo.fd, "F:0", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "r1")) {
				/*{{{  r1*/
				printf ("sending 1 down the RS422 bus...\r\n");
				if (serial_write_line (sminfo.fd, "F:1", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "rsync")) {
				/*{{{  rsync*/
				printf ("sending command to synchronise the RS422 bus...\r\n");
				if (serial_write_line (sminfo.fd, "F:S", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				microdelay (10000);	/* 10ms delay */
				/*}}}*/
			} else if (!strncmp (ibuf, "raux ", 5)) {
				/*{{{  raux <device> <data>*/
				char *devstr = ibuf + 5;
				char *auxstr;
				int devnum;
				unsigned char cbuf[16];

				for (auxstr = devstr; (*auxstr != ' ') && (*auxstr != '\0'); auxstr++);
				if (*auxstr == '\0') {
					printf ("that\'s wrong.  see the help.\r\n");
					goto blip_flibble;
				}
				*auxstr = '\0';
				auxstr++;

				/* expect devstr to be some valid device ID, decimal, data is hex */
				if (sscanf (devstr, "%d", &devnum) != 1) {
					printf ("<device> ought to be a positive integer\r\n");
					goto blip_flibble;
				}
				if ((devnum < 0) || (devnum > 127)) {
					printf ("<device> ought to be a positive integer\r\n");
					goto blip_flibble;
				}
				if (strlen (auxstr) != 2) {
					printf ("<data> ought to be a hex-digit pair\r\n");
					goto blip_flibble;
				}
				if (decode_hex_byte (auxstr[0], auxstr[1], cbuf + 5)) {
					printf ("<data> ought to be a hex-digit pair\r\n");
					goto blip_flibble;
				}

				printf ("sending command to synchronise the RS422 bus...\r\n");
				if (serial_write_line (sminfo.fd, "F:S", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				microdelay (100000);	/* 100ms delay */

				cbuf[0] = 'F';
				cbuf[1] = ':';
				cbuf[2] = 'T';
				cbuf[3] = (unsigned char)devnum;
				cbuf[4] = 0x43;				/* aux1 command */
				cbuf[6] = '\0';

				printf ("sending aux device command to device 0x%2.2x (data = 0x%2.2x)\r\n", cbuf[3], cbuf[5]);
				if (serial_write_line (sminfo.fd, cbuf, 6, 20, 2000) != 6) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strncmp (ibuf, "rping ", 6)) {
				/*{{{  rping <device>*/
				char *devstr = ibuf + 6;
				int devnum;
				unsigned char cbuf[15];

				if (sscanf (devstr, "%d", &devnum) != 1) {
					printf ("<device> ought to be a positive integer\r\n");
					goto blip_flibble;
				}

				printf ("sending command to synchronise the RS422 bus...\r\n");
				if (serial_write_line (sminfo.fd, "F:S", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				microdelay (100000);	/* 100ms delay */

				cbuf[0] = 'F';
				cbuf[1] = ':';
				cbuf[2] = 'R';
				cbuf[3] = (unsigned char)devnum;
				cbuf[4] = 0x55;				/* ping command */
				cbuf[5] = 0x00;				/* our device ID (00) */
				cbuf[6] = '\0';

				printf ("sending ping device command to device 0x%2.2x (data = 0x%2.2x)\r\n", cbuf[3], cbuf[5]);
				if (serial_write_line (sminfo.fd, cbuf, 6, 20, 2000) != 6) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strncmp (ibuf, "rtemp ", 6)) {
				/*{{{  rtemp <device>*/
				char *devstr = ibuf + 6;
				int devnum;
				unsigned char cbuf[15];

				if (sscanf (devstr, "%d", &devnum) != 1) {
					printf ("<device> ought to be a positive integer\r\n");
					goto blip_flibble;
				}

				printf ("sending command to synchronise the RS422 bus...\r\n");
				if (serial_write_line (sminfo.fd, "F:S", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				microdelay (100000);	/* 100ms delay */

				cbuf[0] = 'F';
				cbuf[1] = ':';
				cbuf[2] = 'R';
				cbuf[3] = (unsigned char)devnum;
				cbuf[4] = 0x73;				/* read temperature command (if supported remotely!) */
				cbuf[5] = 0x00;				/* our device ID (00) */
				cbuf[6] = '\0';

				printf ("sending read temperature command to device 0x%2.2x (data = 0x%2.2x)\r\n", cbuf[3], cbuf[5]);
				if (serial_write_line (sminfo.fd, cbuf, 6, 20, 2000) != 6) {
					printf ("failed.\r\n");
				}
				/*}}}*/
			} else if (!strcmp (ibuf, "rscan")) {
				/*{{{  rscan*/
				int devnum = 1;
				unsigned char cbuf[15];

				sminfo.rs422_mindev = 1;
				sminfo.rs422_maxdev = MAXRS422_DEVICES;
				printf ("sending command to synchronise the RS422 bus...\r\n");
				if (serial_write_line (sminfo.fd, "F:S", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				microdelay (100000);	/* 100ms delay */

				cbuf[0] = 'F';
				cbuf[1] = ':';
				cbuf[2] = 'R';
				cbuf[3] = (unsigned char)devnum;
				cbuf[4] = 0x55;				/* ping command */
				cbuf[5] = 0x00;				/* our device ID (00) */
				cbuf[6] = '\0';

				printf ("(scan) sending ping device command to device 0x%2.2x (data = 0x%2.2x)\r\n", cbuf[3], cbuf[5]);
				if (serial_write_line (sminfo.fd, cbuf, 6, 20, 2000) != 6) {
					printf ("failed.\r\n");
				} else {
					/* setup auto-receive crap */
					sminfo.rs422_runstate = 1;
					sminfo.rs422_waitdev = devnum;
					sminfo.rs422_active[sminfo.rs422_waitdev] = 2;	/* waiting for PONG */
				}
				/*}}}*/
			} else if (!strncmp (ibuf, "rscan ", 6)) {
				/*{{{  rscan <mindev> <maxdev>*/
				char *startstr = ibuf + 6;
				char *endstr;
				unsigned char cbuf[15];
				int devnum;

				for (endstr = startstr; (*endstr != ' ') && (*endstr != '\0'); endstr++);
				if (endstr == '\0') {
					printf ("that looks wrong.  see the help.\r\n");
					goto blip_flibble;
				}
				*endstr = '\0';
				endstr++;
				if (sscanf (startstr, "%d", &sminfo.rs422_mindev) != 1) {
					printf ("<mindev> not a number ?\r\n");
					goto blip_flibble;
				}
				if ((sminfo.rs422_mindev < 1) || (sminfo.rs422_mindev > 127)) {
					printf ("<mindev> ought to be between 1 and 127 inclusive\r\n");
					goto blip_flibble;
				}
				if (sscanf (endstr, "%d", &sminfo.rs422_maxdev) != 1) {
					printf ("<maxdev> not a number ?\r\n");
					goto blip_flibble;
				}
				if ((sminfo.rs422_maxdev < 1) || (sminfo.rs422_maxdev > 127)) {
					printf ("<maxdev> ought to be between 1 and 127 inclusive\r\n");
					goto blip_flibble;
				}
				if (sminfo.rs422_mindev > sminfo.rs422_maxdev) {
					printf ("<mindev> ought to be less than <maxdev>\r\n");
					goto blip_flibble;
				}

				printf ("sending command to synchronise the RS422 bus...\r\n");
				if (serial_write_line (sminfo.fd, "F:S", 3, 20, 2000) != 3) {
					printf ("failed.\r\n");
				}
				microdelay (100000);	/* 100ms delay */
				devnum = sminfo.rs422_mindev;

				cbuf[0] = 'F';
				cbuf[1] = ':';
				cbuf[2] = 'R';
				cbuf[3] = (unsigned char)devnum;
				cbuf[4] = 0x55;				/* ping command */
				cbuf[5] = 0x00;				/* our device ID (00) */
				cbuf[6] = '\0';

				printf ("(scan) sending ping device command to device 0x%2.2x (data = 0x%2.2x)\r\n", cbuf[3], cbuf[5]);
				if (serial_write_line (sminfo.fd, cbuf, 6, 20, 2000) != 6) {
					printf ("failed.\r\n");
				} else {
					/* setup auto-receive crap */
					sminfo.rs422_runstate = 1;
					sminfo.rs422_waitdev = devnum;
					sminfo.rs422_active[sminfo.rs422_waitdev] = 2;	/* waiting for PONG */
					sminfo.rs422_maxdev++;				/* because it stops after incrementing it */
				}
				/*}}}*/
			} else {
				printf ("ignoring that.\r\n");
			}
			/*}}}*/
blip_flibble:				/* damn goto's...  ;) */
			command_ready = 0;
			ibuflen = 0;
		}
		/* loop */
	}

	if (sminfo.interactive) {
		restore_stdin_from_interactive (&sminfo);
	}
	tcflush (sminfo.fd, TCIFLUSH);
	restore_serial_state (&sminfo);
	microdelay (100000);
	close (sminfo.fd);

	return exitcode;
}
/*}}}*/



