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

/*
 *------------------------------------------------------------------
 *
 * $Source: /u1/jis/gw/cgw/src/gw/dev.vax/RCS/qna.c,v $
 * $Revision: 1.2 $
 * $Date: 89/03/22 10:51:58 $
 * $State: Exp $
 * $Author: jon $
 * $Locker:  $
 *
 * $Log:	qna.c,v $
 * Revision 1.2  89/03/22  10:51:58  jon
 * Up the receive buffer size.  This should help with DEQNA weirdness and
 * larger (or overlarge) packets.
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_qna_c = "$Header: qna.c,v 1.2 89/03/22 10:51:58 jon Exp $";
#endif	lint

#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[sizeof(setup_buf) + 10];

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

	/* 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;
	/* I'm making this larger than what should be needed.  
	   Rumor has it that very bad things happen if the receive buffer 
	   isn't large enough, and that DEQNA like to hand back 2K packets
	   at times -- JR 3/20/89 */
	bdl->bdl_len = -((2048 + 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);
}
