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

/* Device driver for the deqna (DEC Qbus ethernet interface).
 * This driver is written for the uVAX I or II.
 */

#include	<types.h>
#include	<sys.h>
#include	"../src/const.h"
#include	"../src/param.h"
#include	"../src/defs.h"
#include	"../src/macs.h"
#include	"../src/net.h"
#include	"../src/ext.h"
#include	"../src/status.h"

#define	ETHMINSIZ	64

ext intf reboot;		/* If set, enables deqna reboot timer. */
int qna_restart = 0;		/* Keeps track of the number of times
				 * the deqna is restarted. */

/* Redefinitions of the device structure */
#define d_check d_rhd
#define d_count d_rtl

/*
 * Device register definition
 */

struct qedevice {
	union {
	    unss	_physAddr[6];
	    struct {
		unss	_unused[2];
		unss	_rcvLoBDL;	/* low 16 bits of addr */
		unss	_rcvHiBDL;	/* high bits + status*/
		unss	_xmitLoBDL;	/* as for receive */
		unss	_xmitHiBDL;
		unss	_vector;	/* interrupt vector */
		unss _csreg;		/* Control and Status */
	    }_regs;
	}_dev;
};

#define	qe_physAddr	_dev._physAddr
#define	qe_rcvLoBDL	_dev._regs._rcvLoBDL
#define	qe_rcvHiBDL	_dev._regs._rcvHiBDL
#define qe_xmitLoBDL	_dev._regs._xmitLoBDL
#define	qe_xmitHiBDL	_dev._regs._xmitHiBDL
#define	qe_vector	_dev._regs._vector
#define	qe_csreg	_dev._regs._csreg

/* 
 * Control and Status Register bit definitions
 */

#define	QE_RCV_ENBL	0000001		/* Receiver Enable */
#define	QE_RESET	0000002		/* Reset */
#define	QE_NXM		0000004		/* Nonexistent Memory Interrupt */
#define	QE_BDROM	0000010		/* Boot/Diagnostic ROM */
#define	QE_XL_INV	0000020		/* Transmit List Invalid */
#define	QE_RL_INV	0000040		/* Receive List Invalid */
#define	QE_IE		0000100		/* Interrupt Enable */
#define	QE_XMIT_IR	0000200		/* Transmit Interrupt Request */
#define	QE_INT_LOOP	0000400		/* Internal Loopback (active low!) */
#define	QE_EXT_LOOP	0001000		/* External Loopback */
#define	QE_SANITY_ENBL	0002000		/* Sanity Timer Enable */
 					/* 0004000 reserved */
#define	QE_FUSE		0010000		/* Bulkhead Fuse OK */
#define	QE_CARRIER	0020000		/* Carrier Present */
					/* 0040000 reserved */
#define	QE_RCV_IR	0100000		/* Receive Interrupt Request */


/* Magic setup bits for length */
#define	QE_MULTICAST	0000001		/* Receive all multicast packets */
#define	QE_PROMISCUOUS	0000002		/* Receive all packets */
#define	QE_LED1_OFF	0000004		/* turn off LED 1 */
#define	QE_LED2_OFF	0000010		/* turn off LED 2 */
#define	QE_LED3_OFF	0000014		/* turn off LED 3 */
					/* turn all on by toggling QE_BDROM */
#define QE_SANITY_4thSEC 0x0000	/* 1/4 second */
#define QE_SANITY_1SEC	0x0010	/* 1 second */
#define QE_SANITY_4SEC	0x0020	/* 4 seconds */
#define QE_SANITY_16SEC	0x0030	/* 16 seconds */
#define QE_SANITY_1MIN	0x0040	/* 1 minute */
#define QE_SANITY_4MIN	0x0050	/* 4 minutes */
#define QE_SANITY_16MIN	0x0060	/* 16 minutes */
#define QE_SANITY_64MIN	0x0070	/* 64 minutes */

/*
 * Buffer descriptor list
 */

struct BDL {
	unss	bdl_flag;
	unss	bdl_desc;
	unss	bdl_addr;
	unss	bdl_len;
	unss	bdl_st1;
	unss	bdl_st2;
};

/*
 * Flag bits
 */

