#
/*
 */

/*
 * TIU (DR11-B) interface to Spider
 */

#include "../../h/param.h"
#include "../../h/conf.h"
#include "../../h/user.h"
#include "../../h/buf.h"
#include "../../h/reg.h"
#include "../../h/inode.h"

#define	NCHAN	8

/* bits in tiuch flags */
#define	T_WRITR	01
#define	T_RAVL	02
#define	T_ERROR	04
#define	T_DONE	010
#define	T_OPEN	020
#define	T_EOF	040
#define	T_STOP	0100
#define	T_WRITW 0200

/* drst bits */
#define	T_TERROR 0100000
#define	T_REJ	04000
#define	T_IDLE	02000
#define	T_SON	01000

/* drdb bits */
#define	T_BKSTS	0100000
#define	T_SLSTS	040000
#define	T_WTSTS	010000
#define	T_TROUB	02000
#define	T_ODD	01000
#define	T_SIGNL	0400
#define	T_SELW	0200

#define	TIUPRI	(-1)
#define	TIULPRI	1
#define	TIUADDR	0172430

/* tiu command bits */
#define	IENABLE	0100
#define	GO	01
#define	STOP	0
#define	RCH	02
#define	RDC	04
#define	RNM	06
#define	WSB	010
#define	WCH	012
#define	WDC	014
#define	WDB	016

#define	SREAD	1
#define	SWRITE	2
#define	SWSIG	3
#define	SWDONE	4
#define	SSTOP	5
#define	SSEL	6

#define	TIMLIM	5

struct {
	int	drwc;
	int	drba;
	int	drst;
	int	drdb;
};
struct tiuch {
	char	t_flags;
	char	t_isig;
	char	t_osig;
	char	t_troub;
	char	*t_buffer;
};

struct tiuch tiu_dchan[NCHAN];
struct tiuch tiu_cchan[NCHAN];

struct tiu {
	char	t_state;
	char	t_chan;
	char	t_time;
	char	t_timo;
	char	t_nopen;
	struct	buf *t_actf;
	struct	buf *t_actl;
} tiu;

tiuopen(dev, flag)
{
	int tiutimeout();
	register struct tiuch *cp;
	register struct buf *bp;

	if ((cp = tiuptr(dev)) == NULL) {
		u.u_error = ENXIO;
		return;
	}
	if (cp->t_flags&T_OPEN) {
		u.u_error = EBUSY;
		return;
	}
	cp->t_flags = T_OPEN;
	cp->t_osig = 1;
	if (tiu.t_nopen++ == 0) {
		tiu.t_chan = -1;
		timeout(tiutimeout, 0, HZ);
	}
	cp->t_flags =| T_STOP;
	tiustart();
}

tiuclose(dev)
{
	register struct tiuch *cp;

	if ((cp = tiuptr(dev)) == 0)
		return;
	cp->t_flags =| T_STOP;
	tiustart();
	cp->t_flags =& T_STOP;
	tiu.t_nopen --;
}

tiuwrite(dev)
{
	register int n;
	register struct tiuch *cp;
	register struct buf *bp;

	if ((cp = tiuchan(dev)) == NULL)
		return;
	do {
		spl5();
		if ((cp->t_flags&T_WRITR) == 0) {
			cp->t_flags =| T_WRITW;	/* want select W */
			tiustart();
		}
		while ((cp->t_flags&(T_WRITR|T_ERROR|T_EOF)) == 0)
			sleep(cp, TIULPRI);
		spl0();
		if (tiucheck(cp))
			return;
		bp = getblk(NODEV);
		if (n = min(512, u.u_count))
			iomove(bp, 0, n, B_WRITE);
		if (u.u_count == 0 || u.u_error)
			bp->b_blkno = cp->t_osig;
		else
			bp->b_blkno = 0;
		bp->b_dev.d_minor = dev.d_minor;
		bp->b_wcount = n;
		bp->b_flags = B_WRITE;
		spl5();
		cp->t_flags =& ~T_DONE;
		tiustrategy(bp);
		while ((cp->t_flags&(T_DONE|T_ERROR|T_EOF)) == 0)
			sleep(cp, TIUPRI);
		spl0();
		brelse(bp);
		if (tiucheck(cp))
			return;
	} while (u.u_count);
}

tiuread(dev)
{
	register int n;
	register struct tiuch *cp;
	register struct buf *bp;

	if ((cp = tiuchan(dev)) == NULL)
		return;
	spl5();
	while ((cp->t_flags&(T_RAVL|T_ERROR|T_EOF))==0)
		sleep(cp, TIULPRI);
	spl0();
	if (tiucheck(cp))
		return;
	bp = getblk(NODEV);
	bp->b_flags = B_READ;
	bp->b_dev.d_minor = dev.d_minor;
	cp->t_flags =& ~T_DONE;
	tiustrategy(bp);
	while ((cp->t_flags&(T_DONE|T_ERROR|T_EOF)) == 0)
		sleep(cp, TIUPRI);
	spl0();
	if (cp->t_isig = bp->b_blkno)
		cp->t_flags =& ~T_RAVL;
	if (tiucheck(cp) == 0) {
		if (n = min(bp->b_wcount, u.u_count))
			iomove(bp, 0, n, B_READ);
	}
	brelse(bp);
}

