Return-Path: <cak@Purdue.ARPA>
Received: from merlin.ARPA (purdue-merlin.ARPA) by mit-borax.ARPA (4.12/4.7)
	id AA05180; Wed, 30 Jan 85 07:23:42 est
From: Christopher A Kent <cak@Purdue.ARPA>
Message-Id: <8501301223.AA05191@merlin.ARPA>
Received: by merlin.ARPA; Wed, 30 Jan 85 07:23:35 est
Date: 30 Jan 1985 0723-EST (Wednesday)
To: dab@mit-borax.ARPA (David A. Bridgham)
Subject: Re: deqna driver
In-Reply-To: Your message of 29 Jan 1985 2115-EST (Tuesday).
             <8501300215.AA03334@mit-borax.ARPA>

It's true. Here is what I have running as a small standalone pdp-11
program. I hope it helps -- there are lots of bugs in the documentation. 

If you have any questions, feel free to ask.

Cheers,
chris

-------------
/* $Header: /c/cak/src/deqna/RCS/if_ether.h,v 1.2 85/01/23 15:51:27 cak Exp $ */

/* 
 * if_ether.h - 10 Mb/s ethernet constants and such
 * 
 * Author:	Christopher A. Kent
 * 		Dept. of Computer Sciences
 * 		Purdue University
 * Date:	Sat Jan 12 1985
 */

/*
 * $Log:	if_ether.h,v $
 * Revision 1.2  85/01/23  15:51:27  cak
 * Added loopback ethertype (0x0060).
 * 
 * Revision 1.1  85/01/22  21:52:46  cak
 * Initial revision
 * 
 */

/*
 * Some basic types (these for the pdp-11).
 */

typedef	char	u_char;		/* 11 doesn't really do unsigned char */
typedef	unsigned int	u_short;

/*
 * Structure of a 10Mb/s Ethernet address.
 */
struct ether_addr {
	u_char	octet[6];
};

typedef	struct ether_addr EtherAddr;

/*
 * Structure of a 10Mb/s Ethernet header.
 */
struct	ether_header {
	EtherAddr	ether_dhost;
	EtherAddr	ether_shost;
	u_short		ether_type;
};

typedef	struct ether_header	EtherHeader;

/* 
 * Fundamental 10 Mb/s Ethernet constants.
 */

#define	ETHERMIN		64	/* minimum packet size */
#define	ETHERMAX		1500	/* maximum packet size */
#define	ETHER_MAX_DATA		(ETHERMAX - sizeof(EtherHeader))

/* 
 * Structure of a 10 Mb/s Ethernet packet.
 */
struct	ether_packet {
	struct ether_header	ep_header;
	u_char			ep_data[ETHER_MAX_DATA];
};

typedef	struct ether_packet	EtherPacket;

#define	ep_dhost	ep_header.ether_dhost
#define	ep_shost	ep_header.ether_shost
#define	ep_type		ep_header.ether_type

/*
 * Ethernet Address Resolution Protocol.
 *
 * See RFC 826 for protocol description.  Structure below is adapted
 * to resolving internet addresses.  Field names used correspond to 
 * RFC 826.
 */

struct	ether_arp {
	u_short	arp_hrd;	/* format of hardware address */
#define ARPHRD_ETHER 	1	/* ethernet hardware address */
	u_short	arp_pro;	/* format of proto. address (ETHER_IP) */
	u_char	arp_hln;	/* length of hardware address (6) */
	u_char	arp_pln;	/* length of protocol address (4) */
	u_short	arp_op;
#define	ARP_REQUEST	1	/* request to resolve address */
#define	ARP_REPLY	2	/* response to previous request */
	EtherAddr arp_sha;	/* sender hardware address */
	u_char	  arp_spa[4];	/* sender protocol address */
	EtherAddr arp_tha;	/* target hardware address */
	u_char	  arp_tpa[4];	/* target protocol address */
};

/*
 * 10 Mb/s packet types.
 */

#define	ETHER_LOOP		0x0060	/* loopbacks */
#define	ETHER_ECHO		0x0200	/* echoes */
#define	ETHER_PUP		0x0400	/* PUP protocol */
#define	ETHER_IP		0x0800	/* IP protocol */
#define ETHER_ARP		0x0806	/* Addr. resolution protocol */
---------
/* $Header: /c/cak/src/deqna/RCS/if_qereg.h,v 1.3 85/01/24 16:24:28 cak Exp $ */

/* 
 * if_qereg.h - DEQNA register descriptions
 * 
 * Author:	Christopher A. Kent
 * 		Dept. of Computer Sciences
 * 		Purdue University
 * Date:	Sat Jan 12 1985
 * Copyright (c) 1985 Christopher A. Kent
 *	May be freely redistributed provide this notice remains intact
 *	in all copies or derivatives.
 */

/*
 * $Log:	if_qereg.h,v $
 * Revision 1.3  85/01/24  16:24:28  cak
 * Minor cleanup.
 * 
 * Revision 1.2  85/01/24  15:22:39  cak
 * Missed the unused 0040000 bit in the _bits defn in qe_csr.
 * 
 * Revision 1.1  85/01/22  03:44:03  cak
 * Initial revision
 * 
 */