#define INVALID 0x0000
#define	INIT	0x8000
#define	USING	0xc000

/*
 * Address Descriptor bit definitions
 */

#define	VALID	0x8000
#define	CHAIN	0x4000
#define	EOM	0x2000
#define	SETUP	0x1000
#define	LBYTE	0x80
#define	HBYTE	0x40

/*
 * Transmit Status Word 1
 */

#define	LASTNOT	0x8000
#define	ERROR	0x4000
#define	LOSS	0x1000
#define	NOCAR	0x800
#define	STE16	0x400
#define	ABORT	0x200
#define	FAIL	0x100
#define	COUNT	0xf0
#define	GET_COUNT(x)	(((x)&COUNT)>>4)

/*
 * Receive Status Word 1
 */

#define	ESETUP	0x2000
#define	DISCARD	0x1000
#define	RUNT	0x800
#define	RBL	0x700
#define	FRAME	0x4
#define	CRERR	0x2
#define	OVF	0x1


/* Get Recieved Byte Length */
#define	GET_RBL(x)	( ((x)->bdl_st1 & RBL) | ((x)->bdl_st2 & 0xff) )

/*
 * Address and BDL Structure
 */

struct	adbdl {
	byte a_addr[6];
	struct BDL a_bdl;
	struct BDL a_bdl2;	/* This one is just used to terminate
				 * the implicit list of bdl's */
	word unused[2];		/* The FCO sheet said two words should
				 * be saved after the list because
				 * they got trashed.  (piece of shit) */
	word a_stat;		/* Pointer to statistics structure */
};

/*
 * Statistics structures.
 */
struct qna_rstat {		/* receive stats */
    unsl runt;
    unsl frame;
    unsl crc;
    unsl ovf;
};

struct qna_tstat {		/* transmit stats */
    unsl loss;			/* Lost carrier after start of transmission */
    unsl carr;			/* No carrier */
    unsl abort;			/* Abort due to 16 collisions */
    unsl restarts;		/* Device timeout restarts */
};


/*
 * Buffer for setup.
 */
byte	setup_buf[16][8];

char qnanoalloc[] = "QNA: Couldn't alloc";


/* Fill in a buffer with DEQNA statistics.  Assumes that it gets
 * passed the input device. */
int qna_stat_fill(devp, buf)
dct *devp;
qna_stat *buf;
{
    struct qna_rstat *rst;
    struct qna_tstat *tst;

    buf->type = D_DEQNA;
    buf->version = STAT_DEQNA_VERSION;
    rst = (struct qna_rstat *)(((struct adbdl *)(devp->d_dev1))->a_stat);
    buf->runt = rst->runt;
    buf->frame = rst->frame;
    buf->crc = rst->crc;
    buf->ovf = rst->ovf;
    tst = (struct qna_tstat *)(((struct adbdl *)(devp->d_lnk->d_dev1))->a_stat);
    buf->loss = tst->loss;
    buf->carr = tst->carr;
    buf->abort = tst->abort;
    buf->restarts = tst->restarts;
    return (sizeof(qna_stat));
}

/* Device input initialization routine.  This resets the interface, sets its
 * interrupt vector, allocates the BDL structure and leaves the local net
 * address in a structure pointed to by devp->d_dev1.  The input vector must
 * be properly defined but the output interrupt vector *must* be some bogus
 * value.
 */