tiucheck(acp)
struct tiuch *acp;
{
	register struct tiuch *cp;

	cp = acp;
	if (cp->t_flags & (T_EOF | T_ERROR)) {
		if (cp->t_flags&T_ERROR)
			u.u_error = EIO;
		return(1);
	}
	return(0);
}

tiustart()
{
	register struct buf *bp;
	register i;

	if (tiu.t_state)
		return;
	for (i=0; i<NCHAN; i++) {
		if (tiucntrl(i, &tiu_dchan[i])
		 || tiucntrl(i+64, &tiu_cchan[i]))
		return;
	}
	if ((bp = tiu.t_actf)==0)
		return;
	if (stiuchan(bp->b_dev.d_minor) == 0)
		return;
	TIUADDR->drba = bp->b_addr;
	if (bp->b_flags&B_READ) {
		TIUADDR->drwc = -257;
		tiu.t_state = SREAD;
		TIUADDR->drst = IENABLE|RDC|GO;
	} else {
		tiu.t_state = SWRITE;
		if ((TIUADDR->drwc = -(bp->b_wcount>>1))==0) {
			tiuintr();
			return;
		}
		TIUADDR->drst = IENABLE|WDC|GO;
	}
	tiu.t_time = TIMLIM;
}

tiucntrl(chan, acp)
struct tiuch *acp;
{
	register struct tiu *cp;

	cp = acp;
	if (cp->t_flags&T_STOP) {
		if (stiuchan(chan+0400)) {
			cp->t_flags =& ~T_STOP;
			tiu.t_state = SSTOP;
			tiu.t_time = TIMLIM;
		}
		return(1);
	}
	if (cp->t_flags&T_WRITW) {
		if (stiuchan(chan+0200)) {
			cp->t_flags =& ~T_WRITW;
			tiu.t_time = TIMLIM;
		}
		return(1);
	}
	return(0);
}

stiuchan(ac)
{
	register int c;

	c = ac;
	if (c != tiu.t_chan) {
		if ((TIUADDR->drst&T_IDLE)==0 || (TIUADDR->drdb&T_SLSTS)==0) {
			tiu.t_state = SSEL;
			tiu.t_time = TIMLIM;
			return(0);
		}
		tiu.t_chan = c&0177;
		TIUADDR->drdb = c;
		TIUADDR->drst = IENABLE|WCH|GO;
	}
	return(1);
}

tiutimeout()
{
	tiu.t_time--;
	if (tiu.t_time == 0) {
		tiu.t_timo++;
		tiuintr();
	} else if (tiu.t_time < 0)
		tiu.t_time = 0;
	if (tiu.t_nopen)
		timeout(tiutimeout, 0, HZ);
}

