/* RWDOCS */
/* filename	: sys5_io.c
 */
/* OS- and machine-dependent stuff for SYS5 */

/*
	FILE: UNIX.io.c
	
	Routines:
		onquit()	used to stop console input for shell layer - K5JB
		report_quit() is the deferred report of quit receipt
		ioinit()
		iostop()
		asy_init()
		asy_stop()
		asy_speed()
		asy_output()
		asy_recv()
		dir()
	Written or converted by Mikel Matthews, N9DVG
	SYS5 added by Jere Sandidge, K4FUM
	Directory pipe added by Ed DeHart, WA3YOA
	Numerous changes by Charles Hedrick and John Limpert, N3DMC
	Hell, *I* even hacked on it... :-)  Bdale, N3EUA
	Converted to blocking input to let CPU breathe.  Note that Coherent
	doesn't have timeout on blocking input so put sleep2() on asy_recv().
	Also re-worked to handle bad serial port connection on 3B2, probably 
	hardware dependent -- K5JB
	
	If you (have and) want to use the select code, define SELECT in the 
	makefile or in this file.
*/

#include <stdio.h>
#include <sys/types.h>
#include <termio.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include "global.h"

#ifndef COH386
#include <sys/file.h>
#include <sys/dir.h>
#endif

#include "asy.h"
#include "mbuf.h"
#include "internet.h"
#include "iface.h"
#include "unix.h"
#include "cmdparse.h"
#include "unixopt.h"
#include "motor.h"

#ifndef	B19200
#define	B19200	EXTA
#endif

#ifndef	B38400
#define	B38400	EXTB
#endif

struct asy asy[ASY_MAX];
struct interface *ifaces;
struct termio mysavetty, savecon;
int saveconfl;
int	IORser[ASY_MAX];
char *ttbuf;
int slipisopen;

#define SHL		/* shell layer capability - quit sets reportquit_flag */
					/* check for that flag in main loop and if set call */
					/* report_quit() - A signal handler shouldn't printf */

#ifdef SHL
int reportquit_flag = 0;

static void
onquit()
{

/* RWDOCE */
extern int backgrd;

	signal((int)SIGQUIT,onquit );	/* SIGQUIT is a Ctrl \ */
	backgrd = !backgrd;
	reportquit_flag = 1;

}

/* RWDOCS */
void
report_quit()
{

/* RWDOCE */
extern int backgrd;

	reportquit_flag = 0;
	printf("\nKeyboard input is ");
	if(backgrd)
		printf("IGNORED.  Inactive shell OK now.  Ctrl-\\ to restore.\n");
	else
		printf("ACCEPTED.  Don't make shell inactive.\n");
}
#endif

/* RWDOCS */
/* Called at startup time to set up console I/O, memory heap */
int ioinit()
{

/* RWDOCE */
	struct termio ttybuf;
	void iostop();
	int ioctl();
#ifdef SHL
	void onquit();
#endif
	extern void check_kbd();


	if( (int)(signal(SIGHUP, iostop ) ) <  0 ) {
	perror( "set SIGHUP failed " );
	exit(1);
	}


	if( (int)(signal(SIGINT, iostop ))  <  0 ) {
	perror( "set SIGINT failed " );
	exit(1);
	}

#ifdef SHL

	if( (int)(signal(SIGQUIT, onquit )) <  0 ) {	/* SIGQUIT is a Ctrl \ */
	perror( "set SIGQUIT failed " );
	exit(1);
	}
#else

	if( (int)(signal(SIGQUIT, iostop )) < 0 )  {
	perror( "set SIGQUIT failed " );
	exit(1);
	}

#endif

	if( (int)(signal(SIGTERM, iostop )) < 0 ) {
	perror( "set SIGINT failed " );
	exit(1);
	}



	ioctl(0, TCGETA, &ttybuf);
	savecon = ttybuf;

	ttybuf.c_iflag &= ~IXON;
	ttybuf.c_cflag |= CLOCAL; 
	ttybuf.c_lflag &= ~(ICANON|ECHO);	/* removed ISIG -- K5JB */
	/* note that in Coherent, the following is ineffective */
	ttybuf.c_cc[VTIME] = 1;	/* effective with ~ICANON -- was '\0' - K5JB */
	ttybuf.c_cc[VMIN] = 0; /* ditto, was '\01' - K5JB */
	if ((saveconfl = fcntl(0, F_GETFL, 0)) == -1) {
		perror("Could not read console flags");
		return -1;
	}


	ioctl(0, TCSETAW, &ttybuf);

		/* put it on the list of polls */
/* arg0 = fd, arg1 = r_call, arg2 = t_call, arg3 = p_call, arg4 = ifp
   arg5 = events RLW
  */

	if( (makepiston( 0, check_kbd, NULLFP, NULLFP, NULLIF, POLLIN )) == -1) {
		printf( "fatal error: cannot put stdin on poll list\n" );
		iostop();
	}

	return(0);
}


