386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Mon, 13 Jul 1992 10:33:03 +0000 (02:33 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Mon, 13 Jul 1992 10:33:03 +0000 (02:33 -0800)
Work on file usr/src/sys.386bsd/i386/isa/if_we.c

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

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

diff --git a/usr/src/sys.386bsd/i386/isa/if_we.c b/usr/src/sys.386bsd/i386/isa/if_we.c
new file mode 100644 (file)
index 0000000..9f47ae0
--- /dev/null
@@ -0,0 +1,808 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tim L. Tucker.
+ *
+ * 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_we.c     7.3 (Berkeley) 5/21/91
+ */
+
+/*
+ * Modification history
+ *
+ * 8/28/89 - Initial version(if_wd.c), Tim L Tucker
+ */
+#include "we.h"
+#if    NWE > 0
+/*
+ * Western Digital 8003 ethernet/starlan adapter
+ *
+ * Supports the following interface cards:
+ * WD8003E, WD8003EBT, WD8003S, WD8003SBT, WD8013EBT
+ *
+ * The Western Digital card is one of many AT/MCA ethernet interfaces
+ * based on the National DS8390 Network Interface chip set.
+ */
+#include "param.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "ioctl.h"
+#include "errno.h"
+#include "syslog.h"
+
+#include "net/if.h"
+#include "net/netisr.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.h"
+#include "i386/isa/if_wereg.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+/*
+ * This constant should really be 60 because the we adds 4 bytes of crc.
+ * However when set to 60 our packets are ignored by deunas , 3coms are
+ * okay ??????????????????????????????????????????
+ */
+#define ETHER_MIN_LEN 64
+#define        ETHER_ADDR_LEN 6
+#define ETHER_HDR_SIZE 14
+/*
+ * Ethernet software status per interface.
+ *
+ * Each interface is referenced by a network interface structure,
+ * qe_if, which the routing code uses to locate the interface.
+ * This structure contains the output queue for the interface, its address, ...
+ */
+struct we_softc {
+       struct  arpcom we_ac;           /* Ethernet common part         */
+#define        we_if   we_ac.ac_if             /* network-visible interface    */
+#define        we_addr we_ac.ac_enaddr         /* hardware Ethernet address    */
+
+       u_char  we_flags;               /* software state               */
+#define        WDF_RUNNING     0x01
+#define WDF_TXBUSY     0x02
+
+       u_char  we_type;                /* interface type code          */
+       u_short we_vector;              /* interrupt vector             */
+       short   we_io_ctl_addr;         /* i/o bus address, control     */
+       short   we_io_nic_addr;         /* i/o bus address, DS8390      */
+
+       caddr_t we_vmem_addr;           /* card RAM virtual memory base */
+       u_long  we_vmem_size;           /* card RAM bytes               */
+       caddr_t we_vmem_ring;           /* receive ring RAM vaddress    */
+       caddr_t we_vmem_end;            /* receive ring RAM end */
+} we_softc[NWE];
+
+int    weprobe(), weattach(), weintr(), westart();
+int    weinit(), ether_output(), weioctl(), wereset(), wewatchdog();
+
+struct isa_driver wedriver = {
+       weprobe, weattach, "we",
+};
+static unsigned short wemask[] =
+       { IRQ9, IRQ3, IRQ5, IRQ7, IRQ10, IRQ11, IRQ15, IRQ4 };
+       
+/*
+ * Probe the WD8003 to see if its there
+ */
+weprobe(is)
+       struct isa_device *is;
+{
+       register int i;
+       register struct we_softc *sc = &we_softc[is->id_unit];
+       union we_mem_sel wem;
+       u_char sum;
+       /* reset card to force it into a known state. */
+       outb(is->id_iobase, 0x80);
+       DELAY(100);
+       outb(is->id_iobase, 0x00);
+       /* wait in the case this card is reading it's EEROM */
+       DELAY(5000);
+
+       /*
+        * Here we check the card ROM, if the checksum passes, and the
+        * type code and ethernet address check out, then we know we have
+        * a wd8003 card.
+        *
+        * Autoconfiguration: No warning message is printed on error.
+        */
+       for (sum = 0, i = 0; i < 8; ++i)
+           sum += inb(is->id_iobase + WD_ROM_OFFSET + i);
+       if (sum != WD_CHECKSUM)
+            return (0);
+       sc->we_type = inb(is->id_iobase + WD_ROM_OFFSET + 6);
+#ifdef nope
+       if ((sc->we_type & WD_REVMASK) != 2             /* WD8003E or WD8003S */
+               && (sc->we_type & WD_REVMASK) != 4      /* WD8003EBT */
+               && (sc->we_type & WD_REVMASK) != 6)        /* WD8003ELB? */
+            return (0);
+#endif
+/*printf("type %x ", sc->we_type);*/
+       if (sc->we_type & WD_SOFTCONFIG) {
+               int iv = inb(is->id_iobase + 1) & 4 |
+                       ((inb(is->id_iobase+4) & 0x60) >> 5);
+/*printf("iv %d ", iv);*/
+               if (wemask[iv] != is->id_irq)
+                       return(0);
+               outb(is->id_iobase+4, inb(is->id_iobase+4) | 0x80);
+       }
+
+       /*
+        * Setup card RAM area and i/o addresses
+        * Kernel Virtual to segment C0000-DFFFF?????
+        */
+       sc->we_io_ctl_addr = is->id_iobase;
+       sc->we_io_nic_addr = sc->we_io_ctl_addr + WD_NIC_OFFSET;
+       sc->we_vector = is->id_irq;
+       sc->we_vmem_addr = (caddr_t)is->id_maddr;
+       sc->we_vmem_size = is->id_msize;
+       sc->we_vmem_ring = sc->we_vmem_addr + (WD_PAGE_SIZE * WD_TXBUF_SIZE);
+       sc->we_vmem_end = sc->we_vmem_addr + is->id_msize;
+
+       /*
+        * Save board ROM station address
+        */
+       for (i = 0; i < ETHER_ADDR_LEN; ++i)
+           sc->we_addr[i] = inb(sc->we_io_ctl_addr + WD_ROM_OFFSET + i);
+
+       /*
+        * Mapin interface memory, setup memory select register
+        */
+        wem.ms_addr = kvtop(sc->we_vmem_addr) >> 13;
+       wem.ms_enable = 1;
+       wem.ms_reset = 0;
+       outb(sc->we_io_ctl_addr, wem.ms_byte);
+
+       /*
+        * clear interface memory, then sum to make sure its valid
+        */
+       for (i = 0; i < sc->we_vmem_size; ++i)
+           sc->we_vmem_addr[i] = 0x0;
+       for (sum = 0, i = 0; i < sc->we_vmem_size; ++i)
+           sum += sc->we_vmem_addr[i];
+       if (sum != 0x0) {
+            printf("we%d: wd8003 dual port RAM address error\n", is->id_unit);
+           return (0);
+       }
+
+       return (WD_IO_PORTS);
+}
+/*
+ * Interface exists: make available by filling in network interface
+ * record.  System will initialize the interface when it is ready
+ * to accept packets.
+ */
+weattach(is)
+       struct isa_device *is;
+{
+       register struct we_softc *sc = &we_softc[is->id_unit];
+       register struct ifnet *ifp = &sc->we_if;
+       union we_command wecmd;
+       wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
+       wecmd.cs_stp = 1;
+       wecmd.cs_sta = 0;
+       wecmd.cs_ps = 0;
+       outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+       /*
+        * Initialize ifnet structure
+        */
+       ifp->if_unit = is->id_unit;
+       ifp->if_name = "we" ;
+       ifp->if_mtu = ETHERMTU;
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS ;
+       ifp->if_init = weinit;
+       ifp->if_output = ether_output;
+       ifp->if_start = westart;
+       ifp->if_ioctl = weioctl;
+       ifp->if_reset = wereset;
+       ifp->if_watchdog = wewatchdog;
+       if_attach(ifp);
+       /*
+        * Banner...
+        */
+       printf(" %s address %s",
+               (sc->we_type & WD_ETHERNET) ? "ethernet" : "starlan",
+               ether_sprintf(sc->we_addr));
+}
+/*
+ * Reset of interface.
+ */
+wereset(unit, uban)
+       int unit, uban;
+{
+       if (unit >= NWE)
+               return;
+       printf("we%d: reset\n", unit);
+/*     we_softc[unit].we_flags &= ~WDF_RUNNING; */
+       weinit(unit);
+}
+/*
+ * Take interface offline.
+ */
+westop(unit)
+       int unit;
+{
+       register struct we_softc *sc = &we_softc[unit];
+       union we_command wecmd;
+       int s;
+       /*
+        * Shutdown DS8390
+        */
+       s = splimp();
+       wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
+       wecmd.cs_stp = 1;
+       wecmd.cs_sta = 0;
+       wecmd.cs_ps = 0;
+       outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+       (void) splx(s);
+}
+
+wewatchdog(unit) {
+
+       log(LOG_WARNING,"we%d: soft reset\n", unit);
+       westop(unit);
+       weinit(unit);
+}
+
+static Bdry;
+/*
+ * Initialization of interface (really just DS8390). 
+ */
+weinit(unit)
+       int unit;
+{
+       register struct we_softc *sc = &we_softc[unit];
+       register struct ifnet *ifp = &sc->we_if;
+       union we_command wecmd;
+       int i, s;
+       /* address not known */
+       if (ifp->if_addrlist == (struct ifaddr *)0)
+               return;
+
+       /* already running */
+       /*if (ifp->if_flags & IFF_RUNNING) return; */
+
+       /*
+        * Initialize DS8390 in order given in NSC NIC manual.
+        * this is stock code...please see the National manual for details.
+        */
+       s = splhigh();
+Bdry = 0;
+       wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
+       wecmd.cs_stp = 1;
+       wecmd.cs_sta = 0;
+       wecmd.cs_ps = 0;
+       outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+       outb(sc->we_io_nic_addr + WD_P0_DCR, WD_D_CONFIG);
+       outb(sc->we_io_nic_addr + WD_P0_RBCR0, 0);
+       outb(sc->we_io_nic_addr + WD_P0_RBCR1, 0);
+       outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_MON);
+       outb(sc->we_io_nic_addr + WD_P0_TCR, WD_T_CONFIG);
+       outb(sc->we_io_nic_addr + WD_P0_TPSR, 0);
+       outb(sc->we_io_nic_addr + WD_P0_PSTART, WD_TXBUF_SIZE);
+       outb(sc->we_io_nic_addr + WD_P0_PSTOP,
+               sc->we_vmem_size / WD_PAGE_SIZE);
+       outb(sc->we_io_nic_addr + WD_P0_BNRY, WD_TXBUF_SIZE);
+       outb(sc->we_io_nic_addr + WD_P0_ISR, 0xff);
+       outb(sc->we_io_nic_addr + WD_P0_IMR, WD_I_CONFIG);
+       wecmd.cs_ps = 1;
+       outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+       for (i = 0; i < ETHER_ADDR_LEN; ++i)
+           outb(sc->we_io_nic_addr + WD_P1_PAR0 + i, sc->we_addr[i]);
+       for (i = 0; i < ETHER_ADDR_LEN; ++i)    /* == broadcast addr */
+           outb(sc->we_io_nic_addr + WD_P1_MAR0 + i, 0xff);
+       outb(sc->we_io_nic_addr + WD_P1_CURR, WD_TXBUF_SIZE);
+       wecmd.cs_ps = 0;
+       wecmd.cs_stp = 0;
+       wecmd.cs_sta = 1;
+       wecmd.cs_rd = 0x4;
+       outb(sc->we_io_nic_addr + WD_P1_COMMAND, wecmd.cs_byte);
+       outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_CONFIG);
+
+       /*
+        * Take the interface out of reset, program the vector, 
+        * enable interrupts, and tell the world we are up.
+        */
+       ifp->if_flags |= IFF_RUNNING;
+       sc->we_flags &= ~WDF_TXBUSY;
+       (void) splx(s);
+       westart(ifp);
+}
+/*
+ * Start output on interface.
+ */
+westart(ifp)
+       struct ifnet *ifp;
+{
+       register struct we_softc *sc = &we_softc[ifp->if_unit];
+       struct mbuf *m0, *m;
+       register caddr_t buffer;
+       int len, s;
+       union we_command wecmd;
+       /*
+        * The DS8390 has only one transmit buffer, if it is busy we
+        * must wait until the transmit interrupt completes.
+        */
+       s = splhigh();
+       if (sc->we_flags & WDF_TXBUSY) {
+               (void) splx(s);
+               return;
+       }
+       IF_DEQUEUE(&sc->we_if.if_snd, m);
+       if (m == 0) {
+               (void) splx(s);
+               return;
+       }
+       sc->we_flags |= WDF_TXBUSY; 
+       (void) splx(s);
+
+       /*
+        * Copy the mbuf chain into the transmit buffer
+        */
+       buffer = sc->we_vmem_addr;
+       len = 0;
+       for (m0 = m; m != 0; m = m->m_next) {
+               bcopy(mtod(m, caddr_t), buffer, m->m_len);
+               buffer += m->m_len;
+               len += m->m_len;
+       }
+
+       m_freem(m0);
+
+       /*
+        * Init transmit length registers, and set transmit start flag.
+        */
+       s = splhigh();
+       len = MAX(len, ETHER_MIN_LEN);
+       wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
+       wecmd.cs_ps = 0;
+       outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+       outb(sc->we_io_nic_addr + WD_P0_TBCR0, len & 0xff);
+       outb(sc->we_io_nic_addr + WD_P0_TBCR1, len >> 8);
+       wecmd.cs_txp = 1;
+       outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+       (void) splx(s);
+}
+/*
+ * Ethernet interface interrupt processor
+ */
+weintr(unit)
+       int unit;
+{
+       register struct we_softc *sc = &we_softc[unit];
+       union we_command wecmd;
+       union we_interrupt weisr;
+
+       unit =0;
+       /* disable onboard interrupts, then get interrupt status */
+       wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
+       wecmd.cs_ps = 0;
+       outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+       weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
+loop:
+       outb(sc->we_io_nic_addr + WD_P0_ISR, weisr.is_byte);
+
+       /* transmit error */
+       if (weisr.is_txe) {
+               /* need to read these registers to clear status */
+               sc->we_if.if_collisions +=
+                   inb(sc->we_io_nic_addr + WD_P0_TBCR0);
+               ++sc->we_if.if_oerrors;
+       }
+
+       /* receiver error */
+       if (weisr.is_rxe) {
+               /* need to read these registers to clear status */
+               (void) inb(sc->we_io_nic_addr + 0xD);
+               (void) inb(sc->we_io_nic_addr + 0xE);
+               (void) inb(sc->we_io_nic_addr + 0xF);
+               ++sc->we_if.if_ierrors;
+       }
+
+       /* normal transmit complete */
+       if (weisr.is_ptx || weisr.is_txe)
+               wetint (unit);
+
+       /* normal receive notification */
+       if (weisr.is_prx || weisr.is_rxe)
+               werint (unit);
+
+       /* try to start transmit */
+       westart(&sc->we_if);
+
+       /* re-enable onboard interrupts */
+       wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
+       wecmd.cs_ps = 0;
+       outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+       outb(sc->we_io_nic_addr + WD_P0_IMR, 0xff/*WD_I_CONFIG*/);
+       weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
+       if (weisr.is_byte) goto loop;
+}
+/*
+ * Ethernet interface transmit interrupt.
+ */
+wetint(unit)
+       int unit;
+{
+       register struct we_softc *sc = &we_softc[unit];
+
+       /*
+        * Do some statistics (assume page zero of NIC mapped in)
+        */
+       sc->we_flags &= ~WDF_TXBUSY; 
+       sc->we_if.if_timer = 0;
+       ++sc->we_if.if_opackets;
+       sc->we_if.if_collisions += inb(sc->we_io_nic_addr + WD_P0_TBCR0);
+}
+/*
+ * Ethernet interface receiver interrupt.
+ */
+werint(unit)
+       int unit;
+{
+       register struct we_softc *sc = &we_softc[unit];
+       u_char bnry, curr;
+       long len;
+       union we_command wecmd;
+       struct we_ring *wer;
+       /*
+        * Traverse the receive ring looking for packets to pass back.
+        * The search is complete when we find a descriptor not in use.
+        */
+       wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
+       wecmd.cs_ps = 0;
+       outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+       bnry = inb(sc->we_io_nic_addr + WD_P0_BNRY);
+       wecmd.cs_ps = 1;
+       outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+       curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
+if(Bdry)
+       bnry =Bdry;
+
+       while (bnry != curr)
+       {
+               /* get pointer to this buffer header structure */
+               wer = (struct we_ring *)(sc->we_vmem_addr + (bnry << 8));
+
+               /* count includes CRC */
+               len = wer->we_count - 4;
+               if (len > 30 && len <= ETHERMTU+100
+                       /*&& (*(char *)wer  == 1 || *(char *) wer == 0x21)*/)
+                       weread(sc, (caddr_t)(wer + 1), len);
+               else printf("reject %d", len);
+
+outofbufs:
+               wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
+               wecmd.cs_ps = 0;
+               outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+
+               /* advance on chip Boundry register */
+               if((caddr_t) wer + WD_PAGE_SIZE - 1 > sc->we_vmem_end) {
+                       bnry = WD_TXBUF_SIZE;
+                       outb(sc->we_io_nic_addr + WD_P0_BNRY,
+                                       sc->we_vmem_size / WD_PAGE_SIZE-1);
+       
+               } else {
+                       if (len > 30 && len <= ETHERMTU+100)
+                               bnry = wer->we_next_packet;
+                       else bnry = curr;
+
+                       /* watch out for NIC overflow, reset Boundry if invalid */
+                       if ((bnry - 1) < WD_TXBUF_SIZE) {
+                               outb(sc->we_io_nic_addr + WD_P0_BNRY,
+                                       (sc->we_vmem_size / WD_PAGE_SIZE) - 1);
+                               bnry = WD_TXBUF_SIZE;
+                       } else
+                               outb(sc->we_io_nic_addr + WD_P0_BNRY, bnry-1);
+               }
+
+               /* refresh our copy of CURR */
+               wecmd.cs_ps = 1;
+               outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
+               curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
+       }
+Bdry = bnry;
+}
+
+/*
+ * Process an ioctl request.
+ */
+weioctl(ifp, cmd, data)
+       register struct ifnet *ifp;
+       int cmd;
+       caddr_t data;
+{
+       register struct ifaddr *ifa = (struct ifaddr *)data;
+       struct we_softc *sc = &we_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:
+                       weinit(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 *)(sc->ns_addr);
+                       else {
+                               /* 
+                                * The manual says we cant 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)sc->ns_addr, sizeof(sc->ns_addr));
+                       }
+                       weinit(ifp->if_unit); /* does ne_setaddr() */
+                       break;
+                   }
+#endif
+               default:
+                       weinit(ifp->if_unit);
+                       break;
+               }
+               break;
+
+       case SIOCSIFFLAGS:
+               if ((ifp->if_flags & IFF_UP) == 0 &&
+                   ifp->if_flags & IFF_RUNNING) {
+                       ifp->if_flags &= ~IFF_RUNNING;
+                       westop(ifp->if_unit);
+               } else if (ifp->if_flags & IFF_UP &&
+                   (ifp->if_flags & IFF_RUNNING) == 0)
+                       weinit(ifp->if_unit);
+               break;
+
+#ifdef notdef
+       case SIOCGHWADDR:
+               bcopy((caddr_t)sc->sc_addr, (caddr_t) &ifr->ifr_data,
+                       sizeof(sc->sc_addr));
+               break;
+#endif
+
+       default:
+               error = EINVAL;
+       }
+       splx(s);
+       return (error);
+}
+/*
+ * set ethernet address for unit
+ */
+wesetaddr(physaddr, unit)
+       u_char *physaddr;
+       int unit;
+{
+       register struct we_softc *sc = &we_softc[unit];
+       register int i;
+
+       /*
+        * Rewrite ethernet address, and then force restart of NIC
+        */
+       for (i = 0; i < ETHER_ADDR_LEN; i++)
+               sc->we_addr[i] = physaddr[i];
+       sc->we_flags &= ~WDF_RUNNING;
+       weinit(unit);
+}
+#define        wedataaddr(sc, eh, off, type) \
+       ((type) ((caddr_t)((eh)+1)+(off) >= (sc)->we_vmem_end) ? \
+               (((caddr_t)((eh)+1)+(off))) - (sc)->we_vmem_end \
+               + (sc)->we_vmem_ring: \
+               ((caddr_t)((eh)+1)+(off)))
+/*
+ * Pass a packet to the higher levels.
+ * We deal with the trailer protocol here.
+ */
+weread(sc, buf, len)
+       register struct we_softc *sc;
+       char *buf;
+       int len;
+{
+       register struct ether_header *eh;
+       struct mbuf *m, *weget();
+       int off, resid;
+
+       /*
+        * 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);
+       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(*wedataaddr(sc, eh, off, u_short *));
+               resid = ntohs(*(wedataaddr(sc, eh, off+2, u_short *)));
+               if (off + resid > len) return;          /* sanity */
+               len = off + resid;
+       } else  off = 0;
+
+       len -= sizeof(struct ether_header);
+       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 = weget(buf, len, off, &sc->we_if, sc);
+       if (m == 0) return;
+       ether_input(&sc->we_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 *
+weget(buf, totlen, off0, ifp, sc)
+       caddr_t buf;
+       int totlen, off0;
+       struct ifnet *ifp;
+       struct we_softc *sc;
+{
+       struct mbuf *top, **mp, *m, *p;
+       int off = off0, len;
+       register caddr_t cp = buf;
+       char *epkt;
+       int tc =totlen;
+
+       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;
+               }
+
+               totlen -= len;
+               /* only do up to end of buffer */
+               if (cp+len > sc->we_vmem_end) {
+                       unsigned toend = sc->we_vmem_end - cp;
+
+                       bcopy(cp, mtod(m, caddr_t), toend);
+                       cp = sc->we_vmem_ring;
+                       bcopy(cp, mtod(m, caddr_t)+toend, len - toend);
+                       cp += len - toend;
+                       epkt = cp + totlen;
+               } else {
+                       bcopy(cp, mtod(m, caddr_t), (unsigned)len);
+                       cp += len;
+               }
+               *mp = m;
+               mp = &m->m_next;
+               if (cp == epkt) {
+                       cp = buf;
+                       epkt = cp + tc;
+               }
+       }
+       return (top);
+}
+#endif