qna_in_up(devp)
reg	dct	*devp;
{
	reg	struct qedevice	*dreg;
	reg	struct adbdl	*a;
		struct qna_rstat *st;

	devp->d_dev2 = (word)qna_stat_fill;
	devp->d_flg |= D_INI;
	dreg = (struct qedevice *)devp->d_csr;
	a = (struct adbdl *)mem_alloc(sizeof(struct adbdl));
	if (a == 0)
	  bughalt(qnanoalloc);
	a->a_addr[0] = dreg->qe_physAddr[0];
	a->a_addr[1] = dreg->qe_physAddr[1];
	a->a_addr[2] = dreg->qe_physAddr[2];
	a->a_addr[3] = dreg->qe_physAddr[3];
	a->a_addr[4] = dreg->qe_physAddr[4];
	a->a_addr[5] = dreg->qe_physAddr[5];
	a->a_bdl.bdl_flag = INIT;
	a->a_bdl2.bdl_flag = INVALID;
	a->a_bdl2.bdl_desc = INVALID;

	st = (struct qna_rstat *)mem_alloc(sizeof(struct qna_rstat));
	if (st == 0) bughalt(qnanoalloc);
	st->runt = st->frame = st->crc = st->ovf = 0;
	a->a_stat = (word)st;
	devp->d_dev1 = (word)a;

	dreg->qe_csreg = QE_RESET;
	dreg->qe_csreg &= ~QE_RESET;
	dreg->qe_vector = (unss)devp->d_iva & 0x1fc;

	do_setup(devp, a->a_addr, setup_buf);

	dreg->qe_csreg &= ~QE_EXT_LOOP;
	dreg->qe_csreg |= (QE_IE | QE_RCV_ENBL | QE_INT_LOOP);
}

/*
 * Fill in setup packet and send it.
 */
do_setup(devp, addr, buf)
reg	dct	*devp;
byte	addr[6];
byte	buf[16][8];
{
	reg	intf	i,j;
	reg	struct qedevice	*dreg;

	for (i = 0; i < 6; i++) {
		buf[i+8][0] = buf[i][0] = 0;	/* boundries must be 0 */
		buf[i+8][1] = buf[i][1] = 0xffff;	/* broadcast */
		for (j = 2; j < 8; j++)
			buf[i+8][j] = buf[i][j] = addr[i];
	}
	for (i = 6; i < 8; i++)		/* more boundries */
		for (j = 0; j < 8; j++)
			buf[i+8][j] = buf[i][j] = 0;

	dreg = (struct qedevice *)devp->d_csr;

/*	dreg->qe_csreg |= QE_BDROM;		/* reset the LEDs */
/*	for (i = 0; i < 1000; i++)		/* delay must be at least */
/*		;				/*  100 usec */
/*	dreg->qe_csreg &= ~QE_BDROM;	*/

	/* Set up addresses */
/*	printf("1"); */
	send_setup(devp, 0, SETUP);

	/* Send packet through internal loopback */
/*	printf(" 2");
	send_setup(devp, 0, 0); */

	/* Turn off LEDS and setup sanity timer */
/*	printf(" 2"); */
	if (reboot)
	  dreg->qe_csreg |= QE_SANITY_ENBL;
	send_setup(devp, QE_LED3_OFF | QE_SANITY_1MIN, SETUP);

/*	printf(".\n"); */
}

send_setup(devp, mode, setup)
reg	dct	*devp;
intf	mode;
intf	setup;
{
	reg	struct BDL	tbdl, *rbdl;
	reg	struct qedevice	*dreg;
	byte	rec_buf[2200];

	dreg = (struct qedevice *)devp->d_csr;
	rbdl = &( ((struct adbdl *)(devp->d_dev1))->a_bdl );

	/* Set up receive. */
	rbdl->bdl_flag = INIT;
	rbdl->bdl_st1 = INIT;
	rbdl->bdl_st2 = 1;
	rbdl->bdl_addr = (unss)rec_buf;
	rbdl->bdl_len = -((sizeof(rec_buf) + 1) >> 1);
	rbdl->bdl_desc = VALID | ((word)rec_buf >> 16) | 
			( (word)rec_buf & 1 ? HBYTE : 0);

	dreg = (struct qedevice *)devp->d_csr;
	dreg->qe_rcvLoBDL = (unss)rbdl;
	dreg->qe_rcvHiBDL = (unss)((word)rbdl >> 16);

	/* Set up transmit */
	tbdl.bdl_flag = INIT;
	tbdl.bdl_st1 = INIT;
	tbdl.bdl_addr = (unss)setup_buf;
	if (mode)
	  tbdl.bdl_len = -(( (128 | mode) + 1) >> 1);
	else
	  tbdl.bdl_len = -(( (sizeof(setup_buf)) + 1) >> 1);
	tbdl.bdl_desc = setup | VALID | EOM | ((word)setup_buf >> 16) | 
			( (word)setup_buf & 1 ? HBYTE : 0);

	dreg = (struct qedevice *)devp->d_csr;
	dreg->qe_xmitLoBDL = (unss)&tbdl;
	dreg->qe_xmitHiBDL = (unss)((word)&tbdl >> 16);

	/* Wait for packet to be received. */
	while ((dreg->qe_csreg & QE_RCV_IR) == 0)
		;
	dreg->qe_csreg = dreg->qe_csreg | QE_RCV_IR;
}


