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