struct qedevice {
	union {
	    u_short	_physAddr[sizeof(EtherAddr)];
	    struct {
		u_short	_unused[2];
		u_short	_rcvLoBDL;	/* low 16 bits of addr */
		u_short	_rcvHiBDL;	/* high bits + status*/
		u_short	_xmitLoBDL;	/* as for receive */
		u_short	_xmitHiBDL;
		u_short	_vector;	/* interrupt vector */
		u_short _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
 */

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

struct qe_csr {
	union {
	    u_short _register;
	    struct {
		u_short	_rcvEnable:1,
			_reset:1,
			_nxmIntr:1,
			_bootDiagROM:1,
			_xlInval:1,
			_rlInval:1,
			_ie:1,
			_xmitIR:1,
			_intLoopBack:1,
			_extLoopBack:1,
			_sanityEnable:1,
			:1,
			_fuseOK:1,
			_carrier:1,
			:1,
			_rcvIR:1;
	    }_bits;
	}_csr;
};					/* this must fit in two bytes! */

#define	csr_rcvEnable		_csr._bits._rcvEnable
#define	csr_reset		_csr._bits._reset
#define	csr_nxmIntr		_csr._bits._nxmIntr
#define	csr_bootDiagROM		_csr._bits._bootDiagROM
#define	csr_xlInval		_csr._bits._xlInval
#define	csr_rlInval		_csr._bits._rlInval
#define	csr_ie			_csr._bits._ie
#define	csr_xmitIR		_csr._bits._xmitIR
#define	csr_intLoopBack		_csr._bits._intLoopBack
#define	csr_extLoopBack		_csr._bits._extLoopBack
#define	csr_sanityEnable	_csr._bits._sanityEnable
#define	csr_fuseOK		_csr._bits._fuseOK
#define	csr_carrier		_csr._bits._carrier
#define	csr_rcvIR		_csr._bits._rcvIR

/* 
 * DEQNA Buffer Descriptor
 */

	/* flag word */
 					/* all other bits unused */
#define	QE_BUF_UNUSED	0100000		/* DEQNA not yet using buffer */
#define	QE_BUF_INUSE	0140000		/* DEQNA is using this buffer */

#define	qeBufLen(x)	(-(((x)+1)>>1))	/* 2's complement word count */

	/* hi address / descriptor word */
#define	QE_HI_ADDR	0000077		/* hi order address bits of buffer */
#define	QE_HBO_START	0000100		/* buffer starts on byte boundary */
#define	QE_LBO_END	0000200		/* buffer ends on byte boundary */
					/* 07400 undefined */
#define	QE_SET_UP	0010000		/* buffer is setup packet */
#define	QE_EOM		0020000		/* last buffer in message */
#define	QE_CHAIN	0040000		/* address is pointer, not buffer */
#define	QE_VALID	0100000		/* descriptor has valid address */

/* 
 * BDL status words
 */

	/* transmit status word 1 */
					/* 017 reserved */
#define	QE_COUNT	0000360		/* Collision count */
#define	QE_FAIL		0000400		/* Heart failure */
#define	QE_ABORT	0001000		/* Excessive collisions */
#define	QE_STE16	0002000		/* Sanity timer up, 4 minutes */
#define	QE_NOCAR	0004000		/* No carrier during xmit */
#define	QE_LOSS		0010000		/* Carrier lost during xmit */
					/* 0020000 reserved */
#define	QE_BUFUSE	0140000		/* Buffer use status */
#define	QE_ERRUSED	0040000		/* Error/used */
#define	QE_LASTNOT	0100000		/* not last segment */

	/* transmit status word 2 */
#define	QE_TDR		0037777		/* TDR count (100 ns resolution) */
					/* 0140000 reserved */

	/* receive status word 1 */
#define	QE_OVF		0000001		/* EPP receive overflow */
#define	QE_CRCERR	0000002		/* Incoming CRC error */
#define	QE_FRAME	0000004		/* Framing alignment error */
 					/* 0370 reserved */
#define	QE_RBLHI	0003400		/* High bits of Rcvd Byte Length  */
#define	QE_RUNT		0004000		/* Incoming runt */
#define	QE_DISCARD	0010000		/* Should probably discard this one */
#define	QE_ESETUP	0020000		/* Looped back packet */
					/* 0140000 as for xmit status 1 */
	/* receive status word 2 */
#define	QE_RBL		0000377		/* Received Byte Length <07:00> */
#define	QE_RBL2		0177400		/* copy of QE_RBL */

	/* 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 */
	/* 
	 * Set sanity timer to fire after n seconds of not transmitting:
	 * logsecs = logbase2(n) + 1.
	 */
#define	SanityValue(logsecs)	(logsecs << 4)

struct qe_bufd {
	union {
	    struct {
		u_short	_flags;		/* flag word */
		u_short	_desAddr;	/* descriptor bits + high addr bits */
	    }_words;
	    struct {
		u_short	:14,
			_flag:2;

