386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 15 Jul 1992 02:40:23 +0000 (18:40 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 15 Jul 1992 02:40:23 +0000 (18:40 -0800)
Work on file usr/src/sys.386bsd/i386/isa/if_ne.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/sys.386bsd/i386/isa/if_ne.c [new file with mode: 0644]

diff --git a/usr/src/sys.386bsd/i386/isa/if_ne.c b/usr/src/sys.386bsd/i386/isa/if_ne.c
new file mode 100644 (file)
index 0000000..e1a6c78
--- /dev/null
@@ -0,0 +1,855 @@
+/*-
+ * Copyright (c) 1990, 1991 William F. Jolitz.
+ * Copyright (c) 1990 The Regents of the University of California.
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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_ne.c     7.4 (Berkeley) 5/21/91
+ */
+
+/*
+ * NE2000 Ethernet driver
+ *
+ * Parts inspired from Tim Tucker's if_wd driver for the wd8003,
+ * insight on the ne2000 gained from Robert Clements PC/FTP driver.
+ */
+
+#include "ne.h"
+#if NNE > 0
+
+#include "param.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 "net/if.h"
+#include "net/netisr.h"
+#include "net/route.h"
+
+#ifdef INET
+#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 "i386/isa/isa_device.h"
+#include "i386/isa/if_nereg.h"
+#include "i386/isa/icu.h"
+
+int    neprobe(), neattach(), neintr();
+int    nestart(),neinit(), ether_output(), neioctl();
+
+struct isa_driver nedriver = {
+       neprobe, neattach, "ne",
+};
+
+struct mbuf *neget();
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1536
+
+/*
+ * Ethernet software status per interface.
+ *
+ * Each interface is referenced by a network interface structure,
+ * ns_if, which the routing code uses to locate the interface.
+ * This structure contains the output queue for the interface, its address, ...
+ */
+struct ne_softc {
+       struct  arpcom ns_ac;           /* Ethernet common part */
+#define        ns_if   ns_ac.ac_if             /* network-visible interface */
+#define        ns_addr ns_ac.ac_enaddr         /* hardware Ethernet address */
+       int     ns_flags;
+#define        DSF_LOCK        1               /* block re-entering enstart */
+       int     ns_oactive ;
+       int     ns_mask ;
+       int     ns_ba;                  /* byte addr in buffer ram of inc pkt */
+       int     ns_cur;                 /* current page being filled */
+       struct  prhdr   ns_ph;          /* hardware header of incoming packet*/
+       struct  ether_header ns_eh;     /* header of incoming packet */
+       u_char  ns_pb[2048 /*ETHERMTU+sizeof(long)*/];
+       short   ns_txstart;             /* transmitter buffer start */
+       short   ns_rxend;               /* recevier buffer end */
+       short   ns_rxbndry;             /* recevier buffer boundary */
+       short   ns_port;                /* i/o port base */
+       short   ns_mode;                /* word/byte mode */
+} ne_softc[NNE] ;
+#define        ENBUFSIZE       (sizeof(struct ether_header) + ETHERMTU + 2 + ETHER_MIN_LEN)
+
+#define        PAT(n)  (0xa55a + 37*(n))
+
+u_short boarddata[16];
+neprobe(dvp)
+       struct isa_device *dvp;
+{
+       int val, i, s, sum, bytemode = 1, pat;
+       register struct ne_softc *ns = &ne_softc[0];
+       register nec;
+
+#ifdef lint
+       neintr(0);
+#endif
+
+       nec = ns->ns_port = dvp->id_iobase;
+       s = splimp();
+
+       if (bytemode) {
+               /* Byte Transfers, Burst Mode Select, Fifo at 8 bytes */
+               ns->ns_mode = DSDC_BMS|DSDC_FT1;
+               ns->ns_txstart = TBUF8;
+               ns->ns_rxend = RBUFEND8;
+       } else {
+word:
+               /* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
+               ns->ns_mode = DSDC_WTS|DSDC_BMS|DSDC_FT1;
+               ns->ns_txstart = TBUF16;
+               ns->ns_rxend = RBUFEND16;
+               bytemode = 0;
+       }
+
+       /* Reset the bastard */
+       val = inb(nec + ne_reset);
+       DELAY(200);
+       outb(nec + ne_reset, val);
+       DELAY(200);
+
+       outb(nec + ds_cmd, DSCM_STOP|DSCM_NODMA);
+       
+       i = 10000;
+       while ((inb(nec + ds0_isr) & DSIS_RESET) == 0 && i-- > 0);
+       if (i < 0) return (0);
+
+       outb(nec + ds0_isr, 0xff);
+       outb(nec + ds0_dcr, ns->ns_mode);
+       outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
+       DELAY(1000);
+
+       /* Check cmd reg and fail if not right */
+       if ((i = inb(nec + ds_cmd)) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP))
+               return(0);
+
+       outb(nec + ds0_tcr, 0);
+       outb(nec + ds0_rcr, DSRC_MON);
+       outb(nec + ds0_pstart, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
+       outb(nec + ds0_pstop, ns->ns_rxend/DS_PGSIZE);
+       outb(nec + ds0_bnry, ns->ns_rxend/DS_PGSIZE);
+       outb(nec + ds0_imr, 0);
+       outb(nec + ds0_isr, 0);
+       outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
+       outb(nec + ds1_curr, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
+       outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
+
+
+#ifdef NEDEBUG
+#define        RCON    37
+       {       int i, rom;
+
+               rom=1;
+               printf("ne ram ");
+               
+               for (i = 0; i < 0xfff0; i+=4) {
+                       pat = PAT(i);
+                       neput(ns, &pat,i,4);
+                       nefetch(ns, &pat,i,4);
+                       if (pat == PAT(i)) {
+                               if (rom) {
+                                       rom=0;
+                                       printf(" %x", i);
+                               }
+                       } else {
+                               if (!rom) {
+                                       rom=1;
+                                       printf("..%x ", i);
+                               }
+                       }
+                       pat=0;
+                       neput(ns, &pat,i,4);
+               }
+               printf("\n");
+       }
+#endif
+
+       /*
+        * <groan> detect difference between units
+        * solely by where the RAM is decoded.
+        */
+       pat = PAT(0);
+       neput(ns, &pat, ns->ns_txstart, 4);
+       nefetch(ns, &pat, ns->ns_txstart, 4);
+       if (pat != PAT(0)) {
+               if (bytemode)
+                       goto word;
+               else return (0);
+       }
+
+
+       /* Extract board address */
+       nefetch (ns, (caddr_t)boarddata, 0, sizeof(boarddata));
+
+       for(i=0; i < 6; i++)
+               ns->ns_addr[i] = boarddata[i];
+       splx(s);
+       return (1);
+}
+
+/*
+ * Fetch from onboard ROM/RAM
+ */
+nefetch (ns, up, ad, len) struct ne_softc *ns; caddr_t up; {
+       u_char cmd;
+       register nec = ns->ns_port;
+       int counter = 100000;
+
+       cmd = inb (nec + ds_cmd);
+       outb (nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
+
+       /* Setup remote dma */
+       outb (nec + ds0_isr, DSIS_RDC);
+
+       if ((ns->ns_mode & DSDC_WTS) && len&1)
+               len++;          /* roundup to words */
+
+       outb (nec+ds0_rbcr0, len);
+       outb (nec+ds0_rbcr1, len>>8);
+       outb (nec+ds0_rsar0, ad);
+       outb (nec+ds0_rsar1, ad>>8);
+
+       /* Execute & extract from card */
+       outb (nec+ds_cmd, DSCM_RREAD|DSCM_PG0|DSCM_START);
+
+       if (ns->ns_mode & DSDC_WTS)
+               insw (nec+ne_data, up, len/2);
+       else
+               insb (nec+ne_data, up, len);
+
+       /* Wait till done, then shutdown feature */
+       while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
+               ;
+       outb (nec+ds0_isr, DSIS_RDC);
+       outb (nec+ds_cmd, cmd);
+}
+
+/*
+ * Put to onboard RAM
+ */
+neput (ns, up, ad, len) struct ne_softc *ns; caddr_t up; {
+       u_char cmd;
+       register nec = ns->ns_port;
+       int counter = 100000;
+
+       cmd = inb(nec+ds_cmd);
+       outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
+
+       /* Setup for remote dma */
+       outb (nec+ds0_isr, DSIS_RDC);
+
+       if ((ns->ns_mode & DSDC_WTS) && len&1)
+               len++;          /* roundup to words */
+
+       outb (nec+ds0_rbcr0, len);
+       outb (nec+ds0_rbcr1, len>>8);
+       outb (nec+ds0_rsar0, ad);
+       outb (nec+ds0_rsar1, ad>>8);
+
+       /* Execute & stuff to card */
+       outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START);
+       if (ns->ns_mode & DSDC_WTS)
+               outsw (nec+ne_data, up, len/2);
+       else
+               outsb (nec+ne_data, up, len);
+       
+       /* Wait till done, then shutdown feature */
+       while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
+               ;
+       outb (nec+ds0_isr, DSIS_RDC);
+       outb (nec+ds_cmd, cmd);
+}
+
+/*
+ * Reset of interface.
+ */
+nereset(unit, uban)
+       int unit, uban;
+{
+       if (unit >= NNE)
+               return;
+       printf("ne%d: reset\n", unit);
+       ne_softc[unit].ns_flags &= ~DSF_LOCK;
+       neinit(unit);
+}
+/*
+ * Interface exists: make available by filling in network interface
+ * record.  System will initialize the interface when it is ready
+ * to accept packets.  We get the ethernet address here.
+ */
+neattach(dvp)
+       struct isa_device *dvp;
+{
+       int unit = dvp->id_unit;
+       register struct ne_softc *ns = &ne_softc[unit];
+       register struct ifnet *ifp = &ns->ns_if;
+
+       ifp->if_unit = unit;
+       ifp->if_name = nedriver.name ;
+       ifp->if_mtu = ETHERMTU;
+       printf (" ethernet address %s", ether_sprintf(ns->ns_addr)) ;
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
+       ifp->if_init = neinit;
+       ifp->if_output = ether_output;
+       ifp->if_start = nestart;
+       ifp->if_ioctl = neioctl;
+       ifp->if_reset = nereset;
+       ifp->if_watchdog = 0;
+       if_attach(ifp);
+}
+
+/*
+ * Initialization of interface; set up initialization block
+ * and transmit/receive descriptor rings.
+ */
+neinit(unit)
+       int unit;
+{
+       register struct ne_softc *ns = &ne_softc[unit];
+       struct ifnet *ifp = &ns->ns_if;
+       int s;
+       int i; char *cp;
+       register nec = ns->ns_port;
+
+       if (ifp->if_addrlist == (struct ifaddr *)0) return;
+       if (ifp->if_flags & IFF_RUNNING) return;
+
+       s = splimp();
+
+       /* set physical address on ethernet */
+       outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
+       for (i=0 ; i < 6 ; i++) outb(nec+ds1_par0+i,ns->ns_addr[i]);
+
+       /* clr logical address hash filter for now */
+       for (i=0 ; i < 8 ; i++) outb(nec+ds1_mar0+i,0xff);
+
+       /* init regs */
+       outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
+       outb (nec+ds0_rbcr0, 0);
+       outb (nec+ds0_rbcr1, 0);
+       outb (nec+ds0_imr, 0);
+       outb (nec+ds0_isr, 0xff);
+
+       /* Word Transfer select, Burst Mode Select, Fifo at 8 bytes */
+       outb(nec+ds0_dcr, ns->ns_mode);
+
+       outb(nec+ds0_tcr, 0);
+       outb (nec+ds0_rcr, DSRC_MON);
+       outb (nec+ds0_tpsr, 0);
+       outb(nec+ds0_pstart, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
+       outb(nec+ds0_pstop, ns->ns_rxend/DS_PGSIZE);
+       outb(nec+ds0_bnry, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
+       outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
+       outb(nec+ds1_curr, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
+       ns->ns_cur = (ns->ns_txstart+PKTSZ)/DS_PGSIZE;
+       outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
+       outb (nec+ds0_rcr, DSRC_AB);
+       outb(nec+ds0_dcr, ns->ns_mode);
+       outb (nec+ds0_imr, 0xff);
+
+       ns->ns_if.if_flags |= IFF_RUNNING;
+       ns->ns_flags &= ~DSF_LOCK;
+       ns->ns_oactive = 0; ns->ns_mask = ~0;
+       nestart(ifp);
+       splx(s);
+}
+
+/*
+ * Setup output on interface.
+ * Get another datagram to send off of the interface queue,
+ * and map it to the interface before starting the output.
+ * called only at splimp or interrupt level.
+ */
+nestart(ifp)
+       struct ifnet *ifp;
+{
+       register struct ne_softc *ns = &ne_softc[ifp->if_unit];
+       struct mbuf *m0, *m;
+       int buffer;
+       int len = 0, i, total,t;
+       register nec = ns->ns_port;
+
+       /*
+        * The DS8390 has only one transmit buffer, if it is busy we
+        * must wait until the transmit interrupt completes.
+        */
+       outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
+
+       if (ns->ns_flags & DSF_LOCK)
+               return;
+
+       if (inb(nec+ds_cmd) & DSCM_TRANS)
+               return;
+
+       if ((ns->ns_if.if_flags & IFF_RUNNING) == 0)
+               return;
+
+       IF_DEQUEUE(&ns->ns_if.if_snd, m);
+       if (m == 0)
+               return;
+
+       /*
+        * Copy the mbuf chain into the transmit buffer
+        */
+
+       ns->ns_flags |= DSF_LOCK;       /* prevent entering nestart */
+       buffer = ns->ns_txstart; len = i = 0;
+       t = 0;
+       for (m0 = m; m != 0; m = m->m_next)
+               t += m->m_len;
+               
+       m = m0;
+       total = t;
+       for (m0 = m; m != 0; ) {
+               
+               if (m->m_len&1 && t > m->m_len) {
+                       neput(ns, mtod(m, caddr_t), buffer, m->m_len - 1);
+                       t -= m->m_len - 1;
+                       buffer += m->m_len - 1;
+                       m->m_data += m->m_len - 1;
+                       m->m_len = 1;
+                       m = m_pullup(m, 2);
+               } else {
+                       neput(ns, mtod(m, caddr_t), buffer, m->m_len);
+                       buffer += m->m_len;
+                       t -= m->m_len;
+                       MFREE(m, m0);
+                       m = m0;
+               }
+       }
+
+       /*
+        * Init transmit length registers, and set transmit start flag.
+        */
+
+       len = total;
+       if (len < ETHER_MIN_LEN) len = ETHER_MIN_LEN;
+       outb(nec+ds0_tbcr0,len&0xff);
+       outb(nec+ds0_tbcr1,(len>>8)&0xff);
+       outb(nec+ds0_tpsr, ns->ns_txstart/DS_PGSIZE);
+       outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START);
+}
+
+/* buffer successor/predecessor in ring? */
+#define succ(n) (((n)+1 >= ns->ns_rxend/DS_PGSIZE) ? (ns->ns_txstart+PKTSZ)/DS_PGSIZE : (n)+1)
+#define pred(n) (((n)-1 < (ns->ns_txstart+PKTSZ)/DS_PGSIZE) ? ns->ns_rxend/DS_PGSIZE-1 : (n)-1)
+
+/*
+ * Controller interrupt.
+ */
+neintr(unit)
+{
+       register struct ne_softc *ns = &ne_softc[unit];
+       u_char cmd,isr;
+       register nec = ns->ns_port;
+
+       /* Save cmd, clear interrupt */
+       cmd = inb (nec+ds_cmd);
+loop:
+       isr = inb (nec+ds0_isr);
+       outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
+       outb(nec+ds0_isr, isr);
+
+       /* Receiver error */
+       if (isr & DSIS_RXE) {
+               /* need to read these registers to clear status */
+               (void) inb(nec+ ds0_rsr);
+               (void) inb(nec+ 0xD);
+               (void) inb(nec + 0xE);
+               (void) inb(nec + 0xF);
+               ns->ns_if.if_ierrors++;
+       }
+
+       /* We received something; rummage thru tiny ring buffer */
+       if (isr & (DSIS_RX|DSIS_RXE|DSIS_ROVRN)) {
+               u_char pend,lastfree;
+
+               outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
+               pend = inb(nec+ds1_curr);
+               outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
+               lastfree = inb(nec+ds0_bnry);
+
+               /* Have we wrapped? */
+               if (lastfree >= ns->ns_rxend/DS_PGSIZE)
+                       lastfree = (ns->ns_txstart+PKTSZ)/DS_PGSIZE;
+               if (pend < lastfree && ns->ns_cur < pend)
+                       lastfree = ns->ns_cur;
+               else    if (ns->ns_cur > lastfree)
+                       lastfree = ns->ns_cur;
+
+               /* Something in the buffer? */
+               while (pend != lastfree) {
+                       u_char nxt;
+
+                       /* Extract header from microcephalic board */
+                       nefetch(ns, &ns->ns_ph,lastfree*DS_PGSIZE,
+                               sizeof(ns->ns_ph));
+                       ns->ns_ba = lastfree*DS_PGSIZE+sizeof(ns->ns_ph);
+
+                       /* Incipient paranoia */
+                       if (ns->ns_ph.pr_status == DSRS_RPC ||
+                               /* for dequna's */
+                               ns->ns_ph.pr_status == 0x21)
+                               nerecv (ns);
+#ifdef NEDEBUG
+                       else  {
+                               printf("cur %x pnd %x lfr %x ",
+                                       ns->ns_cur, pend, lastfree);
+                               printf("nxt %x len %x ", ns->ns_ph.pr_nxtpg,
+                                       (ns->ns_ph.pr_sz1<<8)+ ns->ns_ph.pr_sz0);
+                               printf("Bogus Sts %x\n", ns->ns_ph.pr_status);  
+                       }
+#endif
+
+                       nxt = ns->ns_ph.pr_nxtpg ;
+
+                       /* Sanity check */
+                       if ( nxt >= (ns->ns_txstart+PKTSZ)/DS_PGSIZE
+                               && nxt <= ns->ns_rxend/DS_PGSIZE && nxt <= pend)
+                               ns->ns_cur = nxt;
+                       else    ns->ns_cur = nxt = pend;
+
+                       /* Set the boundaries */
+                       lastfree = nxt;
+                       outb(nec+ds0_bnry, pred(nxt));
+                       outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
+                       pend = inb(nec+ds1_curr);
+                       outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
+               }
+               outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
+       }
+
+       /* Transmit error */
+       if (isr & DSIS_TXE) {
+               ns->ns_flags &= ~DSF_LOCK;
+               /* Need to read these registers to clear status */
+               ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
+               ns->ns_if.if_oerrors++;
+       }
+
+       /* Packet Transmitted */
+       if (isr & DSIS_TX) {
+               ns->ns_flags &= ~DSF_LOCK;
+               ++ns->ns_if.if_opackets;
+               ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
+       }
+
+       /* Receiver ovverun? */
+       if (isr & DSIS_ROVRN) {
+               log(LOG_ERR, "ne%d: error: isr %x\n", ns-ne_softc, isr
+                       /*, DSIS_BITS*/);
+               outb(nec+ds0_rbcr0, 0);
+               outb(nec+ds0_rbcr1, 0);
+               outb(nec+ds0_tcr, DSTC_LB0);
+               outb(nec+ds0_rcr, DSRC_MON);
+               outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
+               outb(nec+ds0_rcr, DSRC_AB);
+               outb(nec+ds0_tcr, 0);
+       }
+
+       /* Any more to send? */
+       outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
+       nestart(&ns->ns_if);
+       outb (nec+ds_cmd, cmd);
+       outb (nec+ds0_imr, 0xff);
+
+       /* Still more to do? */
+       isr = inb (nec+ds0_isr);
+       if(isr) goto loop;
+}
+
+/*
+ * Ethernet interface receiver interface.
+ * If input error just drop packet.
+ * Otherwise examine packet to determine type.  If can't determine length
+ * from type, then have to drop packet.  Othewise decapsulate
+ * packet based on type and pass to type specific higher-level
+ * input routine.
+ */
+nerecv(ns)
+       register struct ne_softc *ns;
+{
+       int len,i;
+
+       ns->ns_if.if_ipackets++;
+       len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1<<8);
+       if(len < ETHER_MIN_LEN || len > ETHER_MAX_LEN)
+               return;
+
+       /* this need not be so torturous - one/two bcopys at most into mbufs */
+       nefetch(ns, ns->ns_pb, ns->ns_ba, min(len,DS_PGSIZE-sizeof(ns->ns_ph)));
+       if (len > DS_PGSIZE-sizeof(ns->ns_ph)) {
+               int l = len - (DS_PGSIZE-sizeof(ns->ns_ph)), b ;
+               u_char *p = ns->ns_pb + (DS_PGSIZE-sizeof(ns->ns_ph));
+
+               if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46;
+               b = ns->ns_cur*DS_PGSIZE;
+               
+               while (l >= DS_PGSIZE) {
+                       nefetch(ns, p, b, DS_PGSIZE);
+                       p += DS_PGSIZE; l -= DS_PGSIZE;
+                       if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46;
+                       b = ns->ns_cur*DS_PGSIZE;
+               }
+               if (l > 0)
+                       nefetch(ns, p, b, l);
+       }
+       /* don't forget checksum! */
+       len -= sizeof(struct ether_header) + sizeof(long);
+                       
+       neread(ns,(caddr_t)(ns->ns_pb), len);
+}
+
+/*
+ * Pass a packet to the higher levels.
+ * We deal with the trailer protocol here.
+ */
+neread(ns, buf, len)
+       register struct ne_softc *ns;
+       char *buf;
+       int len;
+{
+       register struct ether_header *eh;
+       struct mbuf *m;
+       int off, resid;
+       register struct ifqueue *inq;
+
+       /*
+        * Deal with trailer protocol: if type is trailer type
+        * get true type from first 16-bit word past data.
+        * Remember that type was trailer by setting off.
+        */
+       eh = (struct ether_header *)buf;
+       eh->ether_type = ntohs((u_short)eh->ether_type);
+#define        nedataaddr(eh, off, type)       ((type)(((caddr_t)((eh)+1)+(off))))
+       if (eh->ether_type >= ETHERTYPE_TRAIL &&
+           eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+               off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
+               if (off >= ETHERMTU) return;            /* sanity */
+               eh->ether_type = ntohs(*nedataaddr(eh, off, u_short *));
+               resid = ntohs(*(nedataaddr(eh, off+2, u_short *)));
+               if (off + resid > len) return;          /* sanity */
+               len = off + resid;
+       } else  off = 0;
+
+       if (len == 0) return;
+
+       /*
+        * Pull packet off interface.  Off is nonzero if packet
+        * has trailing header; neget will then force this header
+        * information to be at the front, but we still have to drop
+        * the type and length which are at the front of any trailer data.
+        */
+       m = neget(buf, len, off, &ns->ns_if);
+       if (m == 0) return;
+
+       ether_input(&ns->ns_if, eh, m);
+}
+
+/*
+ * Supporting routines
+ */
+
+/*
+ * Pull read data off a interface.
+ * Len is length of data, with local net header stripped.
+ * Off is non-zero if a trailer protocol was used, and
+ * gives the offset of the trailer information.
+ * We copy the trailer information and then all the normal
+ * data into mbufs.  When full cluster sized units are present
+ * we copy into clusters.
+ */
+struct mbuf *
+neget(buf, totlen, off0, ifp)
+       caddr_t buf;
+       int totlen, off0;
+       struct ifnet *ifp;
+{
+       struct mbuf *top, **mp, *m, *p;
+       int off = off0, len;
+       register caddr_t cp = buf;
+       char *epkt;
+
+       buf += sizeof(struct ether_header);
+       cp = buf;
+       epkt = cp + totlen;
+
+
+       if (off) {
+               cp += off + 2 * sizeof(u_short);
+               totlen -= 2 * sizeof(u_short);
+       }
+
+       MGETHDR(m, M_DONTWAIT, MT_DATA);
+       if (m == 0)
+               return (0);
+       m->m_pkthdr.rcvif = ifp;
+       m->m_pkthdr.len = totlen;
+       m->m_len = MHLEN;
+
+       top = 0;
+       mp = &top;
+       while (totlen > 0) {
+               if (top) {
+                       MGET(m, M_DONTWAIT, MT_DATA);
+                       if (m == 0) {
+                               m_freem(top);
+                               return (0);
+                       }
+                       m->m_len = MLEN;
+               }
+               len = min(totlen, epkt - cp);
+               if (len >= MINCLSIZE) {
+                       MCLGET(m, M_DONTWAIT);
+                       if (m->m_flags & M_EXT)
+                               m->m_len = len = min(len, MCLBYTES);
+                       else
+                               len = m->m_len;
+               } else {
+                       /*
+                        * Place initial small packet/header at end of mbuf.
+                        */
+                       if (len < m->m_len) {
+                               if (top == 0 && len + max_linkhdr <= m->m_len)
+                                       m->m_data += max_linkhdr;
+                               m->m_len = len;
+                       } else
+                               len = m->m_len;
+               }
+               bcopy(cp, mtod(m, caddr_t), (unsigned)len);
+               cp += len;
+               *mp = m;
+               mp = &m->m_next;
+               totlen -= len;
+               if (cp == epkt)
+                       cp = buf;
+       }
+       return (top);
+}
+
+/*
+ * Process an ioctl request.
+ */
+neioctl(ifp, cmd, data)
+       register struct ifnet *ifp;
+       int cmd;
+       caddr_t data;
+{
+       register struct ifaddr *ifa = (struct ifaddr *)data;
+       struct ne_softc *ns = &ne_softc[ifp->if_unit];
+       struct ifreq *ifr = (struct ifreq *)data;
+       int s = splimp(), error = 0;
+
+
+       switch (cmd) {
+
+       case SIOCSIFADDR:
+               ifp->if_flags |= IFF_UP;
+
+               switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+               case AF_INET:
+                       neinit(ifp->if_unit);   /* before arpwhohas */
+                       ((struct arpcom *)ifp)->ac_ipaddr =
+                               IA_SIN(ifa)->sin_addr;
+                       arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+                       break;
+#endif
+#ifdef NS
+               case AF_NS:
+                   {
+                       register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+                       if (ns_nullhost(*ina))
+                               ina->x_host = *(union ns_host *)(ns->ns_addr);
+                       else {
+                               /* 
+                                * The manual says we can't change the address 
+                                * while the receiver is armed,
+                                * so reset everything
+                                */
+                               ifp->if_flags &= ~IFF_RUNNING; 
+                               bcopy((caddr_t)ina->x_host.c_host,
+                                   (caddr_t)ns->ns_addr, sizeof(ns->ns_addr));
+                       }
+                       neinit(ifp->if_unit); /* does ne_setaddr() */
+                       break;
+                   }
+#endif
+               default:
+                       neinit(ifp->if_unit);
+                       break;
+               }
+               break;
+
+       case SIOCSIFFLAGS:
+               if ((ifp->if_flags & IFF_UP) == 0 &&
+                   ifp->if_flags & IFF_RUNNING) {
+                       ifp->if_flags &= ~IFF_RUNNING;
+                       outb(ns->ns_port + ds_cmd, DSCM_STOP|DSCM_NODMA);
+               } else if (ifp->if_flags & IFF_UP &&
+                   (ifp->if_flags & IFF_RUNNING) == 0)
+                       neinit(ifp->if_unit);
+               break;
+
+#ifdef notdef
+       case SIOCGHWADDR:
+               bcopy((caddr_t)ns->ns_addr, (caddr_t) &ifr->ifr_data,
+                       sizeof(ns->ns_addr));
+               break;
+#endif
+
+       default:
+               error = EINVAL;
+       }
+       splx(s);
+       return (error);
+}
+#endif