
/* Device driver for the vii ring network interface.  Written for the
 * uVAX-I or uVAX-II.
 */

#include "vii.h"

/* device registers */
#define	IREG	0x20001a80
#define	OREG	0x20001a88

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


/* 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 */

/* vii device initialization routine.
 */
unsigned char *vii_init()
{
    register struct vii_reg *dreg;
    register int i;
    static struct vii_hdr tpkt;

    /* Setup in Digital loopback mode for figuring out my address */
    dreg = (struct vii_reg *)IREG;
    dreg->reg_csr = RES | STEN | DLOOP;
    while (!(dreg->reg_csr & RDY))
      ;
    dreg->reg_csr |= EN;
    dreg = (struct vii_reg *)OREG;
    dreg->reg_csr = RES;

    /* find out my own address */
    for (;;) {
	tpkt.vii_dst = VII_BRD;
	tpkt.vii_src = 0;
	tpkt.vii_ver = 2;
	tpkt.vii_prot = 15;		/* Diagnostic protocol */
	tpkt.vii_rsd = 0xA5A5;
	vii_write(&tpkt.vii_dst, 6);
	puts(".");
	for (i = 0; i < 65536; i++) {
	    if (vii_read(&tpkt.vii_dst, 6) == 0) continue;
	    if (tpkt.vii_ver != 2 || tpkt.vii_prot != 15 ||
		tpkt.vii_rsd != 0xA5A5)
	      continue;
	    puts("Local address is ");
	    phex(tpkt.vii_src);

	    dreg = (struct vii_reg *)IREG;
	    dreg->reg_csr = EN | HEN;
	    return (&(tpkt.vii_src));
	}
    }
}

/* Read the next packet.  Return the length of the packet.
 * Return 0 if there is no packet ready. */
vii_read(buf, len)
register char *buf;
register int len;
{
    register struct vii_reg *dreg;

    dreg = (struct vii_reg *)IREG;
    if (!(dreg->reg_csr & RDY))
      return 0;
    dreg->reg_addrl = (unsigned short)buf;
    dreg->reg_addrh = (unsigned short)((unsigned long)buf >> 16);
    dreg->reg_wc = -((len + 1) >> 1);
    dreg->reg_csr |= DMA;
    while (!(dreg->reg_csr & RDY))
      ;
    dreg->reg_csr |= EN;
    len += dreg->reg_wc << 1;	/* Note: this is really subtracting */
    return len;
}

/* Write a packet to the net. */
vii_write(buf, len)
register char *buf;
register int len;
{
    register struct vii_reg *dreg;
    register int csr;

    dreg = (struct vii_reg *)OREG;
again:
    dreg->reg_addrl = (unsigned short)buf;
    dreg->reg_addrh = (unsigned short)((unsigned int)buf >> 16);
    dreg->reg_wc = -((len + 1) >> 1);
    dreg->reg_csr = DMA | CPB | EN | IR;
    while (!((csr = dreg->reg_csr) & RDY))
      ;
    if (csr & OERR) {
	puts("retransmitting pkt\n");
	goto again;
    }
}

/*
dump(p)
	char *p; {
	int i,j;

	for(i = 1; i < 17; i++) {
		printf("%02x ", *p++&0xff);
		if(i%16 == 0) printf("\n");
	}

	printf("\n");
	}
 */