/* RWDOCS */
/* Called just before exiting to restore console state */
void iostop()
{

/* RWDOCE */
	int ioctl();
	extern int exitval;
	setbuf(stdout,NULLCHAR);


	while (ifaces != NULLIF) {
		if (ifaces->stop != NULLFP)
			(*ifaces->stop)(ifaces->dev);
		ifaces = ifaces->next;
	}

	ioctl(0, TCSETAW, &savecon);
	fcntl(0, F_SETFL, saveconfl);

	exit(exitval);
/*	return( (* iostop)() ); */
}

/* RWDOCS */
/* Initialize asynch port "dev" */
int
asy_init(dev, arg1, arg2, bufsize)
int16 dev;
char *arg1, *arg2;
unsigned bufsize;
{

/* RWDOCE */
	register struct asy *ap;
	struct termio	sgttyb;
	int tempttyfl, ioctl();
	bufsize=bufsize; arg1=arg1;
#ifdef	SYS5_DEBUG
	printf("asy_init: called\n");
#endif	/* SYS5_DEBUG */

	if (dev >= nasy) {
		return -1;
	}

	ap = &asy[dev];
	ap->tty = malloc(strlen(arg2)+1);
	strcpy(ap->tty, arg2);
	printf("asy_init: tty name = %s\n", ap->tty);

	/* open with no block because port may not have DCD asserted */
	if ((IORser[dev] = open(ap->tty, (O_RDWR | O_NDELAY), 0)) < 0) {
		perror("Could not open device IORser");
		return -1;
	}

	if ((tempttyfl = fcntl(IORser[dev], F_GETFL, 0)) == -1) {
		perror("Could not read asy flags");
		return -1;
	}

#ifndef COH386	/* Coherent can't break a block */
	/* now make port blocking - first write will report error if so */
	fcntl(IORser[dev], F_SETFL, tempttyfl & ~O_NDELAY);
#endif

 /* 
  * get the stty structure and save it 
  */

	if (ioctl(IORser[dev], TCGETA, &mysavetty) < 0)	{
		perror("ioctl failed on device");
		return -1;
	}

 /* 
  * copy over the structure 
  */

	sgttyb = mysavetty;
	sgttyb.c_iflag = (IGNBRK | IGNPAR);
	sgttyb.c_oflag = 0;
	sgttyb.c_lflag = 0;
	sgttyb.c_cflag = (B9600 | CS8 | CREAD);
	/* note that in Coherent, the following is ineffective */
	sgttyb.c_cc[VTIME] = 0;	/* was 0 - K5JB */
	sgttyb.c_cc[VMIN] = 0;

	if (ioctl(IORser[dev], TCSETAF, &sgttyb) < 0) {
		perror("ioctl could not set parameters for IORser");
		return -1;
	}

	return 0;
}

/* RWDOCS */
int
asy_stop(dev)
int dev;
{

/* RWDOCE */
	dev=dev;
	return(0);
}