/* Device output initialization routine.  Just sets the local net address. */
qna_out_up(devp)
reg	dct	*devp;
{
	reg	struct qedevice	*dreg;
	reg	struct adbdl	*a;
		struct qna_tstat *st;

	devp->d_flg |= D_INI;
	dreg = (struct qedevice *)devp->d_csr;
	a = (struct adbdl *)mem_alloc(sizeof(struct adbdl));
	if (a == 0)
	  bughalt(qnanoalloc);
	a->a_addr[0] = dreg->qe_physAddr[0];
	a->a_addr[1] = dreg->qe_physAddr[1];
	a->a_addr[2] = dreg->qe_physAddr[2];
	a->a_addr[3] = dreg->qe_physAddr[3];
	a->a_addr[4] = dreg->qe_physAddr[4];
	a->a_addr[5] = dreg->qe_physAddr[5];
	a->a_bdl.bdl_flag = INIT;
	a->a_bdl2.bdl_flag = INVALID;
	a->a_bdl2.bdl_desc = INVALID;

	st = (struct qna_tstat *)mem_alloc(sizeof(struct qna_tstat));
	if (st == 0) bughalt(qnanoalloc);
	st->loss = st->carr = st->abort = st->restarts = 0;
	a->a_stat = (word)st;
	devp->d_dev1 = (word)a;
}

/* Input initialization transfer routine */
qna_in(devp)
reg	dct	*devp;
{
	reg	struct BDL	*bdl;
	reg	struct qedevice	*dreg;

	bdl = &( ((struct adbdl *)(devp->d_dev1))->a_bdl );
	bdl->bdl_flag = INIT;
	bdl->bdl_st1 = INIT;
	bdl->bdl_st2 = 1;	/* the high and low bytes are supposed
				 * to be unequal (I don't know why) */
	bdl->bdl_addr = (unss)devp->d_addr;
	bdl->bdl_len = -((devp->d_breq + 1) >> 1);
	bdl->bdl_desc = VALID | ((word)devp->d_addr >> 16) | 
			( (word)devp->d_addr & 1 ? HBYTE : 0);

	dreg = (struct qedevice *)devp->d_csr;
	dreg->qe_rcvLoBDL = (unss)bdl;
	dreg->qe_rcvHiBDL = (unss)((word)bdl >> 16);
}

/* Output initialization transfer routine */
qna_out(devp)
reg	dct	*devp;
{
	reg	struct BDL	*bdl;
	reg	struct qedevice	*dreg;
	int	qna_timeout();

	bdl = &( ((struct adbdl *)(devp->d_dev1))->a_bdl );
	bdl->bdl_flag = INIT;
	bdl->bdl_st1 = INIT;
	bdl->bdl_addr = (unss)devp->d_addr;
	if (devp->d_breq < ETHMINSIZ) devp->d_breq = ETHMINSIZ;
	bdl->bdl_len = -((devp->d_breq + 1) >> 1);
	bdl->bdl_desc = VALID | EOM | ((word)devp->d_addr >> 16) | 
			( (word)devp->d_addr & 1 ? HBYTE : 0);

	dreg = (struct qedevice *)devp->d_csr;
	dreg->qe_xmitLoBDL = (unss)bdl;
	dreg->qe_xmitHiBDL = (unss)((word)bdl >> 16);

	/* Crock!  The deqna needs a timeout because it wedges sometimes. */
	if (!(devp->d_flg & D_DV1)) {
	    devp->d_flg |= D_DV1;
	    devp->d_check = devp->d_count;
	    stime(qna_timeout, devp, 1, 100);
	}
}

