/* Copyright 1986, 1989 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.
 * This is for quick standalone booting only.  It does not use interrupts.
 */

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

#define	ETHMINSIZ	64

extern unsigned char etmyaddr[6];
#define printf puts

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

/* lets not deal with a memory allocator */
#define NRECBUFS	16
#define RECBUFSIZE	2048
static char recbuf[NRECBUFS * RECBUFSIZE];
/* two extra BDLs are allocated because we need one to end the list, and
 * the FCO sheet said two words should be saved after the list because
 * they got trashed.
 */
static struct BDL rbdl[NRECBUFS+2];
static struct BDL tbdl[3];
static int currcv;

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



/* 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 BDL	*b;
		char *p;
		int i; 

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

	for (i = 0; i < 6; i++) 
	  etmyaddr[i] = dreg->qe_physAddr[i];

	for (b = rbdl, i = 0; i < NRECBUFS; i++, b++) {
	    bzero(b, sizeof(struct BDL));
	    b->bdl_flag = INIT;
	    b->bdl_st1 = INIT;
	    b->bdl_st2 = 1;
	    b->bdl_addr = (unss)&recbuf[i * RECBUFSIZE];
	    b->bdl_len = -RECBUFSIZE/2;
	    b->bdl_desc = VALID | ((word)&recbuf[i * RECBUFSIZE] >> 16);
	}
	bzero(b, sizeof(struct BDL));
	b->bdl_flag = INIT;
	b->bdl_addr = (unss)rbdl;
	b->bdl_desc = VALID | CHAIN | ((word)rbdl >> 16);

	devp->d_dev1 = (word)rbdl;

	dreg->qe_csreg = QE_RESET;
	dreg->qe_csreg &= ~QE_RESET;
	do_setup(devp, etmyaddr, setup_buf);

	dreg->qe_rcvLoBDL = (unss)rbdl;
	dreg->qe_rcvHiBDL = (unss)((word)rbdl >> 16);
	currcv = 0;

	dreg->qe_csreg &= ~QE_EXT_LOOP;
	dreg->qe_csreg |= (QE_RCV_ENBL | QE_INT_LOOP);
#ifdef DEBUG
	printf("qna online.\n");
#endif
}


/*
 * 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] = etmyaddr[i];
	    for (j = 2; j < 8; j++) {
		buf[i+8][j] = buf[i][j] = 0xffff;	/* broadcast */
	    }
	}
	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;

	/* Set up addresses */
#ifdef DEBUG
	printf("qna: do_setup 1");
#endif
	send_setup(devp, 0, SETUP);
#ifdef TIMING
	printf("*");
#endif
}


send_setup(devp, mode, setup)
reg	dct	*devp;
intf	mode;
intf	setup;
{
	static struct BDL	tbdl[3], rbdl[3];
	reg	struct qedevice	*dreg;
	static byte	rec_buf[sizeof(setup_buf) + 10];
	int i;

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

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

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

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

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

	/* Wait for packet to be received. */
	for (i = 0; i < 1000000; i++)
	  if ((dreg->qe_csreg & QE_RCV_IR) != 0)
	    break;
	if ((dreg->qe_csreg & QE_RCV_IR) == 0)
	  printf("Setup packet not received\n");
	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 BDL	*b;

	devp->d_flg |= D_INI;
	dreg = (struct qedevice *)devp->d_csr;
	b = tbdl;
	b->bdl_flag = INIT;
	b++;
	b->bdl_flag = INVALID;
	b->bdl_desc = INVALID;

	devp->d_dev1 = (word)tbdl;
}


