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



/*
 *   Polynet  Ring  Driver.
 *	(C)  Ross Nealon, 1982.
 *	Supports basic diagnostics, and m-p protocol only.
 *	Can be used as a data driver to a discipline driver.
 */


/*
 *   Control structures
 */
#define		RBS		512		/* Size of circ. message buffer */
#define		THRESH		(RBS/16)	/* Lower drain threshold */
#define		FW(a,b,c,d)	((a<<24) | (b<<16) | (c<<8) | (d))
struct	ringbuf	{				/* Circular buffer */
	char	r_stn;				/* Destination of msg */
	char	r_typ;				/* Type flags */
	char	r_c1;				/* Data */
	char	r_c2;				/* Data */
	};



/*
 *   Register definitions
 *	(Are really oc command bits)
 */
#define		R0		0x00		/* Source select register */
#define		R1		0x01		/* RX source / TX destination */
#define		R2		0x02		/* Node address */
#define		R3		0x03		/* TX status */
#define		R4		0x04		/* RX status / Node status */
#define		R5		0x05		/* RX / TX byte */
#define		R6		0x06		/* RX / TX byte and go */
#define		R7		0x07		/* RX / TX go */
#define		R8		0x08		/* Extended control */

/*
 *   Further oc command bits
 */
#define		DMAOUT		0x10		/* DMA start output (write) */
#define		DMAIN		0x20		/* DMA start input (read) */


/*
 *   Status bits.
 */
#define		SS_RTYP		0xc0		/* type of m-p */
#define		SS_RDN		0x20		/* RX done */
#define		SS_TDN		0x10		/* TX done */
#define		SS_BUSY		0x08		/* busy */
#define		SS_EX		0x04		/* examine */
#define		SS_TER		0x01		/* TX error */
#define		SS_RSHFT	6		/* shift RTYP down */

/*
 *   Register control and status bits.
 */
#define		R3_TDN		0x80		/* TX done */
#define		R3_TER		0x40		/* TX error */
#define		R3_BCE		0x20		/* Broadcast enabled */
#define		R3_TCE		0x10		/* TX compare error */
#define		R3_TIG		0x08		/* TX ignored */
#define		R3_TAC		0x04		/* TX accepted */
#define		R3_TUS		0x02		/* TX not selected */
#define		R3_TBY		0x01		/* TX busy */

#define		R4_RDN		0x80		/* RX done */
#define		R4_BPR		0x40		/* RX broadcast m-p */
#define		R4_RRJ		0x20		/* RX reject */
#define		R4_RTRY		0x02		/* Auto retry */
#define		R4_NEN		0x01		/* Node enabled */

#define		W4_TRST		0x08		/* TX reset */
#define		W4_RRST		0x04		/* RX reset */
#define		W4_RTRY		R4_RTRY		/* Enable auto retry */
#define		W4_NEN		R4_NEN		/* Enable node */

#define		R8_LMASK	0xe0		/* bytes / m-p */
#define		R8_LSHFT	5		/* shift for LMASK */
#define		R8_AOK		0x10		/* All ok */
#define		R8_RON		0x08		/* Ring on */
#define		R8_40B		0x04		/* 40 bit mode */
#define		R8_RTYP		0x03		/* RX type mask */

#define		W8_TTYP		R8_RTYP		/* TX type field */


/*
 *   Macros
 */
#define		INCR(x)		((x + 1) % RBS)
#define		ADVANCE(x)	x = INCR(x)


/*
 *   Storage.
 */
extern	struct	tty	ring[ ];		/* control structures */
extern	struct	inf	ringinf[ ];		/* inf structure */
extern	int		ringunit;		/* # tty structures */
	struct	tty	*ringmap[256];		/* dev -> tty map */
	struct	ringbuf	ringbuf[RBS];		/* message storage */

extern	int		ringout();
extern	int		ringin();


/*
 *   Misc. flags
 */
#define		RINGDRAIN	0x0001		/* wait for msg q to drain */
#define		RINGFLUSH	0x0002		/* wait for msg q to flush */
#define		TRC		0x2000		/* trace mask */
#define		PAD		0x00		/* NUL */

/*
 *   Type bits in message buffer
 */
#define		TXWAKE		0x10		/* Wake up after transmit */
#define		TXTYPE		0x03		/* Transmit type bits */



/*
 *   System call interface routines.
 */
