/*
 *	Current Loop Interface Driver
 *
 */

#include "../h/local.h"

#ifdef  SCCS_ID
static char SCCS_ID [] = "@(#)cli.c    	3.1	 17:30:23 - 81/11/18 ";
#endif  SCCS_ID

#include "../h/param.h"
#include "../h/conf.h"
#include "../h/proc.h"
#include "../h/tty.h"
#include "../h/dir.h"
#include "../h/user.h"

extern int	ncli;		/* number of terminals */
extern char	cliaddr[];	/* cli addresses */
extern struct tty cli[];	/* common tty structure */

#define	WINIT	WOPEN		/* waiting to change mode read->write */

/* Current Loop Interface Command and Status Bits */

	/* commands */

#define	DISABLE	0200
#define	ENABLE	0100
#define	UNBLOCK	0040
#define	BLOCK	0020
#define	WRITE	0010
#define	READ	0004

	/* status */

#define	OV	0200
#define	BRK	0040
#define	BSY	0010
#define	EX	0004
#define	DU	0001

/*
 * open routine:
 *	called each time a process opens a terminal as a character file
 *
 *	- if the terminal was previously inactive, set up the initial status
 *	  and arm interrupts
 */
cliopen(dev,flag)
{
	register struct tty *tp;
	int clistart();

	if (minor(dev) >= ncli) {	/* minor device number too large ? */
		u.u_error = ENXIO;	/* yes, return error */
		return;
	}
	tp = &cli[minor(dev)];		/* get tty struct for terminal */
	if ((tp->t_state & ISOPEN) == 0) {
		tp->t_dev = dev;	/* set device number in tty struct */
		tp->t_state = ISOPEN | CARR_ON;
		if (tp->t_flags == 0)
			tp->t_flags = XTABS | LCASE | CRMOD | ECHO;
		ttychars(tp);
         	tp->t_oproc = &clistart;

		cliparam(tp);		/* enable read and set parms */
		rd(cliaddr[minor(dev)]);
	}
	ttyopen(dev, tp);
}


/*
 * close routine:
 *	- called only when last process using terminal releases it
 */
cliclose (dev)
{
	register struct tty *tp;

	tp = &cli[minor(dev)];		/* find tty struct for terminal */
	ttyclose(tp);
	oc(cliaddr[minor(dev)], DISABLE | READ | BLOCK);	/* disable read */
}

/*
 * read, write, ioctl routines:
 *	- call standard tty routines
 */
cliread(dev)
{
	ttread(&cli[minor(dev)]);
}

cliwrite(dev)
{
	ttwrite(&cli[minor(dev)]);
}

cliioctl(dev, cmd, addr, flag)
caddr_t addr;
{
	register struct tty *tp;

	tp = &cli[minor(dev)];
	if (ttioccom(cmd, tp, addr, dev) == 0) {
		u.u_error = ENOTTY;
		return;
	}
	if ( cmd == TIOCSETP && !(tp->t_state & BUSY))  /* if read and ECHO */
		cliparam(tp);			/* ... set hardware echo */
}


/* 
 *	set device parameters
 *      enable read; if ECHO, enable hardware echo.
 *
 */
cliparam(tp)
struct tty *tp;
{
/***
	oc(cliaddr[minor(tp->t_dev)], ENABLE | READ | ((tp->t_flag & ECHO) ?
		    UNBLOCK : BLOCK));
***/	oc(cliaddr[minor(tp->t_dev)], ENABLE | READ | BLOCK);
	return;
}

/*
 *
 *	When the interface changes from read to write, an interrupt
 *	is generated.  This is where we enable the transmit side.
 *	If busy is set , then a write is in progress and there is
 *	nothing to do.
 *
 */
clistart(tp)
register struct tty	*tp;
{
	if ( !(tp->t_state & (TIMEOUT|TTSTOP|BUSY))) {
		tp->t_state |= (WINIT | BUSY);		/* set winit flag */
		oc(cliaddr[minor(tp->t_dev)], ENABLE | WRITE | BLOCK );	/* enable write */
	}
}

/*
 *	second level output start routine
 *
 *	This routine is called from  cliint on write interrupt
 *	to send a character.
 *
 */

clistrto(tp)
register struct tty	*tp;
{
	int ttrstrt();
	register c;

	/* get character to output  */

	c = getc(&tp->t_outq);
	if (c < 0)	/* no more chars on outq, start reading */
	{
		cliparam(tp);		/* enable read */
		return;
	}
	if (c > 0177) {
		/* timeout delay */
		tp->t_state |= TIMEOUT;
		timeout(ttrstrt, tp, c&0177);
	} else {
		/* write character to device */
		tp->t_state |= BUSY;
		wd(cliaddr[minor(tp->t_dev)], c);
		trace(0x1000, "wrt", ss(cliaddr[minor(tp->t_dev)]));
	}
}

/*	interrupt handler
 */
cliint(dev,stat)
int	dev;
int	stat;
{
	register int addr;
	register struct tty *tp;
	register c;

	tp = &cli[dev];
	addr = cliaddr[dev];
	if ( !(tp->t_state & ISOPEN))	/* return if device not open */
		return;
	if (tp->t_state & BUSY)  	/* if write enable or write complete */
	{
		if (stat & BRK)  		/* if BRK key hit */
		{
			trace(0x1000,"brk :",0);
			c = CINTR;	/* set character to del */
			tp->t_state &= ~(BUSY | WINIT);	/* reset flags */
			cliparam(tp);		/* reset to read mode */
			ttyinput(c, tp);
		}
		else
		if (tp->t_state & WINIT)  
		{
			/* if WINIT, interrupt was write enable */
         		trace(0x1000, "WINIT", stat);
         		tp->t_state &= ~(BUSY | WINIT);
         		clistrto(tp);
		}
		else 
		{
         		/* else interrupt was write complete */
         		trace(0x1000, "wint", stat);
         		tp->t_state &= ~BUSY;
         		clistrto(tp);
         
#ifdef  UCB_TTHIWAT
			if (tp->t_outq.c_cc <= TTLOWAT(tp) && (tp->t_state&ASLEEP))
#else   UCB_TTHIWAT
         		if (tp->t_outq.c_cc <= TTLOWAT && (tp->t_state&ASLEEP))
#endif  UCB_TTHIWAT
         		{
         			tp->t_state &= ~ASLEEP;
         			wakeup(&tp->t_outq);
         		}
		}
	}
	else	/*  read complete interrupt */
	{
		trace(0x1000, "rint", stat);
		c = rd(addr);
		trace(0x1000, "c = ", c);
		ttyinput(c, tp);
	}
	trace(0x1000,"int exit ss:",ss(addr));
	trace(0x1000,"t_state",tp->t_state);
	trace(0x1000,"addr",addr);
}
