/* Copyright 1984 by the Massachusetts Institute of Technology */

/* Device driver for the vii ring network interface.  Written for the
 * uVAX.
 */

#include	<types.h>
#include	<sys.h>
#include	<gw/src/status.h>

/* device registers */
struct	vii_reg {
    unss	reg_csr;	/* Control/Status register */
    unss	reg_wc;		/* Word count register */
    unss	reg_addrl;	/* Bus address register (low) */
    unss	reg_addrh;	/* Bus address register (high) */
};

/* Statistics */
struct vii_stat {
    unsl ref;			/* Refused */
    unsl ovr;			/* Buffer overflow */
    unsl perr_tmo;		/* Input: parity error
				   Output: timeout */
    unsl rnok;			/* Ring not OK */
    unsl bdf;			/* Bad format */
};


/* bit definitions for the csr */
#define	EN	0x1	/* enable */
#define	DMA	0x2	/* dma enable */
#define	HEN	0x4	/* host enable (input csr) */
#define	CPB	0x4	/* clear packet buffer (output csr) */
#define	STEN	0x8	/* self test enable */
#define	DLOOP	0x10	/* digital loopback (input) */
#define	IR	0x10	/* initialize ring (output) */
#define	RES	0x20	/* reset */
#define	IEN	0x40	/* interrupt enable */
#define	RDY	0x80	/* ready */

#define	IDP	0x100	/* input data present (input) */
#define	REF	0x100	/* refused (output) */
#define	NXM	0x200	/* nxm */
#define	OVR	0x400	/* overrun */
#define	ODD	0x800	/* input odd byte count */
#define	PE	0x1000	/* parity error (input) */
#define	TMO	0x1000	/* timeout (output) */
#define	RNOK	0x2000	/* ring not ok */
#define	BDF	0x4000	/* bad format */
#define	NIR	0x8000	/* not in ring */

/* Errors */
#define	HRDERR	(NXM | NIR)		/* errors which should never occur */
#define	IERR	(IDP | OVR | PE | BDF)	/* random input errors */
#define	OERR	(REF | OVR | TMO | BDF)	/* random output errors */


/* Copies the device statistics into a buffer.  This is used to get
 * gateway statistics from over the net.  Assumes that it's called
 * with the input device.  */
int vii_stat(devp, buf)
dct *devp;
reg pro10_stat *buf;
{
    reg struct vii_stat *st;

    buf->type = D_PRONET10;
    buf->version = STAT_PRO10_VERSION;
    st = (struct vii_stat *)devp->d_dev1;
    buf->iovr = st->ovr;
    buf->perr = st->perr_tmo;
    buf->irnok = st->rnok;
    buf->ibdf = st->bdf;
    st = (struct vii_stat *)devp->d_lnk->d_dev1;
    buf->ref = st->ref;
    buf->oovr = st->ovr;
    buf->tmo = st->perr_tmo;
    buf->ornok = st->rnok;
    buf->obdf = st->bdf;
    return (sizeof(pro10_stat));
}

/* vii device initialization routine.
 */
vii_up(devp)
reg	dct	*devp;
{
	reg struct vii_reg *dreg;
	reg struct vii_stat *st;

	devp->d_dev2 = (word)vii_stat;
	devp->d_dev1 = mem_alloc(sizeof(struct vii_stat));
	if ((st = (struct vii_stat *)devp->d_dev1) == 0)
	  bughalt("No mem for VII statistics");
	st->ref = st->ovr = st->perr_tmo = st->rnok = st->bdf = 0;
	dreg = (struct vii_reg *)devp->d_csr;
	dreg->reg_csr = RES | HEN;		/* HEN is CPB on output */
	devp->d_flg = D_INI;
}

/* input transfer initialization routine */
viiin(devp)
reg	dct	*devp;
{
	reg	struct vii_reg	*dreg;

	dreg = (struct vii_reg *)devp->d_csr;
	dreg->reg_addrl = (unss)devp->d_addr;
	dreg->reg_addrh = (unss)((word)devp->d_addr >> 16);
	dreg->reg_wc = -((devp->d_breq + 1) >> 1);
	dreg->reg_csr = DMA | HEN | IEN | EN;
/*	if (dreg->reg_csr & RDY)
	  viiii(devp); */
}

/* output transfer initialization routine */
viiot(devp)
reg	dct	*devp;
{
	reg struct vii_reg	*dreg;
	reg unss	csr;

	dreg = (struct vii_reg *)devp->d_csr;
	dreg->reg_addrl = (unss)devp->d_addr;
	dreg->reg_addrh = (unss)((word)devp->d_addr >> 16);
	dreg->reg_wc = -((devp->d_breq + 1) >> 1);
	csr = dreg->reg_csr;
	dreg->reg_csr = DMA | IEN | CPB | EN | IR;
}

/* input interrupt routine */
viiii(devp)
reg	dct	*devp;
{
	struct vii_reg	*dreg;
	reg iorb	*iob;
	reg unss	csr;

	dreg = (struct vii_reg *)devp->d_csr;
	csr = dreg->reg_csr;
/*	dreg->reg_csr = HEN | EN;
	if (!(csr & RDY))
	  return; */
	if (csr & HRDERR) bughalt("VII: input hard error");
/* Changed to just restart input on input error. */
/*	if (csr & IERR) iob->i_stat |= I_ERR;	/* should return more
						 * useful information */
	if (csr & IERR) {
	    reg struct vii_stat *st = (struct vii_stat *)devp->d_dev1;
	    if (csr & OVR) st->ovr++;
	    if (csr & PE) st->perr_tmo++;
	    if (csr & BDF) st->bdf++;
	    viiin(devp);
	    return;
	}
	if (csr & RNOK)
	  ((struct vii_stat *)devp->d_dev1)->rnok++;
	iob = devp->d_qhd;
	iob->i_stat |= I_DONE;
	iob->i_bxfr = devp->d_breq + (dreg->reg_wc << 1);
	if (csr & ODD) iob->i_bxfr--;
	iocmr(devp);
}

/* output interrupt routine */
viioi(devp)
reg	dct	*devp;
{
	struct vii_reg	*dreg;
	reg iorb	*iob;
	reg unss	csr;

	dreg = (struct vii_reg *)devp->d_csr;
	iob = devp->d_qhd;
	csr = dreg->reg_csr;
	if (csr & HRDERR) bughalt("VII: output hard error");
	if (csr & OERR) {
	    reg struct vii_stat *st = (struct vii_stat *)devp->d_dev1;
	    if (csr & REF) st->ref++;
	    if (csr & OVR) st->ovr++;
	    if (csr & TMO) st->perr_tmo++;
	    if (csr & BDF) st->bdf++;
	    iob->i_stat |= I_ERR; /* should return more
				   * useful information */
		/* if the error was a refused then the packet should
		 * be retransmitted now without a new DMA.  Oh well. */
	}
	if (csr & RNOK)
	  ((struct vii_stat *)devp->d_dev1)->rnok++;
	iob->i_stat |= I_DONE;
	iob->i_bxfr = devp->d_breq;
	iocmr(devp);
}