/* Timeout routine for the deqna.  D_DV1 is set if there is a timer on
 * the QNA.  This is handled by qna_out which sets the flag and a
 * timer and also sets d_ckeck to d_count.  D_count is incremented by
 * the outut interrupt routine, so when the timer goes off, if d_ckeck
 * and d_count are still equal then the deqna is presumed wedged and
 * is restarted.
 */
qna_timeout(devp)
reg dct *devp;
{
    unsw ps;
    struct qna_tstat *st;

    ps = disable();
    if (devp->d_check != devp->d_count) {
	if (devp->d_qhd) {
	    devp->d_check = devp->d_count;
	    stime(qna_timeout, devp, 1, 100);
	}
	else {
	    devp->d_flg &= ~D_DV1;
	}
    }
    else {
	st = (struct qna_tstat *)(((struct adbdl *)(devp->d_dev1))->a_stat);
	st->restarts++;
	dolog("QNA: restarting\n");
	devp->d_flg = 0;
	qna_in_up(devp->d_lnk);	/* Reinitialize the deqna */
	qna_out(devp);		/* Restart input and output */
	qna_in(devp->d_lnk);
    }
    enable(ps);
}

/* Interrupt handler routine.  Just demultiplexs on the reson for the
 * interrupt.  Note that the interrupt is always on the input dct. */
qna_int(devp)
reg	dct	*devp;
{
	reg	struct qedevice	*dreg;
	reg	unss		csr;

	dreg = (struct qedevice *)devp->d_csr;
	csr = dreg->qe_csreg;
	dreg->qe_csreg = csr;
	if (csr & QE_RCV_IR) qna_ii(devp);
	if (csr & QE_XMIT_IR) qna_oi(devp->d_lnk);
}

/* Input interrupt. */
qna_ii(devp)
reg	dct	*devp;
{
	reg	struct qedevice	*dreg;
	reg	struct BDL	*bdl;
	reg	iorb		*iob;
		struct qna_rstat *st;

	dreg = (struct qedevice *)devp->d_csr;
	if ((iob = devp->d_qhd) == NULL)
	  return;
	bdl = &( ((struct adbdl *)(devp->d_dev1))->a_bdl );
	if (bdl->bdl_st1 & ERROR) {
	    st = (struct qna_rstat *)
	      (((struct adbdl *)(devp->d_dev1))->a_stat);
	    if (bdl->bdl_st1 & RUNT) {
		st->runt++;
		qna_in(devp);	/* Resart this iorb immediately */
		return;
	    }
	    if (bdl->bdl_st1 & DISCARD) {
		if (bdl->bdl_st1 & FRAME) st->frame++;
		if (bdl->bdl_st1 & CRERR) st->crc++;
		if (bdl->bdl_st1 & OVF) st->ovf++;
	    }
	    iob->i_stat |= I_ERR;
	    if ((bdl->bdl_st1 & USING) == USING)
	      bughalt("Not last segment!");
	}

	iob->i_stat |= I_DONE;
	iob->i_bxfr = GET_RBL(bdl);
	iob->i_bxfr += 60;		/* broken hardware ! */
	iocmr(devp);
}

/* Output interrupt routine. */
qna_oi(devp)
reg	dct	*devp;
{
	reg	struct qedevice	*dreg;
	reg	struct BDL	*bdl;
	reg	iorb		*iob;
		struct qna_tstat *st;

	devp->d_count++;	/* this is so the timeout doesn't time out */
	dreg = (struct qedevice *)devp->d_csr;
	iob = devp->d_qhd;
	if (iob == NULL) return;

	bdl = &( ((struct adbdl *)(devp->d_dev1))->a_bdl );
	if (bdl->bdl_st1 & ERROR) {
	    st = (struct qna_tstat *)
	      (((struct adbdl *)(devp->d_dev1))->a_stat);
	    if (bdl->bdl_st1 & LOSS) st->loss++;
	    if (bdl->bdl_st1 & NOCAR) st->carr++;
	    if (bdl->bdl_st1 & ABORT) st->abort++;
	    /* Turn off error checking for now.  Just assume that the packet
	     * was sent ok.  [I don't remember why I did this. -dab]
	    iob->i_stat |= I_ERR;
	    */
	}
	iob->i_stat |= I_DONE;
	iob->i_bxfr = iob->i_breq;
	iocmr(devp);
}
