Subject: working DELQA-YM etherboard driver (#121)
Index:	pdpif/if_qt.c,if_qtreg.h 2.11BSD

Description:
	The DELQA-YM driver is now operational and being released for
	more widespread testing.  The earlier version (in update #98) was known
	not to work and was released as a "place holder" only (the various
	Makefiles needed to be updated, etc).

Repeat-By:
	Have a DELQA-YM (M7516-YM) in the system.  Configure a kernel with
	'NQT 1' in the config file.  Notice that after a few packets the
	system no longer communicates on the network.  This is due to the
	ring buffers not being properly recycled by the old driver.  Next,
	install the driver below and see that the problem is fixed.

Fix:
	Remove any existing 'pdpif/if_qt.c' and 'pdpif/if_qtreg.h' files
	that are present on the system.  Save the remainder of this file
	(below the 'cut here' line) to /tmp/qt and unshar it.

		cd /sys/pdpif
		rm -f if_qt.c if_qtreg.h
		sh /tmp/qt

	To use this driver you must have a DELQA-YM (the driver detects
	if this is not the case so you won't crash the system).  The device
	is specified as 'QT' (right below the 'QE' for the DEQNA in the
	config files) in the kernel config file.

	Many thanks to Terry Kennedy for the loan of the board (and the
	RG-58 used to hook the systems together) used for development of
	the driver.
==============================cut here=====================================
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	if_qt.c
#	if_qtreg.h
# This archive created: Thu Mar  4 10:52:22 1993
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'if_qt.c'
then
	echo shar: "will not over-write existing file 'if_qt.c'"
else
sed 's/^X//' << \SHAR_EOF > 'if_qt.c'
X/*	@(#)if_qt.c	1.2 (2.11BSD) 2/20/93
X *
X * Modification History 
X * 23-Feb-92 -- sms
X *	Rewrite the buffer handling so that fewer than the maximum number of
X *	buffers may be used (32 receive and 12 transmit buffers consume 66+kb
X *	of main system memory in addition to the internal structures in the
X *	networking code).  A freelist of available buffers is maintained now.
X *	When I/O operations complete the associated buffer is placed on the 
X *	freelist (a single linked list for simplicity) and when an I/O is 
X *	started a buffer is pulled off the list. 
X *
X * 20-Feb-92 -- sms
X *	It works!  Darned board couldn't handle "short" rings - those rings
X *	where only half the entries were made available to the board (the
X *	ring descriptors were the full size, merely half the entries were
X * 	flagged as belonging always to the driver).  Grrrr.  Would have thought
X *	the board could skip over those entries reserved by the driver. 
X *	Now to find a way not to have to allocated 32+12 times 1.5kb worth of 
X *	buffers...
X *
X * 03-Feb-92 -- sms
X *	Released but still not working.  The driver now responds to arp and
X *	ping requests.  The board is apparently not returning ring descriptors
X *	to the driver so eventually we run out of buffers.  Back to the
X *	drawing board.
X *
X * 28-Dec-92 -- sms
X *	Still not released.  Hiatus in finding free time and thin-netting
X *	the systems (many thanks Terry!).
X *	Added logic to dynamically allocate a vector and initialize it.
X *
X * 23-Oct-92 -- sms
X *	The INIT block must (apparently) be quadword aligned [no thanks to
X *	the manual for not mentioning that fact].  The necessary alignment
X *	is achieved by allocating the INIT block from main memory ('malloc'
X *	guarantees click alignment) and mapping it as needed (which is _very_
X *	infrequently).  A check for quadword alignment of the ring descriptors
X *	was added - at present the descriptors are properly aligned, if this
X *	should change then something will have to be done (like do it "right").
X *	Darned alignment restrictions!
X *
X *	A couple of typos were corrected (missing parentheses, reversed 
X *	arguments to printf calls, etc).
X *
X * 13-Oct-92 -- sms@wlv.iipo.gtegsc.com
X *	Created based on the DELQA-PLUS addendum to DELQA User's Guide.
X *	This driver ('qt') is selected at system configuration time.  If the 
X *	board *	is not a DELQA-YM an error message will be printed and the 
X *	interface will not be attached.
X*/
X 
X#include "qt.h"
X#if	NQT > 0
X
X#include "param.h"
X#include "pdp/psl.h"
X#include "pdp/seg.h"
X#include "map.h"
X#include "systm.h"
X#include "mbuf.h"
X#include "buf.h"
X#include "protosw.h"
X#include "socket.h"
X#include "ioctl.h"
X#include "errno.h"
X#include "syslog.h"
X#include "time.h"
X#include "kernel.h"
X
X#include "../net/if.h"
X#include "../net/netisr.h"
X#include "../net/route.h"
X
X#ifdef INET
X#include "domain.h"
X#include "../netinet/in.h"
X#include "../netinet/in_systm.h"
X#include "../netinet/in_var.h"
X#include "../netinet/ip.h"
X#include "../netinet/if_ether.h"
X#endif
X
X#ifdef NS
X#include "../netns/ns.h"
X#include "../netns/ns_if.h"
X#endif
X
X#include "if_qtreg.h"
X#include "if_uba.h"
X#include "../pdpuba/ubavar.h"
X 
X#define NRCV	16	 	/* Receive descriptors (must be <= 32) */
X#define NXMT	6	 	/* Transmit descriptors	(must be <= 12) */
X#if	NRCV > 32 || NXMT > 12
X	generate an error
X#endif
X 
X	struct	qt_uba
X		{
X		struct	qt_uba	*next;	/* link to next buffer in list or
X					 * NULL if the last buffer
X					*/
X		struct	ifuba	ubabuf;	/* buffer descriptor */
X		};
X
Xstruct	qt_softc {
X	struct	arpcom is_ac;		/* common part - must be first  */
X#define	is_if	is_ac.ac_if		/* network-visible interface 	*/
X#define	is_addr	is_ac.ac_enaddr		/* hardware Ethernet address 	*/
X	struct	qt_uba	*freelist;	/* list of available buffers	*/
X	struct	qt_uba	ifrw[NRCV + NXMT];
X	u_short	initclick;		/* click addr of the INIT block */
X	struct	qt_rring *rring;	/* Receive ring address 	*/
X	struct	qt_tring *tring;	/* Transmit ring address 	*/
X	char	r_align[QT_MAX_RCV * sizeof (struct qt_rring) + 8];
X	char	t_align[QT_MAX_XMT * sizeof (struct qt_tring) + 8];
X	short	qt_flags;		/* software state		*/
X#define	QTF_RUNNING	0x1
X#define	QTF_STARTUP	0x2		/* Waiting for start interrupt  */
X	char	rindex;			/* Receive Completed Index	*/
X	char	nxtrcv;			/* Next Receive Index		*/
X	char	nrcv;			/* Number of Receives active	*/
X	char	tindex;			/* Transmit index		*/
X	char	otindex;		/* Old transmit index		*/
X	char	nxmit;			/* # of xmits in progress	*/
X	struct	qtdevice *addr;		/* device CSR addr		*/
X} qt_softc[NQT];
X
Xstruct	uba_device *qtinfo[NQT];
X 
Xint	qtattach(), qtintr(), qtinit(), qtoutput(), qtioctl();
X 
Xextern	struct ifnet loif;
X
Xu_short qtstd[] = { 0 };
X
Xstruct	uba_driver qtdriver =
X	{ 0, 0, qtattach, 0, qtstd, "qe", qtinfo };
X 
X/*
X * Maximum packet size needs to include 4 bytes for the CRC 
X * on received packets.
X*/
X#define MAXPACKETSIZE (ETHERMTU + sizeof (struct ether_header) + 4)
X#define	MINPACKETSIZE 64
X
X/*
X * The C compiler's propensity for prepending '_'s to names is the reason
X * for the hack below.  We need the "handler" address (the code which
X * sets up the interrupt stack frame) in order to initialize the vector.
X*/
X
Xstatic int qtfoo()
X	{
X	asm("mov $qtintr, r0");		/* return value is in r0 */
X	}
X
X/*
X * Interface exists.  More accurately, something exists at the CSR (see
X * sys/sys_net.c) -- there's no guarantee it's a DELQA-YM.
X *
X * The ring descriptors are initialized, the buffers allocated using first the
X * DMA region allocated at network load time and then later main memory.  The
X * INIT block is filled in and the device is poked/probed to see if it really
X * is a DELQA-YM.  If the device is not a -YM then a message is printed and
X * the 'if_attach' call is skipped.  For a -YM the START command is issued,
X * but the device is not marked as running|up - that happens at interrupt level
X * when the device interrupts to say it has started.
X*/
X
Xqtattach(ui)
X	struct uba_device *ui;
X	{
X	register struct qt_softc *sc = &qt_softc[ui->ui_unit];
X	register struct ifnet *ifp = &sc->is_if;
X	register struct qt_init *iniblk = (struct qt_init *)SEG5;
X	segm	seg5;
X	long	bufaddr;
X	extern int nextiv();
X 
X	ifp->if_unit = ui->ui_unit;
X	ifp->if_name = "qe";
X	ifp->if_mtu = ETHERMTU;
X	ifp->if_flags = IFF_BROADCAST;
X 
X/*
X * Fill in most of the INIT block: vector, options (interrupt enable), ring
X * locations.  The physical address is copied from the ROMs as part of the
X * -YM testing proceedure.  The CSR is saved here rather than in qtinit()
X * because the qtturbo() routine needs it.
X *
X * The INIT block must be quadword aligned.  Using malloc() guarantees click
X * (64 byte) alignment.  Since the only time the INIT block is referenced is
X * at 'startup' or 'reset' time there is really no time penalty (and a modest
X * D space savings) involved.
X*/
X	sc->addr = (struct qtdevice *)ui->ui_addr;
X	sc->initclick = MALLOC(coremap, btoc(sizeof (struct qt_init)));
X	saveseg5(seg5);
X	mapseg5(sc->initclick, 077406);
X	bzero(iniblk, sizeof (struct qt_init));
X	sc->rring = (struct qt_rring *)(((int)sc->r_align + 7) & ~7);
X	sc->tring = (struct qt_tring *)(((int)sc->t_align + 7) & ~7);
X
X/*
X * Fetch the next available interrupt vector.  The routine is in the kernel
X * (for several reasons) so use SKcall.  Then initialize the vector with
X * the address of our 'handler' and PSW of supervisor, priority 4 and unit
X*/
X	iniblk->vector = SKcall(nextiv, 0);
X	mtkd(iniblk->vector, qtfoo());
X	mtkd(iniblk->vector + 2, PSL_CURSUP | PSL_BR4 | ifp->if_unit);
X
X	iniblk->options = INIT_OPTIONS_INT;
X	bufaddr = startnet + (long)sc->rring;
X	iniblk->rx_lo = loint(bufaddr);
X	iniblk->rx_hi = hiint(bufaddr);
X	bufaddr = startnet + (long)sc->tring;
X	iniblk->tx_lo = loint(bufaddr);
X	iniblk->tx_hi = hiint(bufaddr);
X	restorseg5(seg5);
X
X/*
X * Now allocate the buffers and initialize the buffers.  This should _never_
X * fail because main memory is allocated after the DMA pool is used up.
X*/
X
X	if	(!qbaini(sc, NRCV + NXMT))
X		return;		/* XXX */
X 
X	ifp->if_init = qtinit;
X	ifp->if_output = qtoutput;
X	ifp->if_ioctl = qtioctl;
X	ifp->if_reset = 0;
X	if	(qtturbo(sc))
X		if_attach(ifp);
X	}
X 
Xqtturbo(sc)
X	register struct qt_softc *sc;
X	{
X	register int i;
X	register struct qtdevice *addr = sc->addr;
X	struct	qt_init *iniblk = (struct qt_init *)SEG5;
X	segm	seg5;
X
X/*
X * Issue the software reset.  Delay 150us.  The board should now be in
X * DELQA-Normal mode.  Set ITB and DEQTA select.  If both bits do not
X * stay turned on then the board is not a DELQA-YM.
X*/
X	addr->arqr = ARQR_SR;
X	addr->arqr = 0;
X	delay(150L);
X
X	addr->srr = 0x8001;		/* MS | ITB */
X	i = addr->srr;
X	addr->srr = 0x8000;		/* Turn off ITB, set DELQA select */
X	if	(i != 0x8001)
X		{
X		printf("qt@%o !-YM\n", addr);
X		return(0);
X		}
X/*
X * Board is a DELQA-YM.  Send the commands to enable Turbo mode.  Delay
X * 1 second, testing the SRR register every millisecond to see if the
X * board has shifted to Turbo mode.
X*/
X	addr->xcr0 = 0x0baf;
X	addr->xcr1 = 0xff00;
X	for	(i = 0; i < 1000; i++)
X		{
X		if	((addr->srr & SRR_RESP) == 1)
X			break;
X		delay(1000L);
X		}
X	if	(i >= 1000)
X		{
X		printf("qt@%o !Turbo\n", addr);
X		return(0);
X		}
X/*
X * Board has entered Turbo mode.  Now copy the physical address from the
X * ROMs to the INIT block.  Fill in the address in the part of the structure 
X * "visible" to the rest of the system.
X*/
X	saveseg5(seg5);
X	mapseg5(sc->initclick, 077406);
X	sc->is_addr[0] = addr->sarom[0];
X	sc->is_addr[1] = addr->sarom[2];
X	sc->is_addr[2] = addr->sarom[4];
X	sc->is_addr[3] = addr->sarom[6];
X	sc->is_addr[4] = addr->sarom[8];
X	sc->is_addr[5] = addr->sarom[10];
X	bcopy(sc->is_addr, iniblk->paddr, 6);
X	restorseg5(seg5);
X	return(1);
X	}
X
Xqtinit(unit)
X	int unit;
X	{
X	int	s;
X	register struct qt_softc *sc = &qt_softc[unit];
X	struct qtdevice *addr = sc->addr;
X	struct	ifnet *ifp = &sc->is_if;
X	struct	qt_rring *rp;
X	struct	qt_tring *tp;
X	register struct	qt_uba	*xp;
X	register int i;
X	long	buf_adr;
X 
X	if	(!ifp->if_addrlist)		/* oops! */
X		return;
X/*
X * Now initialize the receive ring descriptors.  Because this routine can be
X * called with outstanding I/O operations we check the ring descriptors for
X * a non-zero 'rhost0' (or 'thost0') word and place those buffers back on
X * the free list.
X*/
X	for	(i = 0, rp = sc->rring; i < QT_MAX_RCV; i++, rp++)
X		{
X		rp->rmd3 = RMD3_OWN;
X		if	(xp = rp->rhost0)
X			{
X			rp->rhost0 = 0;
X			xp->next = sc->freelist;
X			sc->freelist = xp;
X			}
X		}
X	for	(i = 0, tp = sc->tring ; i < QT_MAX_XMT; i++, tp++)
X		{
X		sc->tring[i].tmd3 = TMD3_OWN;
X		if	(xp = tp->thost0)
X			{
X			tp->thost0 = 0;
X			xp->next = sc->freelist;
X			sc->freelist = xp;
X			}
X		}
X 
X	sc->nxmit = 0;
X	sc->otindex = 0;
X	sc->tindex = 0;
X	sc->rindex = 0;
X	sc->nxtrcv = 0;
X	sc->nrcv = 0;
X 
X	s = splimp();
X/*
X * Now we tell the device the address of the INIT block.  The device
X * _must_ be in the Turbo mode at this time.  The "START" command is
X * then issued to the device.  A 1 second timeout is then started.
X * When the interrupt occurs the IFF_UP|IFF_RUNNING state is entered and
X * full operations will proceed.  If the timeout expires without an interrupt 
X * being received an error is printed, the flags cleared and the device left
X * marked down.
X*/
X	buf_adr = ctob((long)sc->initclick);
X	addr->ibal = loint(buf_adr);
X	addr->ibah = hiint(buf_adr);
X	addr->srqr = 2;
X/*
X * set internal state to 'startup' and start a one second timer.  the interrupt
X * service routine will be entered either 1) when the device posts the 'start'
X * interrupt or 2) when the timer expires.  The interrupt routine will fill
X * the receive rings, etc.
X*/
X	sc->qt_flags = QTF_STARTUP;
X	TIMEOUT(qtintr, unit, 60);
X	splx(s);
X	}
X
X/*
X * Start output on interface.
X */
X
Xqtstart(unit)
X	int	unit;
X	{
X	int 	len, s;
X	register struct qt_softc *sc = &qt_softc[unit];
X	register struct qt_tring *rp;
X	struct	mbuf *m;
X	long	buf_addr;
X	register struct	qt_uba *xp;
X 
X	s = splimp();
X	while	(sc->nxmit < NXMT)
X		{
X		IF_DEQUEUE(&sc->is_if.if_snd, m);
X		if	(m == 0)
X			break;
X		rp = &sc->tring[sc->tindex];
X#ifdef	QTDEBUG
X		if	((rp->tmd3 & TMD3_OWN) == 0)
X			printf("qt xmit in progress\n");
X#endif
X/*
X * Now pull a buffer off of the freelist.  Guaranteed to be a buffer
X * because both the receive and transmit sides limit themselves to
X * NRCV and NXMT buffers respectively.
X*/
X		xp = sc->freelist;
X		sc->freelist = xp->next;
X
X		buf_addr = xp->ubabuf.ifu_w.ifrw_info;
X		len = if_wubaput(&xp->ubabuf, m);
X		if	(len < MINPACKETSIZE)
X			len = MINPACKETSIZE;
X		rp->tmd4 = loint(buf_addr);
X		rp->tmd5 = hiint(buf_addr) & TMD5_HADR;
X		rp->tmd3 = len & TMD3_BCT;	/* set length,clear ownership */
X		rp->thost0 = xp;		/* set entry active */
X		sc->addr->arqr = ARQR_TRQ;	/* tell device it has buffer */
X		sc->nxmit++;
X		if	(++sc->tindex >= QT_MAX_XMT)
X			sc->tindex = 0;
X		}
X	splx(s);
X	}
X 
X/*
X * General interrupt service routine.  Receive, transmit, device start 
X * interrupts and timeouts come here.  Check for hard device errors and print a
X * message if any errors are found.  If we are waiting for the device to 
X * START then check if the device is now running.
X*/
X
Xqtintr(unit)
X	int unit;
X	{
X	register struct qt_softc *sc = &qt_softc[unit];
X	register int status;
X	int	s;
X 
X	status = sc->addr->srr;
X	if	(status < 0)
X		/* should we reset the device after a bunch of these errs? */
X		qtsrr(unit, status);
X	if	(sc->qt_flags == QTF_STARTUP)
X		{
X		if	((status & SRR_RESP) == 2)
X			{
X			sc->qt_flags = QTF_RUNNING;
X			sc->is_if.if_flags |= (IFF_UP | IFF_RUNNING);
X			}
X		else
X			printf("qt%d !start\n", unit);
X		}
X	s = splimp();
X	qtrint(unit);
X	if	(sc->nxmit)
X		qttint(unit);
X	qtstart(unit);
X	splx(s);
X	}
X 
X/*
X * Transmit interrupt service.  Only called if there are outstanding transmit
X * requests which could have completed.  The DELQA-YM doesn't provide the
X * status bits telling the kind (receive, transmit) of interrupt.
X*/
X 
X#define BBLMIS (TMD2_BBL|TMD2_MIS)
X
Xqttint(unit)
X	int unit;
X	{
X	register struct qt_softc *sc = &qt_softc[unit];
X	register struct qt_tring *rp;
X	register struct	qt_uba *xp;
X 
X	while	(sc->nxmit > 0)
X		{
X		rp = &sc->tring[sc->otindex];
X		if	((rp->tmd3 & TMD3_OWN) == 0)
X			break;
X/*
X * Now check the buffer address (the first word in the ring descriptor
X * available for the host's use).  If it is NULL then we have already seen
X * and processed (or never presented to the board in the first place) this 
X * entry and the ring descriptor should not be counted.
X*/
X		xp = rp->thost0;
X		if	(xp == 0)
X			break;
X/*
X * Clear the buffer address from the ring descriptor and put the
X * buffer back on the freelist for future use.
X*/
X		rp->thost0 = 0;
X		xp->next = sc->freelist;
X		sc->freelist = xp;
X
X		sc->nxmit--;
X		sc->is_if.if_opackets++;
X/*
X * Collisions don't count as output errors, but babbling and missing packets
X * do count as output errors.
X*/
X		if	(rp->tmd2 & TMD2_CER)
X			sc->is_if.if_collisions++;
X		if	((rp->tmd0 & TMD0_ERR1) || 
X			 ((rp->tmd2 & TMD2_ERR2) && (rp->tmd2 & BBLMIS)))
X			{
X#ifdef QTDEBUG
X			printf("qt%d tmd2 %b\n", unit, rp->tmd2, TMD2_BITS);
X#endif
X			sc->is_if.if_oerrors++;
X			}
X		if	(++sc->otindex >= QT_MAX_XMT)
X			sc->otindex = 0;
X		}
X	}
X 
X/*
X * Receive interrupt service.  Pull packet off the interface and put into
X * a mbuf chain for processing later.
X*/
X
Xqtrint(unit)
X	int unit;
X	{
X	register struct qt_softc *sc = &qt_softc[unit];
X	register struct qt_rring *rp;
X	register struct	qt_uba *xp;
X	int	len;
X	long	bufaddr;
X 
X	while	(sc->rring[sc->rindex].rmd3 & RMD3_OWN)
X		{
X		rp = &sc->rring[sc->rindex];
X/*
X * If the host word is 0 then this is a buffer either already seen or not
X * presented to the device in the first place.
X*/
X		xp = rp->rhost0;
X		if	(xp == 0)
X			break;
X
X		if     ((rp->rmd0 & (RMD0_STP|RMD0_ENP)) != (RMD0_STP|RMD0_ENP))
X			{
X			printf("qt%d chained packet\n", unit);
X			sc->is_if.if_ierrors++;
X			goto rnext;
X			}
X		len = (rp->rmd1 & RMD1_MCNT) - 4;	/* -4 for CRC */
X		sc->is_if.if_ipackets++;
X 
X		if	((rp->rmd0 & RMD0_ERR3) || (rp->rmd2 & RMD2_ERR4))
X			{
X			sc->is_if.if_ierrors++;
X#ifdef QTDEBUG
X			printf("qt%d rmd0 %b\n", unit, rp->rmd0, RMD0_BITS);
X			printf("qt%d rmd2 %b\n", unit, rp->rmd2, RMD2_BITS);
X#endif
X			}
X		else
X			qtread(sc, &xp->ubabuf,
X				len - sizeof (struct ether_header));
Xrnext:
X		--sc->nrcv;
X		if	(++sc->rindex >= QT_MAX_RCV)
X			sc->rindex = 0;
X/*
X * put the buffer back on the free list and clear the first host word
X * in the ring descriptor so we don't process this one again.
X*/
X		xp->next = sc->freelist;
X		sc->freelist = xp;
X		rp->rhost0 = 0;
X		}
X	while	(sc->nrcv < NRCV)
X		{
X		rp = &sc->rring[sc->nxtrcv];
X#ifdef	QTDEBUG
X		if	((rp->rmd3 & RMD3_OWN) == 0)
X			printf("qtrint: !OWN\n");
X#endif
X		xp = sc->freelist;
X		sc->freelist = xp->next;
X		bufaddr = xp->ubabuf.ifu_r.ifrw_info;
X		rp->rmd1 = MAXPACKETSIZE;
X		rp->rmd4 = loint(bufaddr);
X		rp->rmd5 = hiint(bufaddr);
X		rp->rhost0 = xp;
X		rp->rmd3 = 0;			/* clear RMD3_OWN */
X		++sc->nrcv;
X		sc->addr->arqr = ARQR_RRQ;	/* tell device it has buffer */
X		if	(++sc->nxtrcv >= QT_MAX_RCV)
X			sc->nxtrcv = 0;
X		}
X	}
X
X/*
X * Place data on the appropriate queue and call the start routine to
X * send the data to the device.
X*/
X
Xqtoutput(ifp, m0, dst)
X	struct	ifnet *ifp;
X	struct	mbuf *m0;
X	struct	sockaddr *dst;
X	{
X	int	type, s, trail;
X	u_char	edst[6];
X	struct	in_addr idst;
X	register struct ether_header *eh;
X	register struct qt_softc *is = &qt_softc[ifp->if_unit];
X	register struct mbuf *m = m0;
X	struct	mbuf *mcopy = (struct mbuf *)0;
X 
X	if	((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
X		{
X		m_freem(m0);
X		return(ENETDOWN);
X		}
X	switch	(dst->sa_family)
X		{
X#ifdef INET
X		case	AF_INET:
X			idst = ((struct sockaddr_in *)dst)->sin_addr;
X			if	(!arpresolve(&is->is_ac, m, &idst, edst,&trail))
X				return(0);	/* wait for arp to finish */
X			if	(!bcmp(edst, etherbroadcastaddr,sizeof (edst)))
X				mcopy = m_copy(m, 0, (int)M_COPYALL);
X			type = ETHERTYPE_IP;
X			break;
X#endif
X#ifdef NS
X		case	AF_NS:
X			type = ETHERTYPE_NS;
X			bcopy(&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
X		    		edst, sizeof (edst));
X			if	(!bcmp(edst, &ns_broadcast, sizeof (edst)))
X				return(looutput(&loif, m, dst));
X			break;
X#endif
X		case	AF_UNSPEC:
X			eh = (struct ether_header *)dst->sa_data;
X			bcopy(eh->ether_dhost, (caddr_t)edst, sizeof (edst));
X			type = eh->ether_type;
X			break;
X		default:
X			printf("qt%d can't handle af%d\n", ifp->if_unit,
X				dst->sa_family);
X			m_freem(m);
X			return(EAFNOSUPPORT);
X		}
X/*
X * Add local net header.  If no space in first mbuf, allocate another.
X*/
X	if	(m->m_off > MMAXOFF ||
X			MMINOFF + sizeof (struct ether_header) > m->m_off)
X		{
X		m = m_get(M_DONTWAIT, MT_HEADER);
X		if	(m == 0)
X			{
X			m_freem(m0);
Xnobufs:			if	(mcopy)
X				m_freem(mcopy);
X			return(ENOBUFS);
X			}
X		m->m_next = m0;
X		m->m_off = MMINOFF;
X		m->m_len = sizeof (struct ether_header);
X		}
X	else
X		{
X		m->m_off -= sizeof (struct ether_header);
X		m->m_len += sizeof (struct ether_header);
X		}
X	eh = mtod(m, struct ether_header *);
X	eh->ether_type = htons((u_short)type);
X 	bcopy(edst, (caddr_t)eh->ether_dhost, sizeof (edst));
X 	bcopy(is->is_addr, (caddr_t)eh->ether_shost, sizeof (is->is_addr));
X 
X	s = splimp();
X	if	(IF_QFULL(&ifp->if_snd))
X		{
X		IF_DROP(&ifp->if_snd);
X		m_freem(m);
X		splx(s);
X		goto nobufs;
X		}
X	IF_ENQUEUE(&ifp->if_snd, m);
X	qtstart(ifp->if_unit);
X	splx(s);
X	return(mcopy ? looutput(&loif, mcopy, dst) : 0);
X	}
X 
Xqtioctl(ifp, cmd, data)
X	register struct ifnet *ifp;
X	int	cmd;
X	caddr_t	data;
X	{
X	struct qt_softc *sc = &qt_softc[ifp->if_unit];
X	struct ifaddr *ifa = (struct ifaddr *)data;
X	int s = splimp(), error = 0;
X#ifdef	NS
X	register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
X#endif
X 
X	switch	(cmd)
X		{
X		case	SIOCSIFADDR:
X/*
X * Resetting the board is probably overkill, but then again this is only
X * done when the system comes up or possibly when a reset is needed after a
X * major network fault (open wire, etc).
X*/
X			qtrestart(sc);
X			switch	(ifa->ifa_addr.sa_family)
X				{
X#ifdef INET
X				case	AF_INET:
X					((struct arpcom *)ifp)->ac_ipaddr =
X						IA_SIN(ifa)->sin_addr;
X					arpwhohas(ifp, &IA_SIN(ifa)->sin_addr);
X					break;
X#endif
X#ifdef NS
X				case	AF_NS:
X					if	(ns_nullhost(*ina))
X						ina->x_host = 
X						*(union ns_host *)(sc->is_addr);
X					else
X						{
X						qt_ns(ina->x_host.c_host);
X						qtrestart(sc);
X						}
X					break;
X#endif
X				}
X			break;
X		case	SIOCSIFFLAGS:
X			if	((ifp->if_flags & IFF_UP) == 0 &&
X				    sc->qt_flags & QTF_RUNNING)
X				{
X/*
X * We've been asked to stop the board and leave it that way.  qtturbo()
X * does this with the side effect of placing the device back in Turbo mode.
X*/
X				qtturbo(sc);
X				sc->qt_flags &= ~QTF_RUNNING;
X				}
X			else if (ifp->if_flags & IFF_UP &&
X					!(sc->qt_flags & QTF_RUNNING))
X				qtrestart(sc);
X			break;
X		default:
X			error = EINVAL;
X		}
X	splx(s);
X	return(error);
X	}
X 
X#ifdef	NS
Xqt_ns(cp)
X	register char *cp;
X	{
X	segm	seg5;
X	register struct qt_init *iniblk = (struct qt_init *)SEG5;
X
X	saveseg5(seg5);
X	mapseg5(sc->initclick, 077406);
X	bcopy(cp, sc->is_addr, 6);
X	bcopy(cp, iniblk->paddr, 6);
X	restorseg5(seg5);
X	}
X#endif
X
X/*
X * Pull the data off of the board and pass back to the upper layers of
X * the networking code.  Trailers are counted as errors and the packet
X * dropped.  SEG5 is saved and restored (used to peek at the packet type).
X*/
X
Xqtread(sc, ifuba, len)
X	register struct qt_softc *sc;
X	struct	ifuba *ifuba;
X	int	len;
X	{
X	struct	ether_header *eh;
X    	register struct	mbuf *m;
X	struct	ifqueue *inq;
X	int	type;
X	segm	seg5;
X 
X	saveseg5(seg5);
X	mapseg5(ifuba->ifu_r.ifrw_click, 077406);
X	eh = (struct ether_header *)SEG5;
X	eh->ether_type = ntohs((u_short)eh->ether_type);
X	type = eh->ether_type;
X	restorseg5(seg5);
X	if	(len == 0 || type >= ETHERTYPE_TRAIL &&
X			type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER)
X		{
X		sc->is_if.if_ierrors++;
X		return;
X		}
X
X	m = if_rubaget(ifuba, len, 0, &sc->is_if);
X	if	(m == 0)
X		return;
X 
X	switch	(type)
X		{
X#ifdef INET
X		case	ETHERTYPE_IP:
X			schednetisr(NETISR_IP);
X			inq = &ipintrq;
X			break;
X		case	ETHERTYPE_ARP:
X			arpinput(&sc->is_ac, m);
X			return;
X#endif
X#ifdef NS
X		case	ETHERTYPE_NS:
X			schednetisr(NETISR_NS);
X			inq = &nsintrq;
X			break;
X#endif
X		default:
X			m_freem(m);
X			return;
X		}
X 
X	if	(IF_QFULL(inq))
X		{
X		IF_DROP(inq);
X		m_freem(m);
X		return;
X		}
X	IF_ENQUEUE(inq, m);
X	}
X
X
Xqtsrr(unit, srrbits)
X	int	unit, srrbits;
X	{
X	printf("qt%d srr=%b\n", unit, srrbits, SRR_BITS);
X	}
X
X/*
X * Reset the device.  This moves it from DELQA-T mode to DELQA-Normal mode.
X * After the reset put the device back in -T mode.  Then call qtinit() to
X * reinitialize the ring structures and issue the 'timeout' for the "device
X * started interrupt".
X*/
X
Xqtrestart(sc)
X	register struct qt_softc *sc;
X	{
X 
X	qtturbo(sc);
X	qtinit(sc - qt_softc);
X	}
X
Xqbaini(sc, num)
X	struct	qt_softc *sc;
X	int num;
X	{
X	register int i;
X	register memaddr click;
X	struct	qt_uba	*xp;
X	register struct	ifuba	*ifuba;
X
X	for	(i = 0; i < num; i++)
X		{
X		xp = &sc->ifrw[i];
X		ifuba = &xp->ubabuf;
X		click = m_ioget(MAXPACKETSIZE);
X		if	(click == 0)
X			{
X			click = MALLOC(coremap, btoc(MAXPACKETSIZE));
X			if	(click == 0)
X				return(0);	/* _can't_ happen */
X			}
X		ifuba->ifu_hlen = sizeof (struct ether_header);
X		ifuba->ifu_w.ifrw_click = ifuba->ifu_r.ifrw_click = click;
X		ifuba->ifu_w.ifrw_info = ifuba->ifu_r.ifrw_info = 
X			ctob((long)click);
X		xp->next = sc->freelist;
X		sc->freelist = xp;
X		}
X	return(1);
X	}
X#endif
SHAR_EOF
fi
if test -f 'if_qtreg.h'
then
	echo shar: "will not over-write existing file 'if_qtreg.h'"
else
sed 's/^X//' << \SHAR_EOF > 'if_qtreg.h'
X/*	@(#)if_qtreg.h	1.0 (GTE) 10/12/92 */
X
X/*
X * Modification History 
X *  26 Feb 93 -- sms
X *	Add defines for number of receive and transmit ring descriptors.
X *
X *  12 Oct 92 -- Steven M. Schultz (sms)
X *	Created from the DELQA-PLUS Addendum to the DELQA User's Guide.
X*/
X
X#define	QT_MAX_RCV	32
X#define	QT_MAX_XMT	12
X
X/* Receive ring descriptor and bit/field definitions */
X
X	struct	qt_rring
X		{
X		short	rmd0;
X		short	rmd1;
X		short	rmd2;
X		short	rmd3;
X		short	rmd4;
X		short	rmd5;
X		struct	qt_uba	*rhost0;
X		short	rhost1;
X		};
X
X#define	RMD0_ERR3	0x4000		/* Error summary. FRA|CRC|OFL|BUF */
X#define	RMD0_FRA	0x2000		/* Framing error */
X#define	RMD0_OFL	0x1000		/* Overflow error.  Oversized packet */
X#define	RMD0_CRC	0x0800		/* CRC error */
X#define	RMD0_BUF	0x0400		/* Internal device buffer error */
X#define	RMD0_STP	0x0200		/* Start of packet */
X#define	RMD0_ENP	0x0100		/* End of packet */
X
X#define	RMD1_MCNT	0x0fff		/* Message byte count */
X
X#define	RMD2_ERR4	0x8000		/* Error summary.  BBL|CER|MIS */
X#define	RMD2_BBL	0x4000		/* Babble error on transmit */
X#define	RMD2_CER	0x2000		/* Collision error on transmit */
X#define	RMD2_MIS	0x1000		/* Packet lost on receive */
X#define	RMD2_EOR	0x0800		/* End of receive ring */
X#define	RMD2_RON	0x0020		/* Receiver on */
X#define	RMD2_TON	0x0010		/* Transmitter on */
X
X#define	RMD3_OWN	0x8000		/* Ownership field. */
X
X#define	RMD4_LADR	0xfff8		/* Octabyte aligned low address bits */
X
X#define	RMD5_HADR	0x003f		/* High 6 bits of buffer address */
X
X#define	RMD0_BITS	"\010\016FRA\015OFL\014CRC\013BUF\012STP\011ENP"
X#define	RMD2_BITS	"\010\017BBL\014CER\013MIS\012EOR\06RON\05TON"
X
X/* Transmit ring descriptor and bit/field definitions */
X
X	struct	qt_tring
X		{
X		short	tmd0;
X		short	tmd1;
X		short	tmd2;
X		short	tmd3;
X		short	tmd4;
X		short	tmd5;
X		struct	qt_uba	*thost0;
X		short	thost1;
X		};
X
X#define	TMD0_ERR1	0x4000		/* Error summary.  LCO|LCA|RTR */
X#define	TMD0_MOR	0x1000		/* More than one retry on transmit */
X#define	TMD0_ONE	0x0800		/* One retry on transmit */
X#define	TMD0_DEF	0x0400		/* Deferral during transmit */
X
X#define	TMD1_LCO	0x1000		/* Late collision on transmit */
X#define	TMD1_LCA	0x0800		/* Loss of carrier on transmit */
X#define	TMD1_RTR	0x0400		/* Retry error on transmit */
X#define	TMD1_TDR	0x03ff		/* Time Domain Reflectometry value */
X
X#define	TMD2_ERR2	0x8000		/* Error summary.  BBL|CER|MIS */
X#define	TMD2_BBL	0x4000		/* Babble error on transmit */
X#define	TMD2_CER	0x2000		/* Collision error on transmit */
X#define	TMD2_MIS	0x1000		/* Packet lost on receive */
X#define	TMD2_EOR	0x0800		/* Endof Receive ring reached */
X#define	TMD2_RON	0x0020		/* Receiver on */
X#define	TMD2_TON	0x0010		/* Transmitter on */
X
X#define	TMD3_OWN	0x8000		/* Ownership field */
X#define	TMD3_FOT	0x4000		/* First of two flag */
X#define	TMD3_BCT	0x0fff		/* Byte count */
X
X#define	TMD4_LADR	0xfff8		/* Octabyte aligned low address bits */
X
X#define	TMD5_HADR	0x003f		/* High 6 bits of buffer address */
X
X#define	TMD1_BITS	"\010\015LCO\014LCA\013RTR"
X#define	TMD2_BITS	"\010\017BBL\016CER\015MIS\014EOR\06RON\05TON"
X
X/* DELQA-YM CSR layout */
X
X	struct	qtcsr0
X		{
X		short	Ibal;
X		short	Ibah;
X		short	Icr;
X		short	pad0;
X		short	Srqr;
X		short	pad1;
X		};
X
X	struct	qtdevice
X		{
X		union	{
X			u_char	Sarom[12];
X			struct	qtcsr0	csr0;
X			} qt_un0;
X		short	srr;
X		short	arqr;
X		};
X
X#define	ibal	qt_un0.csr0.Ibal
X#define	ibah	qt_un0.csr0.Ibah
X#define	srqr	qt_un0.csr0.Srqr
X#define	icr	qt_un0.csr0.Icr
X#define	sarom	qt_un0.Sarom
X
X/* SRR definitions */
X
X#define	SRR_FES		0x8000
X#define	SRR_CHN		0x4000
X#define	SRR_NXM		0x1000
X#define	SRR_PER		0x0800
X#define	SRR_IME		0x0400
X#define	SRR_TBL		0x0200
X#define	SRR_RESP	0x0003
X#define	SRR_BITS	"\010\017CHN\015NXM\014PER\013IME\012TBL"
X
X/* SRQR definitions */
X
X#define	SRQR_REQ	0x0003
X
X/* ARQR definitions */
X
X#define	ARQR_TRQ	0x8000
X#define	ARQR_RRQ	0x0080
X#define	ARQR_SR		0x0002
X
X/* define ICR definitions */
X
X#define	ICR_CMD		0x0001
X
X/* DELQA registers used to shift into -T mode */
X
X#define	xcr0	qt_un0.csr0.Ibal
X#define	xcr1	qt_un0.csr0.Ibah
X
X/* INIT block structure and definitions */
X
X	struct	qt_init
X		{
X		short	mode;
X		u_char	paddr[6];	/* 48 bit physical address */
X		u_char	laddr[8];	/* 64 bit logical address filter */
X		u_short	rx_lo;		/* low 16 bits of receive ring addr */
X		u_short	rx_hi;		/* high 6 bits of receive ring addr */
X		u_short	tx_lo;		/* low 16 bits of transmit ring addr */
X		u_short	tx_hi;		/* high 6 bits of transmit ring addr */
X		u_short	options;
X		u_short	vector;
X		u_short	hit;
X		char	passwd[6];
X		};
X
X#define	INIT_MODE_PRO	0x8000		/* Promiscuous mode */
X#define	INIT_MODE_INT	0x0040		/* Internal Loopback */
X#define	INIT_MODE_DRT	0x0020		/* Disable Retry */
X#define	INIT_MODE_DTC	0x0008		/* Disable Transmit CRC */
X#define	INIT_MODE_LOP	0x0004		/* Loopback */
X
X#define	INIT_OPTIONS_HIT 0x0002		/* Host Inactivity Timeout Flag */
X#define	INIT_OPTIONS_INT 0x0001		/* Interrupt Enable Flag */
SHAR_EOF
fi
exit 0
#	End of shell archive
