#
#include "/usr/sys/param.h"
#include "/usr/sys/user.h"
#include "/usr/sys/buf.h"
typedef struct buf *bufptr;
/*
 *	Ring interface 
 *	Peter Collinson September 1979
 *	This interface deals with 'packets' in the ring sense
 */

#define RNADDR	0164040

/*
 *	Ring data structure
 */

struct ring
{	bufptr	rnin;		/* Ring input q */
	bufptr	rnout;		/* Ring output q */
	int	*rdptr;		/* Used by interrupt routine */
	int	*wtptr;		/* ditto for write */
	int	flag;		/* Internal flag */
	int	errcnt;		/* Error count for transmit retries */
				/* The following entries are
				 * accessible by user using
				 * stty/gtty functions - they
				 * mirror the contents of the
				 * ring interface
				 */
	char	raddr;		/* Station receiver address */
	char	srcsel;		/* Station source select */
	char	rstat;		/* Ring status byte */
	char	saddr;		/* Transmit station address */
	int	retries;	/* User settable retry count */
} ring;

/*
 *	Flag states
 */
#define OPEN	01		/* Device in use */
#define WTERR	02		/* Error on write */
#define RNREAD	04		/* Reading from the ring */
#define RNWRITE 010		/* Writing to ring */

/*
 * 	Priorities
 */
#define RNIPRI	11
#define RNOPRI	21
#define RETRIES 1000		/* Default retries on transmit */

/*
 *	Source selectors
 */
#define NOSOURCE	0
#define ANYSOURCE	0377

/*
 *	Ring status bits
 *	Note these are asserted when they are zero
 */
#define TB0	01			/* Ring busy */
#define TB1	02			/* Status unselected */
#define TB2	04			/* Transfer OK */
#define TB3	010			/* Status ignored */
#define TB4	020			/* Error packet */
#define RB6	0100			/* Packet from unselected station
					 * pending */
#define RB7	0200			/* ?? */
#define ALLT	(TB0|TB1|TB2|TB3|TB4)
#define TRANSOK	(TB0|TB1|TB3|TB4)

/*
 * Ring interface structure
 */
struct
{	int	rn_rbuf;		/* Receiver buffer */
	int	rn_raddr;		/* Receive address buffer */
	int	rn_srcsel;		/* Source select */
	int	rn_stat;		/* Ring status */
	int	rn_sbuf;		/* Transmit buffer */
	int	rn_saddr;		/* Transmit station address */
	int	rn_na;			/* Not used */
	int	rn_csr;			/* Control status register */
};

/*
 *	csr status bits
 */
#define RECENB(a)	a++
#define ENBINP	01		/* Enable receive */
#define ENBOP	02		/* Enable output-also by writing into rn_sbuf */
#define ONOFF	020		/* Status of On/off switch */
#define RINTOFF 040		/* Turn receive interrupts off */
#define RINT	0100		/* Turn receive interrupts on */
#define TRDY	0200		/* Transmitter ready */
#define TINTOFF 0400		/* Turn transmit interrupt off */
#define TINT	01000		/* Transmit interrupt enable */
#define RRDY	0100000		/* Receive ready */
#define OFF	012000|RINTOFF|TINTOFF	/* Turn all pending interrupts off */


/*
 *	open routine
 *	see if ring is in use and powered up
 *	if not clear structure
 *	and set up interface
 */

rnopen()
{	if((ring.flag & OPEN) || (RNADDR->rn_csr & ONOFF) == 0)
	{	u.u_error = EIO;
		return;
	}

	ring.rnin = getblk(NODEV);
	ring.rnout = getblk(NODEV);
	ring.flag = OPEN;
	ring.raddr = ring.srcsel = ring.saddr = ring.rstat = '\0';
	ring.retries = RETRIES;
	rnparam();		/* set structure into interface */
	RNADDR->rn_csr = RINT|TINT;
}

/*
 *	Close routine
 *	clear everything down
 */
rnclose()
{	spl5();
	RNADDR->rn_srcsel = 0;		/* clear source select */
	brelse(ring.rnin);
	brelse(ring.rnout);
	RNADDR->rn_csr = OFF;
	RNADDR->rn_saddr = 0;
	ring.flag = 0;
	spl0();
}

/*
 *	Read routine called from top level
 *	On entry
 *	u.u_count contains the number of bytes to be read
 *	this number is passed to the interrupt routine
 *	and return is effected to the user when this
 *	happens - getting out of this uses a time-out
 */
rnread()
{	register bufptr bp;
	register n;

	bp = ring.rnin;
	if(((n = u.u_count) == 0) || n > 512)
	{	u.u_error = EIO;
		return;
	}
	spl5();
	bp->b_wcount = (n + 1)&~01; /* Make count even */
	ring.flag =| RNREAD;
	ring.rdptr = bp->b_addr;	/* set up start pointer */
	RECENB(RNADDR->rn_csr);
	while(bp->b_wcount)
		sleep(&ring.rnin, RNIPRI);
	ring.flag =& ~RNREAD;
	spl0();
	iomove(bp, 0, n, B_READ);
}