/* RWDOCS */
/* Set asynch line speed */
int
asy_speed(dev, speed)
int dev;
int speed;
{

/* RWDOCE */
	struct termio sgttyb;
	int ioctl();

	if (speed == 0 || dev >= nasy) {
		return -1;
	}

#ifdef	SYS5_DEBUG
	printf("asy_speed: Setting speed for device %d to %d\n",dev, speed);
#endif

	asy[dev].speed = speed;

	if (ioctl(IORser[dev], TCGETA, &sgttyb) < 0) {
		perror("ioctl could not set parameters");
		return -1;
	}

	sgttyb.c_cflag &= ~CBAUD;

	switch ((unsigned)speed) {
	case 0:
		sgttyb.c_cflag |= B0;
		break;
	case 50:
		sgttyb.c_cflag |= B50;
		break;
	case 75:
		sgttyb.c_cflag |= B75;
		break;
	case 110:
		sgttyb.c_cflag |= B110;
		break;
	case 134:
		sgttyb.c_cflag |= B134;
		break;
	case 150:
		sgttyb.c_cflag |= B150;
		break;
	case 200:
		sgttyb.c_cflag |= B200;
		break;
	case 300:
		sgttyb.c_cflag |= B300;
		break;
	case 600:
		sgttyb.c_cflag |= B600;
		break;
	case 1200:
		sgttyb.c_cflag |= B1200;
		break;
	case 1800:
		sgttyb.c_cflag |= B1800;
		break;
	case 2400:
		sgttyb.c_cflag |= B2400;
		break;
	case 4800:
		sgttyb.c_cflag |= B4800;
		break;
	case 9600:
		sgttyb.c_cflag |= B9600;
		break;
	case 19200:
		sgttyb.c_cflag |= B19200;
		break;
	case 38400:
		sgttyb.c_cflag |= B38400;
		break;
	default:
		printf("asy_speed: Unknown speed (%d)\n", speed);
		break;
	}

#ifdef	SYS5_DEBUG
	printf("speed = %d\n", sgttyb.sg_ispeed);
#endif	/* SYS5_DEBUG */

	if (ioctl(IORser[dev], TCSETAW, &sgttyb) < 0) {
		perror("ioctl could not set parameters for IORser");
		return -1;
	}

	return 0;
}

/* RWDOCS */
/* Send a buffer to serial transmitter */
int asy_output(dev, buf, cnt)
unsigned dev;
char *buf;
unsigned short cnt;
{

/* RWDOCE */
	int write();
	int out;
#ifdef	SYS5_DEBUG
	printf("asy_output called. dev = %d, cnt = %d\n", dev, cnt);
	printf("buf = %lx\n", (long) buf);
	fflush(stdout);
#endif

	if (dev >= nasy) {
		return -1;
	}

	if ( (out = write(IORser[dev], buf, cnt)) < cnt)	{
#ifdef	SYS5_DEBUG
		fprintf(stderr, "asy_output: out:%d - cnt %d\r\n", out, cnt );
#endif
		if( out < 0 && errno == EAGAIN ) return( 0);
		else return( out );

		return( out );
	} else {
#ifdef	SYS5_DEBUG 
		fprintf( stderr, "asy_output %d OK\r\n",cnt ); 
#endif
	}

	return out;
}

/* RWDOCS */
#ifdef COH386
#include <poll.h>

/* this always returns 0 unless interval is -1.  It returns either when
 * interval, in ms, elapses, or input available on stdin .  This can be
 * removed if Coherent comes up with the VMIN VTIME thing.
 */


/* no longer used in cohulip */
int
sleep2(interval)
int interval;
{

/* RWDOCE */
int polret;
struct pollfd pollcat[1];


	fprintf(stderr, "sleep2 called\r\n" ); 
	pollcat[0].fd = 0;	/* stdin */
	pollcat[0].events = POLLIN;
	polret = poll(pollcat,1,interval);

	return(polret);
}
#endif

/* RWDOCS */
/* Receive characters from asynch line
 * Returns count of characters read
 */