ringopen(dev)
{
	register struct tty *atp;
	register struct tty *tp;
	register s;

	if (!ringcheck())  {
		/* Ring not active or not set up ok */
		u.u_error = EPERM;
		return;
		}

	s = spl4();
	if (tp = ringmap[minor(dev)])  {
		if (tp->t_state & ISOPEN)
			/* This dev already open */
			return;

		/* A discipline is set on this line, but it isn't open yet */
		tp->t_state = WOPEN;
		splx(s);
		}
	else  {
		/* Not open - search for a free tty structure */
		tp = 0;
		for (atp = &ring[0];  atp < &ring[ringunit];  atp++)
			if (!atp->t_state)  {
				/* Free */
				tp = atp;
				tp->t_state = WOPEN;
				break;
				}
	
		splx(s);
		if (!tp)  {
			/* Too many open */
			u.u_error = ENXIO;
			return;
			}
		}

	/* Set up remainder of tty struct */
	tp->t_state |= CARR_ON;
	ttychars(tp);
	tp->t_dev   = dev;
	tp->t_flags = XTABS | ECHO | CRMOD;
	tp->t_oproc = ringout;
	tp->t_iproc = ringin;

	ringmap[minor(dev)] = tp;
	(*linesw[tp->t_line].l_open)(dev, tp);
	if (!ringenable())
		u.u_error = EIO;

	return;
}


ringclose(dev)
{
	register struct tty *tp;

	tp = ringmap[minor(dev)];
	if (!tp)
		return;

	/* Drain queues immediately */
	flushtty(tp);
	if (tp->t_line)  {
		/*
		 *   Leave the tty struct allocated to the line
		 *   - for future opens to share the discipline.
		 */
		(*linesw[tp->t_line].l_close)(tp);
		tp->t_state = WOPEN;
		}
	else  {
		/*
		 *   Close the line, free the tty struct
		 */
		tp->t_state = 0;
		ringmap[minor(dev)] = 0;
		}

	ringdisable();
	return;
}


ringread(dev)
{
	register struct tty *tp;

	tp = ringmap[minor(dev)];
	if (!tp)
		return;

	(*linesw[tp->t_line].l_read)(tp);
	return;
}


ringwrite(dev)
{
	register struct tty *tp;

	tp = ringmap[minor(dev)];
	if (!tp)
		return;

	(*linesw[tp->t_line].l_write)(tp);
	return;
}


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

	tp = ringmap[minor(dev)];
	if (!tp)
		return;

	if (ttioccom(cmd, tp, addr, dev) == 0)
		u.u_error = ENOTTY;

	return;
}


/*
 *   Line discipline interface
 */
ringout(tp)
register struct tty *tp;
{
	register c1;
	register c2;
	register dev;
	register s;

	trace(TRC, "ringout", tp);

	/*
	 *   Transfer data to output queue
	 */
	dev = minor(tp->t_dev);
	s = spl4();
/*****
	while (tp->t_outq.c_cc)  {
		if (tp->t_outq.c_cc > 1)  {
			c1 = getc(&tp->t_outq);
			c2 = getc(&tp->t_outq);
			if (tp->t_outq.c_cc == 0)
				ringq(dev, TXWAKE+2, c1, c2);
			else
				ringq(dev, 2, c1, c2);
			continue;
			}

		if (tp->t_outq.c_cc == 1)  {
			c1 = getc(&tp->t_outq);
			if (tp->t_outq.c_cc == 0)
				ringq(dev, TXWAKE+1, PAD, c1);
			else
				ringq(dev, 1, PAD, c1);
			continue;
			}
		}
******/
	while (tp->t_outq.c_cc)  {
		c2 = getc(&tp->t_outq);
		if (tp->t_outq.c_cc == 0)
			/* last character */
			ringq(dev, TXWAKE+1, PAD, c2);
		else
			/* normal character */
			ringq(dev, 1, PAD, c2);
		}

	splx(s);

	/* Initiate output */
	ringstart();
	return;
}

ringin(tp)
{
	return;
}



/*
 *   Message storage.
 *	Pack data into a circular buffer.
 */
ringq(stn, typ, c1, c2)
{
	register struct ringbuf *rp;
	register s;

	s = spl4();
	while (ringinf[0].inf_nblk >= (RBS - THRESH))  {
		/* Running out of room in the buffer */
		ringinf[0].inf_flags |= RINGDRAIN;
		sleep((caddr_t) ringbuf, TTIPRI);
		}

	rp = &ringbuf[ringinf[0].inf_hd];
	rp->r_stn = stn;
	rp->r_typ = typ;
	rp->r_c1  = c1;
	rp->r_c2  = c2;

	ADVANCE(ringinf[0].inf_hd);
	ringinf[0].inf_nblk++;
	splx(s);
	return;
}