/*
 *	Receive interrupt routine
 */
rnrint()
{
	if((ring.flag & RNREAD) && (RNADDR->rn_csr&RRDY))
	{	*ring.rdptr++ = RNADDR->rn_rbuf;
		ring.raddr = RNADDR->rn_raddr;
		if(ring.rnin->b_wcount =- 2)
			RECENB(RNADDR->rn_csr);
		else
			wakeup(&ring.rnin);
	}
}

/*
 *	Write routine
 */
rnwrite()
{
	register bufptr bp;
	register n;


	bp = ring.rnout;
	if(((n = u.u_count) == 0) || n > 512)
	{	u.u_error = EIO;
		return;
	}
	spl5();
	bp->b_wcount = (n + 1) & ~01;
	ring.flag =| RNWRITE;
	ring.flag =& ~WTERR;
	ring.wtptr = bp->b_addr;
	iomove(bp, 0, n, B_WRITE);
	rnstart();
	while(bp->b_wcount && !(ring.flag&WTERR))
		sleep(&ring.rnout, RNOPRI);
	ring.flag =& ~RNWRITE;
	spl0();
	if(ring.flag&WTERR)
	{	u.u_count = bp->b_wcount;
		u.u_error = EIO;
	}
}

/*
 *	Start routine - start outputting to ring
 */
rnstart()
{
	register bufptr bp = ring.rnout;
	register flg = ring.flag;
	if((flg & RNWRITE) == 0 || (bp->b_wcount == 0) ||
	   (flg & WTERR))
	{	wakeup(&ring.rnout);
		return;
	}
	if(RNADDR->rn_csr  & TRDY)
	{
		ring.errcnt = 0;
		RNADDR->rn_sbuf = *ring.wtptr++;
		bp->b_wcount =- 2;
	}
}

/*
 *	This routine is called (at priority 5)
 *	from the clock routine by timeout
 *	timer set running every 16th retry fail
 */
rnretry()
{	RNADDR->rn_csr =| ENBOP;	}

/*
 *	Ring transmit interrupt
 */
rntint()
{
	if(RNADDR->rn_csr & TRDY)
	{
		if(((ring.rstat = RNADDR->rn_stat)&ALLT) != TRANSOK)
		{	if(++ring.errcnt >= ring.retries)
				ring.flag =| WTERR;
			else
			{	if((ring.errcnt&017) == 0)
					timeout(&rnretry, 0, 1);
				else
				RNADDR->rn_csr =| ENBOP;
				return;
			}
		}
		rnstart();
	}
}

/*
 *	Stty/gtty routine
 *	if gtty - u.u_arg[0] == 0
 *	if stty - u.u_arg[0] == 1
 *	u.u_arg[1] is address of users area
 *	sends and receives the following structure
 */
struct
{
	int	sctrl;		/* Control Word not touched by gtty */
	char	sraddr;		/* rec station address  */
	char	ssrcsel;	/* sel src station address  */
	char	srstat;		/* received status  */
	char	ssaddr;		/* send station address  */
	int	sretries;	/* user defined retry count (1000 if 0) */
} rnst;
rnsgtty()
{	register int *usr, *p;
	usr = u.u_arg[1];
	if(u.u_arg[0] == 0)
	{	/* gtty */
		p = &ring.raddr;
		suword(++usr, *p++);	/* Avoid control word */
		suword(++usr, *p++);
		suword(++usr, *p);
	}
	else
	{	/* stty */
		p = &rnst;
		if((*p++ = fuword(usr++)) == 0)		/* Flag == 0 - set all */
			p = &ring.raddr;
		*p++ = fuword(usr++);
		*p++ = fuword(usr++);
		*p = fuword(usr);
		if(rnst.sctrl == 0)
			rnparam();
		else
		{	if(rnst.sctrl&01)	/* Flag == 1 - set src select */
				RNADDR->rn_srcsel = ring.srcsel = rnst.ssrcsel;
			if(rnst.sctrl&02)	/* Flag == 2 - set send address */
				RNADDR->rn_saddr = ring.saddr = rnst.ssaddr;
			if(rnst.sctrl&04)	/* Flag == 4 - set retries */
				ring.retries = rnst.sretries;
		}
		if(ring.retries <= 0)
			ring.retries = RETRIES;
	}
}

/*
 *	Load interface from ring structure
 */
rnparam()
{	spl5();
	RNADDR->rn_srcsel = ring.srcsel;
	RNADDR->rn_saddr = ring.saddr;
	spl0();
}