unsigned
asy_recv(dev,buf,cnt)
int dev;
char *buf;
unsigned cnt;
{

/* RWDOCE */
	int read();
#define	IOBUFLEN	256
	unsigned tot;
	int r;
/* uncomment this define if you want to see what is comming out of
   the serial port
*/
/* #define ASYRAWD */
#ifdef ASYRAWD
	int i;
#endif
  	static struct	{
		char	buf[IOBUFLEN];
		char	*data;
		int	cnt;
	}	IOBUF[ASY_MAX];

#ifdef SELECT
	int	mask;
	int	writemask;
	int	ok;
	struct timeval timeout;
	timeout.tv_sec = 0;
	timeout.tv_usec = 35;
	mask = (1<<IORser[dev]);
	writemask = (1<<IORser[dev]);
	ok = 0;
	tot = 0;

	ok = select(mask, &mask, 0, 0, &timeout);
	if ( mask & (1<<IORser[dev]))
	{
		tot = read(IORser[dev], buf, cnt);
	}
	return (tot);
#else
	if(IORser[dev] < 0) {
		printf("asy_recv: bad file descriptor passed for device %d\n",
			dev);
		return(0);
	}
	tot = 0;
	/* fill the read ahead buffer */
	if(IOBUF[dev].cnt == 0) {
		IOBUF[dev].data = IOBUF[dev].buf;

		r = read(IORser[dev], IOBUF[dev].data, IOBUFLEN);
#ifdef	SYS5_DEBUG
		fprintf( stderr, "asy_rcv:read returned %d\r\n", r );
#endif
		/* check the read */
		if (r == -1) {
#ifndef COH386	/* apparently read() in coherent returns -1 when nothing
		available from port */
			IOBUF[dev].cnt = 0;	/* bad read */
			if(!reportquit_flag){
				perror("asy_recv");
				printf("asy_recv: error in reading from device %d\n", dev);
			}
#endif
			return(0);
		} else
			IOBUF[dev].cnt = r;

#ifdef ASYRAWD
		fprintf(stderr, "asy_recv read:\n" );
		for( i = 0; i < r; i++ ) {
			fprintf( stderr, "%x ", IOBUF[dev].data[i] & 0xff );
			if( i % 8 == 7 ) fprintf(stderr,"\n");
			fflush(stderr);
		}
		fprintf(stderr, "\nend asy_recv-----\n" );
		fflush(stderr);
#endif
	} 
	r = 0;	/* return count */
	/* fetch what you need with no system call overhead */
	if(IOBUF[dev].cnt > 0) {
		if(cnt == 1) { /* single byte copy, do it here */
			*buf = *IOBUF[dev].data++;
			IOBUF[dev].cnt--;
			r = 1;
		} else { /* multi-byte copy, left memcpy do the work */
			unsigned n = min(cnt, IOBUF[dev].cnt);
			memcpy(buf, IOBUF[dev].data, n);
			IOBUF[dev].cnt -= n;
			IOBUF[dev].data += n;
			r = n;
		}
	}
	tot = (unsigned int) r;

	return (tot);
#endif	/* not SELECT */
}


/* RWDOCS */
/* Generate a directory listing by opening a pipe to /bin/ls.
 * If full == 1, give a full listing; else return just a list of names.
 */
FILE *
dir(path,full)
char *path;
int full;
{

/* RWDOCE */
	FILE *fp;
	char cmd[1024];

	if (path == NULLCHAR || path[0] == '\0')
		path = ".";

#ifdef	SYS5_DEBUG
	printf("DIR: path = %s\n", path);
#endif	/* SYS5_DEBUG */

	if (full)
		sprintf(cmd,"ls -l %s 2>&1", path);
	else
		sprintf(cmd, "ls %s 2>&1", path);

	if ((fp = popen(cmd,"r")) == NULLFILE) {
		perror("popen");

		return NULLFILE;
	}

	return fp;
}


/* RWDOCS */
int asy_ioctl(interface, argc, argv)
struct interface *interface;
int	argc;
char	*argv[];
{

/* RWDOCE */
	int atoi();

	if (argc < 1) {
		fprintf(stderr,"asy_ioctl: no speed specifed, keeping [%d]\r\n", asy[interface->dev].speed);
		return 0;
	}

	return asy_speed(interface->dev, atoi(argv[0]));
}

/* end sys5_io.c */