/*
 *   Ring and node status - make sure ring is active.
 */
ringcheck()
{
	register stat;
	register addr;
	register int bcount;

	addr = ringinf[0].inf_addr;
	if (ss(addr) & SS_EX)
		/* False sync */
		return(0);

	/* Extended control status */
	oc(addr, R8);
	stat = rd(addr);
	trace(TRC, "ringcheck", stat);

	if ((stat & (R8_RON|R8_40B)) != (R8_RON|R8_40B))
		/* Ring not operational */
		return(0);

	bcount = (stat & R8_LMASK) >> R8_LSHFT;
	if (bcount != 2)
		printf("ring: bad byte count %d (%x)\n", bcount, stat);

	/* Ring set up ok */
	return(1);
}


/*
 *   Turn node on for the first user.
 */
ringenable()
{
	if (ringinf[0].inf_count++)
		/* Once only enable already done */
		return(1);

	return(ringprime());
}


/*
 *   Turn node off if no more users.
 */
ringdisable()
{
	register addr;

	if (--ringinf[0].inf_count)
		/* More using the ring */
		return;

	addr = ringinf[0].inf_addr;
	oc(addr, DISARM);

	/* Ignore all data */
	oc(addr, R0);
	wd(addr, 0);

	/* Disable node and auto re-try */
	oc(addr, R4);
	wd(addr, 0);

	return;
}


/*
 *   Program (prime) the station.
 */
ringprime()
{
	register addr;
	register stat;

	addr = ringinf[0].inf_addr;
	oc(addr, DISARM);

	/* enable node */
	oc(addr, R4);
	wd(addr, W4_TRST|W4_RRST|W4_NEN);
	if (!(rd(addr) & R4_NEN))  {
		printf("ring: enable failed\n");
		return(0);
		}

	/* Ignore any packets received */
	oc(addr, R0);
	wd(addr, 0);

	/* Clear rx done */
	oc(addr, R7);
	rd(addr);
	rd(addr);

	/* Check status of node - has it powered up ok? */
	oc(addr, R3);
	stat = rd(addr);
	if (!(stat & R3_TDN))  {
		trace(TRC, "ring!TDN", stat);
		return(0);
		}

	stat = ss(addr) & (SS_RDN|SS_TDN|SS_TER|SS_BUSY|SS_EX);
	if (stat != SS_TDN)  {
		trace(TRC, "ring!SS", stat);
		return(0);
		}

	/* Enable auto-retry */
	oc(addr, R4);
	wd(addr, W4_RTRY|W4_NEN);
	stat = rd(addr);
	if (stat != (R4_RTRY|R4_NEN))  {
		trace(TRC, "ring!RX", stat);
		wd(addr, 0);
		return(0);
		}

	/* Set RX selector to anyone */
	oc(addr, R0);
	wd(addr, 255);

	oc(addr, ENABLE);
	return(1);
}


/*
 *   Output synchronization and control.
 *	Only one output at a time is sensible, so only 1 process
 *	is selected to do the output, others leave their data for
 *	that process to write for them.  When a transmit is complete,
 *	the process running at the time of completion sends the next one.
 */
ringstart()
{
	register s;

	s = spl4();
	if (ringinf[0].inf_active)  {
		splx(s);
		trace(TRC, "ringbusy", ringinf[0].inf_active);
		return;
		}

	ringinf[0].inf_active = WIO;
	splx(s);

	/* Kick I/O off */
	ringrestart();
	return;
}


ringrestart()
{
	register struct ringbuf *rp;
	register rstat;
	register xstat;
	register addr;
	register s;

	addr = ringinf[0].inf_addr;

	if (ringinf[0].inf_hd == ringinf[0].inf_tl)  {
		/* No data left */
		trace(TRC, "ringempty", ringinf[0].inf_hd);
		ringinf[0].inf_active = DNACT;
		if (ringinf[0].inf_flags & RINGFLUSH)  {
			ringinf[0].inf_flags &= ~RINGFLUSH;
			wakeup((caddr_t) ringbuf);
			}

		return;
		}

	rp = &ringbuf[ringinf[0].inf_tl];

	/* Check ring ok */
	oc(addr, R8);
	rstat = rd(addr);
	if (!(rstat & R8_RON))  {
		/* No - try re-starting every 5 seconds */
		trace(TRC, "ringdown", rstat);
		timeout(&ringrestart, 0, 500);
		return;
		}

	/* Transmit status */
	oc(addr, R3);
	xstat = rd(addr);
	if (!(xstat & R3_TDN))  {
		/* Bad status, re-prime the node & try again in 1 second */
		ringprime();
		timeout(&ringrestart, 0, 100);
		return;
		}

	/* Transmit packet type */
	oc(addr, R8);
	wd(addr, rp->r_typ & W8_TTYP);

	/* Transmit destination */
	oc(addr, R1);
	wd(addr, rp->r_stn);

	/* Data */
	trace(TRC<<1, "ringwd", FW(rp->r_stn, rp->r_typ, rp->r_c1, rp->r_c2));
	oc(addr, R5);
	wd(addr, rp->r_c1);
	wd(addr, rp->r_c2);

	oc(addr, R7);
	wd(addr, 0);

	return;
}