tiuintr()
{
	register struct buf *bp;
	register struct tiuch *cp;
	struct tiuch *lastcp;
	register int s;

	s = tiu.t_state;
	if ((s==SSTOP || s==SSEL) && tiu.t_timo==0
	 && ((TIUADDR->drst&T_IDLE)==0 || (TIUADDR->drdb&T_SLSTS)==0))
		return;
	tiu.t_time = 0;
	cp = tiuptr(tiu.t_chan);
	tiu.t_state = 0;
	bp = NULL;
	if (s && s!=SSTOP && s!=SSEL) {
		bp = tiu.t_actf;
		if (bp==NULL || cp==NULL) {
			tiuerr(-1, 0);
			goto done;
		}
	}
	if ((TIUADDR->drst&(T_TERROR|T_REJ)) || tiu.t_timo) {
		tiuerr(tiustop(), 040+s+040*(tiu.t_timo!=0));
		goto done;
	}
	if (TIUADDR->drdb&T_TROUB) {
		tiuerr(tiustop(), TIUADDR->drdb);
		goto done;
	}
	switch (s) {

	case SSTOP:
		tiustop();
		goto done;

	case SREAD:
		if ((TIUADDR->drdb&T_SIGNL) == 0) {
			tiuerr(tiustop(), 10);
			goto done;
		}
		s = 513 + (TIUADDR->drwc<<1);
		if ((TIUADDR->drdb&T_ODD) != 0)
			s++;
		bp->b_wcount = s;
		bp->b_blkno = (TIUADDR->drdb).lobyte;
		s = SREAD;
		goto done;

	case SWRITE:
		if (bp->b_wcount&01) {
			TIUADDR->drdb = bp->b_addr[bp->b_wcount-1];
			TIUADDR->drst = IENABLE|WDB|GO;
			if ((TIUADDR->drst & T_IDLE)==0) {
				tiu.t_state = SWSIG;
				tiu.t_time = TIMLIM;
				return;
			}
		}

	case SWSIG:
		cp->t_flags =& ~T_WRITR;
		TIUADDR->drdb = bp->b_blkno;
		TIUADDR->drst = IENABLE|WSB|GO;
		if ((TIUADDR->drst & T_IDLE)==0) {
			tiu.t_state = SWDONE;
			tiu.t_time = TIMLIM;
			return;
		}

	done:
	case SWDONE:
	case SSEL:
		if (bp && tiu.t_actf)
			tiu.t_actf = bp->av_forw;
		if (cp) {
			wakeup(cp);
			cp->t_flags =| T_DONE;
		}

	default:
		if (TIUADDR->drdb&T_WTSTS && cp) {
			cp->t_flags =| T_WRITR;
			wakeup(cp);
		}
		lastcp = cp;
		while (TIUADDR->drdb&T_BKSTS) {
			TIUADDR->drst = IENABLE|RCH|GO;
			if ((cp = tiuptr(TIUADDR->drdb)) == NULL)
				tiuerr(-1, 0);
			else if (s==0 || lastcp != cp) {
				cp->t_flags =| (TIUADDR->drdb&T_SELW)!=0?
					T_RAVL:T_WRITR;
				wakeup(cp);
			}
		}
		tiustart();
	}
}

tiustop()
{
	register int lastchan;

	lastchan = tiu.t_chan;
	tiu.t_chan = -1;
	tiu.t_state = 0;
	TIUADDR->drst = IENABLE|STOP|GO;
	return(lastchan);
}

tiuerr(chan, acode)
{
	register struct tiuch *cp;
	register code;

	tiu.t_timo = 0;
	code = acode;
	if ((cp = tiuptr(chan)) != NULL)
		tiucherr(cp, code);
	else {
		tiu.t_state = 0;
		tiu.t_actf = 0;
		for (cp = tiu_dchan; cp < &tiu_dchan[2*NCHAN]; cp++)
			tiucherr(cp, code);
	}
}

tiucherr(acp, code)
struct tiuch *acp;
{
	register struct tiuch *cp;

	cp = acp;
	cp->t_flags =& ~(T_WRITR|T_RAVL);
	cp->t_flags =| T_ERROR|T_DONE;
	cp->t_troub = code;
	wakeup(cp);
}

tiuchan(dev)
{
	register struct tiuch *cp;

	if ((cp = tiuptr(dev)) == NULL) {
		u.u_error = ENXIO;
		return(NULL);
	}
	if (cp->t_flags&(T_ERROR|T_EOF)) {
		if (cp->t_flags&T_ERROR)
			u.u_error = EIO;
		return(NULL);
	}
	return(cp);
}

tiustrategy(abp)
struct buf *abp;
{
	register struct buf *bp;

	bp = abp;
	bp->av_forw = NULL;
	if (tiu.t_actf==NULL) {
		tiu.t_actf = bp;
		tiustart();
	} else
		tiu.t_actl->av_forw = bp;
	tiu.t_actl = bp;
}

snstat(dev, v)
{
	register struct tiu *cp;
	register int channo;

	channo = dev.d_minor;
	if ((cp = tiuptr(channo))==NULL)
		return;
	switch (u.u_arg[1]) {

	/* set signal byte */
	case 0:
		cp->t_osig = fubyte(u.u_arg[0]);
		return;

	/* get signal byte */
	case 1:
		suword(u.u_arg[0], cp->t_isig);
		cp->t_isig = 0;
		return;

	/* get channel # */
	case 2:
		suword(u.u_arg[0], channo);
		return;

	/* get trouble code */
	case 3:
		suword(u.u_arg[0], cp->t_troub);
		cp->t_troub = 0;
		cp->t_flags =& ~T_ERROR;
		return;

	/* clear EOF request */
	case 4:
		if (channo >= 64)
			cp = tiuptr(channo - 64);
		cp->t_flags =& ~T_EOF;
		return;

	/* set EOF request */
	case 5:
		if (channo >= 64)
			cp = tiuptr(channo - 64);
		cp->t_flags =| T_EOF;
		wakeup(cp);
		return;

	}
	u.u_error = EINVAL;
}

tiuptr(dev)
{
	register struct tiuch *cp;
	register int d;

	d = dev.d_minor & 0177;
	if (d<NCHAN)
		return(&tiu_dchan[d]);
	if (d>=64 && d<NCHAN+64)
		return(&tiu_cchan[d-64]);
	return(NULL);
}