/* Output initialization transfer routine */
qna_out(devp)
reg	dct	*devp;
{
    reg	struct BDL	*bdl;
    reg	struct qedevice	*dreg;
    unss	csr;
    int i, again=0;
	
resend:
    bdl = (struct BDL *)devp->d_dev1;
    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 = EOM | ((word)devp->d_addr >> 16) | 
    			  ( (word)devp->d_addr & 1 ? HBYTE : 0);
    bdl->bdl_flag = INIT;
    bdl->bdl_st1 = INIT;
    bdl->bdl_desc |= VALID;
    bdl[1].bdl_flag = INVALID;
    bdl[1].bdl_desc = INVALID;
	
    dreg = (struct qedevice *)devp->d_csr;
    dreg->qe_xmitLoBDL = (unss)bdl;
    dreg->qe_xmitHiBDL = (unss)((word)bdl >> 16);

    i = 0;
#ifdef DEBUG
    printf ("Sending ...");
#endif
    do {
	csr = dreg->qe_csreg;
	if (csr & QE_XL_INV) {
#ifdef TIMING
	    printf("!");
#endif
	    dreg = (struct qedevice *)devp->d_csr;
	    dreg->qe_xmitLoBDL = (unss)bdl;
	    dreg->qe_xmitHiBDL = (unss)((word)bdl >> 16);
	}
	if (i++ > 0x8000) {
#ifdef DEBUG
	    printf ("qna_out: QNA restart. ");
#endif DEBUG
	    qna_in_up (devp->d_lnk);
	    qna_out_up(devp);
	    if (again++ == 0) goto resend;
	    return;
	}
    } while ((csr & QE_XMIT_IR) == 0);
#ifdef TIMING
    printf("+");
#endif
#ifdef DEBUG
    printf ("sent.\n");
#endif
}


/* force the interface idle before exiting program */

qna_down()
{
    struct qedevice *dreg = (struct qedevice *)0x20001920;

    dreg->qe_csreg = QE_RESET;
}


int qna_dev_read(devp, data, len)
dct *devp;
byte *data;
int len;

{
	reg struct qedevice *dreg;
	reg unss csr;
	reg struct BDL *bdl;
	int cc, i;
	static timeout = 0;

	dreg = (struct qedevice *) devp->d_csr;
	bdl = &rbdl[currcv];

	if (timeout++ > 1000) {
#ifdef DEBUG
	    printf("qna_in: QNA restart. ");
#endif
	    qna_in_up(devp);
	    qna_out_up(devp->d_lnk);
	    timeout = 0;
	}

	/* Wait for packet to be received.  This loop constant 
	 * has been tuned for .001 seconds of delay on a VS2.
	 */
	i = 0;
	while (i++ < 200) {
	    if (bdl->bdl_st1 & LASTNOT == 0)
	      break;
	}
	if (bdl->bdl_st1 & LASTNOT)
	  return(0);

	if (bdl->bdl_st1 & ERROR) {
	    if (bdl->bdl_st1 & RUNT) {
#ifdef TIMING
		printf ("@");
#endif
	    }
	    if (bdl->bdl_st1 & DISCARD) {
#ifdef TIMING
		if (bdl->bdl_st1 & (FRAME | CRERR | OVF)) printf ("&");
#endif
	    }
	    if ((bdl->bdl_st1 & USING) == USING) {
		printf ("qna: err not last segment.\n");
	    }
	    bdl->bdl_st1 = INIT;
	    bdl->bdl_flag = INIT;
	    bdl->bdl_desc &= ~USING;
	    bdl->bdl_desc |= VALID;
	    currcv = (currcv + 1) % NRECBUFS;
	    return (0);
	}
	cc = GET_RBL(bdl) + 60;

	for (i = 0; (i < len) && (i < cc); i++)
	  data[i] = recbuf[currcv * RECBUFSIZE + i];
	bdl->bdl_flag = INIT;
	bdl->bdl_st1 = INIT;
	bdl->bdl_st2 = 1;
	bdl->bdl_len = -RECBUFSIZE/2;
	bdl->bdl_desc &= ~USING;
	bdl->bdl_desc |= VALID;
	currcv = (currcv + 1) % NRECBUFS;
	timeout = 0;
#ifdef TIMING
	printf("-");
#endif

	if ((dreg->qe_csreg & QE_RL_INV) &&
	    rbdl[currcv].bdl_flag & USING == INIT ) {
	    dreg->qe_rcvLoBDL = (unss)&rbdl[currcv];
	    dreg->qe_rcvHiBDL = (unss)((word)&rbdl[currcv] >> 16);
	}
	return (cc);
}