		u_short	_hiAddr:6,
			_HBOStart:1,
			_LBOEnd:1,
			:4,
			_setUp:1,
			_endOfMsg:1,
			_chain:1,
			_valid:1;
	    }_bdBits;
	}_bd;
	u_short	_loAddr;		/* buffer address low bits */
	u_short	_length;		/* length of message (in words) */
	union {
	    u_short	_status[2];
	    struct {
		u_short	:4,
			_count:4,
			_fail:1,
			_abort:1,
			_ste16:1,
			_nocar:1,
			_loss:1,
			:1,
			_xuse:2;	/* BDL use status: */
#define	LAST_NOERR	0		/* last segment, no errors */
#define	LAST_ERR	1		/* last segment, errors occurred */
#define	UNUSED		2		/* buffer not used yet */
#define	BUSY		3		/* buffer used, not end of message */
		u_short	_tdr:14,
			:2;
	    }_xmit;
	    struct {
		u_short	_ovf:1,
			_crcerr:1,
			_frame:1,
			:5,
			_rblhi:3,
			_runt:1,
			_discard:1,
			_esetup:1,
			_ruse:2;
		u_short	_rbl:8,
			_rbl2:8;
#define	QE_RBLINIT	052652		/* init value so halves differ */
	    }_rcv;
	}_st;
};

#define	bd_flags	_bd._words._flags
#define	bd_desAddr	_bd._words._desAddr
#define	bd_flag		_bd._bdBits._flag
#define	bd_hiAddr	_bd._bdBits._hiAddr
#define	bd_HBOStart	_bd._bdBits._HBOStart
#define	bd_LBOEnd	_bd._bdBits._LBOEnd
#define	bd_setUp	_bd._bdBits._setUp
#define	bd_endOfMsg	_bd._bdBits._endOfMsg
#define	bd_chain	_bd._bdBits._chain
#define	bd_valid	_bd._bdBits._valid
#define	bd_loAddr	_loAddr
#define	bd_length	_length

#define	bd_status	_st._status
#define	xmit_count	_st._xmit._count
#define	xmit_fail	_st._xmit._fail
#define	xmit_abort	_st._xmit._abort
#define	xmit_ste16	_st._xmit._ste16
#define	xmit_nocar	_st._xmit._nocar
#define	xmit_loss	_st._xmit._loss
#define	xmit_use	_st._xmit._xuse
#define	xmit_tdr	_st._xmit._tdr
#define	rcv_ovf		_st._rcv._ovf
#define	rcv_crcerr	_st._rcv._crcerr
#define	rcv_frame	_st._rcv._frame
#define	rcv_rblhi	_st._rcv._rblhi
#define	rcv_runt	_st._rcv._runt
#define	rcv_discard	_st._rcv._discard
#define	rcv_esetup	_st._rcv._esetup
#define	rcv_use		_st._rcv._ruse
#define	rcv_rbl		_st._rcv._rbl
#define	rcv_rbl2	_st._rcv._rbl2
-----------
/* $Header: /c/cak/src/deqna/RCS/if_qe.c,v 1.7 85/01/28 15:56:13 cak Exp $ */

/* 
 * if_qe.c - DEQNA ethernet interface generic driver
 * 
 * Author:	Christopher A. Kent
 * 		Dept. of Computer Sciences
 * 		Purdue University
 * Date:	Wed Jan 16 1985
 * Copyright (c) 1985 Christopher A. Kent
 *	May be freely redistributed provided this notice remains intact
 *	in all copies or derivatives.
 */

/*
 * $Log:	if_qe.c,v $
 * Revision 1.7  85/01/28  15:56:13  cak
 * Make wait loops pause for clock interrupts, rather than just
 * hard loops.
 * 
 * Revision 1.6  85/01/24  16:24:08  cak
 * Minor cleanup.
 * 
 * Revision 1.5  85/01/24  16:06:39  cak
 * Removed debugging code, added receive interrupt counter.
 * 
 * Revision 1.4  85/01/24  15:50:43  cak
 * Changes to use interrupts wherever possible.
 * 
 * Revision 1.3  85/01/23  23:15:28  cak
 * Added support for multiple transmit buffers in the ring.
 * 
 * Revision 1.2  85/01/23  16:26:42  cak
 * Added support for multiple deqna devices.
 * (despite what DEC says about RFI/EMI emissions.)
 * 
 * Revision 1.1  85/01/22  21:52:19  cak
 * Initial revision
 * 
 */

static char rcs_ident[] = "$Header: /c/cak/src/deqna/RCS/if_qe.c,v 1.7 85/01/28 15:56:13 cak Exp $";

#include "if_ether.h"
#include "if_qereg.h"

#define	NQE	2			/* handle up to NQE devices */

#define	TRUE	1
#define	FALSE	(!TRUE)

#define	XMIT_BDLS	2
#define	RCV_BDLS	2

#define	QE_TIMEOUT	10*60		/* 10 seconds wait for operation */

#define	ETHER_BUF_DATA	(2048 - sizeof(EtherHeader))
struct	qe_buf {
	EtherHeader	b_header;
	u_char		b_data[ETHER_BUF_DATA];	/* room for BDROM segment */
};
#define	b_dhost		b_header.ether_dhost
#define	b_shost		b_header.ether_shost
#define	b_type		b_header.ether_type

struct	qe_softc {
	struct qedevice	*qeaddr;		/* the device itself */
	int		unit;			/* unit number for errors */
	int		open;			/* is this really open? */
	EtherAddr	physAddr;		/* ethernet address */
	u_char		setUpBuf[16][8];	/* set up buffer */
	int		rcv_ints;		/* # rcv interrupts */
	int		rcv_sem;		/* receive semaphore */
	int		rcv_index;		/* into rcv_list */
	struct qe_bufd	rcv_list[RCV_BDLS+1];	/* receive chain */
	struct qe_buf	rcv_buf[RCV_BDLS];	/* receive buffers */
	int		xmit_sem;		/* transmit semaphore */
	int		xmit_index;		/* into xmit_list */
	struct qe_bufd	xmit_list[XMIT_BDLS+1];	/* transmit chain */
	struct qe_buf	xmit_buf[XMIT_BDLS];	/* transmit buffer */
}qe_softc[NQE];

/* kludge for 11 compiler that won't do the assignment */
#define	qeCSRcopy(src,dst)	{*(int *)&dst= *(int*)&src;}

/*
 * pick apart an address. Use (u_short) on 11 to avoid arithmetic
 * shift -- other machines may need something else.
 */
#define	hiord(x)	((u_short)(x)>>16)
#define	loord(x)	((u_short)(x)&0xffff)

/* 
 * hacks to enable interrupts, etc., on LSI-11.
 */
#define	disable(ps)	asm("mfps ~ps");asm("mtps $0340")
#define	restore(ps)	asm("mtps ~ps")
#define	enable(ps)	asm("mtps $000")
#define	pause()		asm("wait")

/* 
 * qe_open - prepare device for operation
 *	first time ever: set up hardware address
 *			 go through some self-test
 *	every time:	 set up the receive and transmit buffers
 */

qe_open(addr, vector, ether)
EtherAddr *ether;
{
	register struct qe_softc *qep = &qe_softc[0];
	register int i;
	int unit = -1;
	int *vecp;
	int _qe_intr();

	for (i = 0; i < NQE; i++, qep++)
		if (qep->qeaddr == addr) {
			if (qep->open == TRUE)
				break;	/* already open, fail */
			unit = i;
			qe_init(qep);	/* just init the BDLs */
			qeOnline(qep);	/* bring it up */
			break;
		} else
			if (qep->qeaddr == 0) {	/* never been used */
				unit = i;
				qep->qeaddr = (struct qedevice *) addr;
				qep->unit = unit;
				qep->qeaddr->qe_vector = vector;
				vecp = (int *)vector;
				*vecp++ = _qe_intr;
				*vecp   = 0240 + unit;	/* br5 */

				if (qe_hardinit(qep) != TRUE){	/* full init */
					unit = -1;
					qep->qeaddr = 0;
					qep->unit = -1;
				}
				break;
			}

	if (unit != -1) {
		/* seems OK */
		qep->open = TRUE;
		qep->rcv_ints = 0;
		for (i = 0; i < sizeof (EtherAddr); i++)
			ether->octet[i] = qep->physAddr.octet[i];
	}
	return unit;
}

/* 
 * qe_close - shut down operations
 */

qe_close(unit)
{
	register struct qe_softc *qep;

	if (unit >= NQE || (qep = &qe_softc[unit])->open == FALSE) {
		printf("qe%d_close: illegal unit\n", unit);
		return -1;
	}

	qep->open = FALSE;
	qep->qeaddr->qe_csreg |= QE_RESET;
	return 0;
}

/* 
 * qe_read - read next packet
 *	copies up to len bytes of the next packet into the user's buffer;
 *	returns the actual length.
 *	if there's a packet waiting, just return it and advance the
 *	ring, else block until a packet comes in, or timeout expires.
 */

qe_read(unit, buf, length, timeout, missed)
register char *buf;
int *missed;
{
	register struct qe_softc *qep;
	struct qe_bufd	*qeb;
	int		done, status;
	u_short		packetLength;
	int		ps;

	if (unit >= NQE || (qep = &qe_softc[unit])->open == FALSE) {
		printf("qe%d_read: illegal unit\n", unit);
		return -1;
	}

	done = FALSE;
	qeb = &qep->rcv_list[qep->rcv_index];

	while (!done) {
		status = TRUE;
		disable(ps);
		*missed = qep->rcv_ints;
		qep->rcv_ints = 0;
		if (qeb->rcv_use == UNUSED) {	/* is a packet ready? */
			qep->rcv_sem = FALSE;
			status = wait(&qep->rcv_sem, timeout);
		}
		restore(ps);

		packetLength = -1;
		if (status && !qeb->rcv_discard) {
			packetLength = qeb->rcv_rblhi << 8;
			packetLength |= qeb->rcv_rbl;
			packetLength += 60;	/* deqna magic! sec 4.3.2.5 */
			bcopy(&qep->rcv_buf[qep->rcv_index], buf,
				min(packetLength, length));
			done = TRUE;
		} else {
			done =   (  !status
				|| (qeb->rcv_crcerr &&
				    qeb->rcv_discard &&
				    !qeb->rcv_runt));
			if (status) {
				if (qeb->rcv_ovf)
					printf("ovf ");
				if (qeb->rcv_crcerr)
					printf("crcerr ");
				if (qeb->rcv_frame)
					printf("frame ");
				if (qeb->rcv_runt)
					printf("runt ");
				if (qeb->rcv_discard)
					printf("discard ");
				if (qeb->rcv_esetup)
					printf("esetup ");
				printf("\n");
			}
		}
	}
	if (status) {			/* return buffer to usefulness */
		qeRestoreRcvBD(qep, qeb);

		/* 
		 * If the device has received RCV_BDLS packets since
		 * last read, the receive list will now be invalid.
		 * Inform the device that there is another buffer
		 * waiting for it.
		 */

		if (qep->qeaddr->qe_csreg & QE_RL_INV){
			printf("qe%d: receive went invalid\n", unit);
			qep->qeaddr->qe_rcvLoBDL = loord(qeb);
			qep->qeaddr->qe_rcvHiBDL = hiord(qeb);
		}
	}
	return packetLength;
}

/* 
 * qe_write - write a packet
 *	takes a user buffer and writes it. Blocks until written.
 *	Note that the deqna will not hear its own packets; if this
 *	is necessary, code must go here to loop broadcasts and other
 *	packets that would reach us to the input side.
 */

qe_write(unit, buf, len)
register char *buf;
{
	register struct qe_softc *qep;

	if (unit >= NQE || (qep = &qe_softc[unit])->open == FALSE) {
		printf("qe%d_write: illegal unit\n", unit);
		return -1;
	}

	qeLoadXmitBD(qep, buf, max(len, ETHERMIN), FALSE);

	if (qeStart(qep))
		return len;
	else
		return -1;
}

/* 
 * qe_control - device control operations
 *	change the mode of the device (promiscuous, etc)
 *	this should handle adding and deleting multicasts, too.
 *	To turn off a mode, simply do qe_control(0).
 */

qe_control(unit, magic)
{
	register struct qe_softc *qep;

	if (unit >= NQE || (qep = &qe_softc[unit])->open == FALSE) {
		printf("qe%d_control: illegal unit\n", unit);
		return -1;
	}

	return qeMode(qep, magic);
}

/* 
 * qe_intr - interrupt routine. Determine type of interrupt, signal
 *	upper later.
 */

qe_intr(unit)
{
	register struct qe_softc *qep = &qe_softc[unit];
	struct qe_csr csr;

	qeCSRcopy(qep->qeaddr->qe_csreg, csr);
	qep->qeaddr->qe_csreg = qep->qeaddr->qe_csreg;	/* clear RI, XI */

	if (csr.csr_rcvIR){
		qep->rcv_sem = TRUE;
		qep->rcv_ints++;
		if (csr.csr_rlInval)
			printf("\nqe%dintr: rcv list went invalid\n", unit);
	}

	if (csr.csr_xmitIR)
		if (csr.csr_nxmIntr)
			printf("\nqe%dintr: NXM!\n", unit);
		else{
			qep->xmit_sem = TRUE;
		}
}

/* 
 * qe_hardinit - once-only device set up
 *	resets the device and the LEDs, then
 *	sets up the address recognition matrix and performs some
 *	simple self-test. If all goes well, the device is brought online.
 *
 *	If we knew how to get at the bootstrap
 *	ROM stuff, this would be the place to use it.
 */

qe_hardinit(qep)
register struct qe_softc *qep;
{
	register struct qedevice *qeaddr = qep->qeaddr;
	register int i;
	struct qe_buf	*qeb;
	int	retval;

	qeaddr->qe_csreg |= QE_RESET;	/* reset the device */
	qeaddr->qe_csreg &= ~QE_RESET;
	delay();			/* let the bits settle */

	qeaddr->qe_csreg |= QE_BDROM;	/* reset the LEDs (undocumented!) */
	delay();			/* must be at least 100 usec */
	qeaddr->qe_csreg &= ~QE_BDROM;

	qeSetEtherAddr(qep);		/* fetch physical address */
	printf("deqna %d at 0%o, physical address ", qep->unit, (int)qeaddr);
	printEtherAddr(&qep->physAddr);
	printf("\n");

	qeFillSetup(&qep->physAddr, qep->setUpBuf);

	qe_init(qep);			/* set up BDLs */

	qeaddr->qe_csreg |= QE_IE;	/* enable interrupts */

	retval = FALSE;
	qeMode(qep, QE_LED1_OFF);	/* indicate progress */

	qeb = &qep->xmit_buf[qep->xmit_index];
	qeb->b_dhost = qep->physAddr;
	qeb->b_shost = qep->physAddr;
	qeb->b_type = htons(ETHER_LOOP);
	for (i = 0; i < ETHER_BUF_DATA; i++)
		qeb->b_data[i] = i % 512;

	if (qeLoopBack(qep, TRUE)) {
		qeMode(qep, QE_LED2_OFF);
		if (qeLoopBack(qep, FALSE)) {
			qeMode(qep, QE_LED3_OFF);
			qeOnline(qep);
			retval = TRUE;
		}
	}
	return retval;
}

/* 
 * qe_init - reinitialize data structures
 *	set up the receive ring, invalidate the transmit buffer,
 *	hand receive ring to device. 
 */

qe_init(qep)
register struct qe_softc *qep;
{
	register int	i;
	register struct qe_bufd *qeb;

	qep->qeaddr->qe_csreg |= QE_RESET;	/* invalidate BDLs */
	qep->qeaddr->qe_csreg &= ~QE_RESET;

	qep->xmit_index = 0;		/* initialize transmit ring */
	for (i = 0; i <= XMIT_BDLS; i++){
		qeb = &qep->xmit_list[i];
		qeb->bd_flags = QE_BUF_UNUSED;
		qeb->bd_hiAddr = 0;
		qeb->bd_loAddr = 0;
		qeb->bd_length = 0;
		qeb->bd_chain = FALSE;
		qeb->bd_valid = FALSE;
		qeb->xmit_use = UNUSED;
	}
	qeb = &qep->xmit_list[XMIT_BDLS];
	qeb->bd_chain = TRUE;
	/* 
	 * qeb->bd_flag, qeb->status words are apparently 
	 * unused in chain pointer.
	 */
	qeb->bd_valid = TRUE;
	qeb->bd_hiAddr = hiord(qep->xmit_list);
	qeb->bd_loAddr = loord(qep->xmit_list);

	qep->rcv_index = 0;		/* initialize receive ring */
	for (i = 0; i < RCV_BDLS; i++) {
		qeb = &qep->rcv_list[i];
		qeb->bd_flags = QE_BUF_UNUSED;
		qeb->bd_hiAddr = hiord(&qep->rcv_buf[i]);
		qeb->bd_loAddr = loord(&qep->rcv_buf[i]);
		qeb->bd_length = qeBufLen(sizeof(qep->rcv_buf[i]));
		qeb->bd_chain = FALSE;
		qeb->bd_valid = TRUE;
		qeb->rcv_use = UNUSED;
		qeb->bd_status[1] = QE_RBLINIT;
	}
	qeb = &qep->rcv_list[RCV_BDLS];
	qeb->bd_chain = TRUE;
	/*
	 * qeb->bd_flag, qeb->status words are
	 * apparently unused in chain pointer 
	 */
	qeb->bd_valid = TRUE;
	qeb->bd_hiAddr = hiord(qep->rcv_list);
	qeb->bd_loAddr = loord(qep->rcv_list);

	/* hand the receive ring to the device */
	qep->qeaddr->qe_rcvLoBDL = loord(qep->rcv_list);
	qep->qeaddr->qe_rcvHiBDL = hiord(qep->rcv_list);
}

/* 
 * qeSetEtherAddr - copy physical address from device into softc
 */

qeSetEtherAddr(qep)
register struct qe_softc *qep;
{
	register int i;

	for (i = 0; i < sizeof (EtherAddr); i++)
		qep->physAddr.octet[i] = qep->qeaddr->qe_physAddr[i] & 0xff;
}

/* 
 * qeFillSetup - fill a setup buffer with the physical address
 */

qeFillSetup(addr, buf)
EtherAddr	*addr;
u_char		buf[16][8];
{
	register int	i, j;

	for (i = 0; i < sizeof(EtherAddr); i++) {
		buf[i+8][0] = buf[i][0] = 0;	/* boundaries 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->octet[i];
	}
	for (i = sizeof(EtherAddr); i < 8; i++)	/* more boundaries */
		for (j = 0; j < 8; j++)
			buf[i+8][j] = buf[i][j] = 0;
}

/* 
 * qeMode - change the mode of the device by sending a setup packet.
 *	Packet loops back into receive ring for synchronization;
 *	wait till it comes back. Packets that are received while waiting
 *	are discarded (oh well).
 */

qeMode(qep, magic)
register struct qe_softc *qep;
{
	register struct qe_bufd *qeb;
	int status, ps;

	qeLoadXmitBD(qep, qep->setUpBuf, sizeof(qep->setUpBuf) + magic, TRUE);

	if (qeStart(qep)) {
		/* it got sent -- wait for it to come back */
		qeb = &qep->rcv_list[qep->rcv_index];
		status = TRUE;
		disable(ps);
		if (qeb->rcv_use == UNUSED) {	/* is a packet ready? */
			do {
				qep->rcv_sem = FALSE;
				status = wait(&qep->rcv_sem, QE_TIMEOUT);
				if (!status)
					break;
				if (qeb->rcv_esetup == FALSE){
					printf("qe%dMode: lost packet\n",
						qep->unit);
					qeRestoreRcvBD(qep, qeb);
				}
			} while (qeb->rcv_esetup == FALSE);
		}
		restore(ps);

		if (!status){
			printf("qe%dMode: timed out on receive\n", qep->unit);
			return FALSE;
		}

		/* successful -- restore rcv BDL that got tromped */
		qeRestoreRcvBD(qep, qeb);

		return (qep->xmit_list[0].xmit_use != LAST_ERR);
	} else
		return FALSE;
}

/* 
 * qeLoadXmitBD - load a transmit descriptor with a buffer
 *	assumes entire message fits in one buffer -- caveat emptor!
 */

qeLoadXmitBD(qep, buf, len, setup)
register struct qe_softc *qep;
char	*buf;
int	len, setup;
{
	register struct qe_bufd *qeb = &qep->xmit_list[0];

	qeb->bd_flags = QE_BUF_UNUSED;
	qeb->xmit_use = UNUSED;
	qeb->bd_HBOStart = FALSE;	/* is this always true? */
	qeb->bd_LBOEnd = (len&1);
	qeb->bd_length = qeBufLen(len);
	qeb->bd_hiAddr = hiord(buf);
	qeb->bd_loAddr = loord(buf);
	qeb->bd_setUp = setup;
	qeb->bd_endOfMsg = TRUE;
	qeb->bd_valid = TRUE;
}

/* 
 * qeRestoreRcvBD - restore a receive BDL entry to usable form and
 *	advance the ring pointer.
 */

qeRestoreRcvBD(qep, qeb)
register struct qe_softc *qep;
register struct qe_bufd *qeb;
{
	qeb->bd_flags = QE_BUF_UNUSED;
	qeb->rcv_use = UNUSED;
	qeb->bd_status[1] = QE_RBLINIT;
	/* don't need to reset bd_valid because DEQNA doesn't touch it! */
	qep->rcv_index = (qep->rcv_index + 1) % RCV_BDLS;
}

/* 
 * qeLoopBack - send a loopback packet.
 */

qeLoopBack(qep, internal)
register struct qe_softc *qep;
{
	register int i, *j;
	struct qe_csr csr;
	int status, msize;

	qeCSRcopy(qep->qeaddr->qe_csreg, csr);	/* gak! */
	csr.csr_intLoopBack = !internal;
	csr.csr_extLoopBack = !internal;
	csr.csr_rcvEnable = FALSE;
	qeCSRcopy(csr, qep->qeaddr->qe_csreg);

	j = (int *)&qep->rcv_buf[qep->rcv_index];
	for (i = 0; i < sizeof(qep->rcv_buf[qep->rcv_index]); i++)
		*j++ = 0;

	msize = internal ? 6 : sizeof(EtherPacket);
	qeLoadXmitBD(qep, &qep->xmit_buf[qep->xmit_index], msize, FALSE);

	status = internal ? qeIntLoop(qep) : qeStart(qep);
	if (!status)
		printf("qe%dLoopBack: pkt xmit fails\n", qep->unit);

	if (status) {
		/* 
		 * xmit was successful; must now wait for receive to complete.
		 * Loopbacks don't cause a receive interrupt!
		 */

		for (i = 0; i < QE_TIMEOUT; i++)
			if (qep->rcv_list[qep->rcv_index].rcv_use != UNUSED)
				break;
			else
				pause();

		if ( i >= QE_TIMEOUT){
			printf("qeLoopBack: timed out on receive\n");
			return FALSE;
		}
		for (i = 0; i < msize & status; i++) {
			status &= ((u_char *)&qep->rcv_buf[qep->rcv_index])[i]
				== ((u_char *)&qep->xmit_buf[qep->xmit_index])[i];
		}
		if (i != msize)
			printf("qe%dLoopBack mismatch at %d of %d\n",
				qep->unit, i, msize);

		/* successful -- restore rcv BDL that got tromped */
		qeRestoreRcvBD(qep, &qep->rcv_list[qep->rcv_index]);
	}
	return status;
}

/* 
 * qeIntLoop - do an internal loopback. Leaves the device in internal
 *	loopback mode. Only difference is that internal loopback must
 *	have the receiver enabled.
 */

qeIntLoop(qep)
register struct qe_softc *qep;
{
	int retval;
	
	qep->qeaddr->qe_csreg |= QE_RCV_ENBL;

	return qeStart(qep);		/* do the transmit */
}

/* 
 * qeStart - start transmitting the xmit ring.
 */

qeStart(qep)
register struct qe_softc *qep;
{
	int ps, status;

	disable(ps);
	qep->qeaddr->qe_xmitLoBDL = loord(&qep->xmit_list[0]);
	qep->qeaddr->qe_xmitHiBDL = hiord(&qep->xmit_list[0]);

	qep->xmit_sem = FALSE;
	status = wait(&qep->xmit_sem, QE_TIMEOUT);
	restore(ps);

	if (!status)
		printf("qe%dStart: timed out on transmit\n", qep->unit);

	return status;
		
}

/* 
 * qeOnline - reset data structures and bring the device on line.
 */

qeOnline(qep)
register struct qe_softc *qep;
{
	register struct qedevice *qeaddr = qep->qeaddr;

	qeaddr->qe_csreg |= QE_RESET;	/* reset the device */
	qeaddr->qe_csreg &= ~QE_RESET;

	qep->rcv_index = 0;
	qeaddr->qe_rcvLoBDL = loord(qep->rcv_list);
	qeaddr->qe_rcvHiBDL = hiord(qep->rcv_list);

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

/* 
 * HELP FUNCTIONS
 *	your environment may already have routines to do these things;
 *	if so, use them instead.
 */

/* 
 * printEtherAddr - print an ethernet address
 */

printEtherAddr(a)
char	a[];
{
	register i;

	for (i = 0; i < 6; i++){
		printf("%x%x", (a[i] & 0xf0) >> 4, a[i] & 0x0f);
		if (i < 5)
			printf(".");
	}
}

/* 
 * delay - delay a short while
 */

delay()
{
	int	i;

	for (i = 500; i ; i--)
		;
}

/* 
 * wait -- wait for a semaphore to go TRUE, or a timeout to expire.
 *	(In a real OS, this would suspend the process, too.)
 */

wait(sem, timeout)
int	*sem;
{
	int	timeCount = 0;
	int	ps;			/* save processor priority */

	enable(ps);

	while (*sem == FALSE && (timeout == 0 || timeCount < timeout)){
		pause();
		timeCount++;
	}

	restore(ps);

	return timeout == 0 || timeCount < timeout;
}

/*
 * clock interrupt routine. Well, something has to be there!
 */

ckintr() {

}

min(a, b)
{
	return a<b?a:b;
}

max(a, b)
{
	return a>b?a:b;
}

bcopy(src, dst, count)
register char *src, *dst;
int count;
{
	for (; count; count--)
		*dst++ = *src++;
}
-----------
/* $Header$ */

/* 
 * qetest.c - test deqna driver
 * 
 * Author:	Christopher A. Kent
 * 		Dept. of Computer Sciences
 * 		Purdue University
 * Date:	Tue Jan 22 1985
 * Copyright (c) 1985 Christopher A. Kent
 */

/*
 * $Log$
 */

static char rcs_ident[] = "$Header$";

#include "if_ether.h"
#include "if_qereg.h"

char	myIPaddr[] = { 192, 5, 48, 30 };
char	targetIPaddr[] = { 192, 5, 48, 3 };

main()
{
	struct ether_packet	packet;
	struct ether_arp	*arp;
	EtherAddr		physAddr;
	int			fd;
	int			missed;
	register int		i;
	int			*vecp;
	int			_ckintr();

	vecp = (int *) 0100;		/* set up clock interrupt vector */
	*vecp++ = _ckintr;
	*vecp   = 0300;			/* br6 */
	asm("mtps $000");		/* enable interrupts */

	if ((fd = qe_open(0174440, 0300, &physAddr)) == - 1) {
		printf("open failed\n");
	}

	if ((fd = qe_open(0174460, 0310, &physAddr)) == - 1) {
		printf("open failed\n");
		while (1) ;
	}

	/* 
	 * send an ARP packet and await the reply
	 */

	for (i = 0; i < sizeof (EtherAddr); i++){
		packet.ep_dhost.octet[i] = 0377;
		packet.ep_shost.octet[i] = physAddr.octet[i];
	}
	packet.ep_type = htons(ETHER_ARP);

	arp = (struct ether_arp *) &packet.ep_data;
	arp->arp_hrd = htons(ARPHRD_ETHER);
	arp->arp_pro = htons(ETHER_IP);
	arp->arp_hln = sizeof(arp->arp_sha);
	arp->arp_pln = sizeof(arp->arp_spa);
	arp->arp_op = htons(ARP_REQUEST);

	for (i = 0; i < arp->arp_hln; i++) {
		arp->arp_sha.octet[i] = packet.ep_shost.octet[i];
		arp->arp_tha.octet[i] = 0;
	}

	for (i = 0; i < arp->arp_pln; i++) {
		arp->arp_spa[i] = myIPaddr[i];
		arp->arp_tpa[i] = targetIPaddr[i];
	}

	printf("qe_write returns %d\n", qe_write(fd, &packet,
		sizeof(*arp) + sizeof(EtherHeader)));

	for (i = 0; i < 10; i++)
		if(qe_read(fd, &packet, sizeof(packet), 5*60, &missed) > 0){
			if (arp->arp_op == htons(ARP_REPLY)){
				printf("target is at ");
				printEtherAddr(&arp->arp_sha);
				printf("\n");
				break;
			} else
				printf("funny packet\n");
	}else
		printf(".");

	if (i == 10)
		printf("read timed out\n");
	
	qe_control(fd, QE_PROMISCUOUS);

	while (1)
		if((i = qe_read(fd, &packet, sizeof(packet), 0, &missed)) > 0){
			printf("%d bytes, type 0x%x: ",
				i, ntohs(packet.ep_type));
			printEtherAddr(&packet.ep_shost);
			printf(" -> ");
			printEtherAddr(&packet.ep_dhost);
			printf(" (missed %d)\n", missed);
		} else
			printf(".");

	qe_close(fd);
}
-----------
/ $Header$
/
/ if_qesup.s - Various cheap support routines for deqna
/

.globl _htonl, _htons, _ntohl, _ntohs

_htonl:
_ntohl:
	mov	4(sp), r1
	swab	r1
_htons:
_ntohs:
	mov	2(sp), r0
	swab	r0
	rts	pc

.globl __qe_intr, _qe_intr		/ deqna interrupt
__qe_intr:
	mfps	-(sp)		/ save device descriptor from PS
	mov	r0,-(sp)	/ save r0 (csv doesn't)
	mov	$_qe_intr,r0	/ what routine to call
	br	intr		/ do it

.globl __ckintr, _ckintr
__ckintr:			/ clock interrupt
	mfps	-(sp)		/ see above
	mov	r0,-(sp)
	mov	$_ckintr,r0
	/ fall through ...
	
intr:
	mov	r1,-(sp)	/ have to save r1, too
	mov	4(sp), r1	/ get saved PS in r1
	bic	$177760,r1	/ mask off device descriptor
	mov	r1,-(sp)	/ push as argument
	jsr	pc,(r0)		/ go to C routine
	mov	2(sp),r1	/ restore r1
	mov	4(sp),r0	/ restore r0
	add	$8,sp		/ pop arg, saved r0, r1, PS 
	rtt			/ return from interrupt
-----------
#
# make the deqna standalone test program
#

all:		a.out

clean:
		rm a.out *.o

install:
		echo 'No installation yet.'

a.out:		qetest.o if_qe.o if_qesup.o
		cc11 -L qetest.o if_qe.o if_qesup.o

qetest.o:	qetest.c
		cc11 -c qetest.c

if_qe.o:	if_qe.c if_qereg.h if_ether.h
		cc11 -c if_qe.c

if_qesup.o:	if_qesup.s
		cc11 -c if_qesup.s

d:		a.out
		download -a9; odt

down:		a.out
		download -s

----------