/*
 *   Specific receive interrupt.
 */
ringrint(dev, stat)
{
	register struct tty *tp;
	register addr;
	register c1;
	register c2;
	register typ;
	register rstat;

	addr = ringinf[0].inf_addr;
	oc(addr, DISABLE);
	trace(TRC, "interrupt", addr);
	trace(TRC, "ringrint", stat);

	if (!(stat & SS_RDN))
		printf("ring: RDN, but not SS_RDN (%x)\n", stat);

	oc(addr, R4);
	rstat = rd(addr);
	if (!(rstat & R4_RDN))  {
		printf("ring: R4_RDN not set (%x)\n", rstat);
		oc(addr, ENABLE);
		return;
		}

	if (rstat & R4_RRJ)
		printf("ring: RRJ (%x)\n", rstat);

	/* Find source */
	if (rstat & R4_BPR)  {
		/* Broadcast */
		dev = 255;
		}
	else  {
		oc(addr, R1);
		dev = rd(addr);
		}

	/* Fetch type bits */
	oc(addr, R8);
	typ = rd(addr);

	/* Fetch data */
	oc(addr, R5);
	c1 = rd(addr);
	c2 = rd(addr);
	oc(addr, R7);
	rd(addr);
	if (ss(addr) & SS_RDN)  {
		printf("ring: read & RDN still set\n");
		ringprime();
		}

	trace(TRC<<1, "ringrd", FW(dev, typ, c1, c2));
	oc(addr, ENABLE);

	/* Determine recipient of data */
	tp = ringmap[dev];
	if (!tp)  {
		/* Try unopened monitor data queue */
		tp = ringmap[0];
		}

	if (!tp)  {
		/* All failed  - discard data */
		return;
		}


	switch (typ & R8_RTYP)  {

		case 2:
			(*linesw[tp->t_line].l_rint)(c1, tp);

		case 1:
			(*linesw[tp->t_line].l_rint)(c2, tp);
			break;

		case 0:
		case 3:
			break;
		}

	return;
}


/*
 *   Specific transmit interrupt.
 */
ringxint(dev, stat)
{
	register struct tty *tp;
	register struct ringbuf *rp;
	register xstat;
	register addr;

	addr = ringinf[0].inf_addr;
	oc(addr, DISABLE);
	trace(TRC, "interrupt", addr+1);
	trace(TRC, "ringxint", stat);

	oc(addr, R3);
	xstat = rd(addr);
	if (!(xstat & R3_TDN))  {
		printf("ring: R3_TDN not set (%x)\n", xstat);
		oc(addr, ENABLE);
		return;
		}

	if (xstat & (R3_TER|R3_TCE|R3_TIG|R3_TUS|R3_TBY))  {
		/* Potential re-try ? */
		printf("ring: tx error (%x)\n", xstat);
		}

	rp = &ringbuf[ringinf[0].inf_tl];
	if (rp->r_typ & TXWAKE)  {
		/* Wake up writer from upper half */
		tp = ringmap[rp->r_stn];
		if (tp)
			if (tp->t_state & ASLEEP)  {
				tp->t_state &= ~ASLEEP;
				wakeup((caddr_t) &tp->t_outq);
				}
		}

	ADVANCE(ringinf[0].inf_tl);
	ringinf[0].inf_nblk--;
	if ((ringinf[0].inf_flags & RINGDRAIN)  &&  (ringinf[0].inf_nblk <= THRESH))  {
		/* Waiting to queue data */
		ringinf[0].inf_flags &= ~RINGDRAIN;
		wakeup((caddr_t) ringbuf);
		}

	oc(addr, ENABLE);
	ringrestart();
	return;
}
