BSD 4_4_Lite1 release
[unix-history] / usr / src / sys / pmax / dev / if_le.c
index 7e99316..8c3d3bd 100644 (file)
@@ -1,19 +1,45 @@
-/*
- * Copyright (c) 1992 Regents of the University of California.
- * All rights reserved.
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  *
  * This code is derived from software contributed to Berkeley by
- * Ralph Campbell.
+ * Ralph Campbell and Rick Macklem.
+ *
+ * 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.
  *
  *
- * %sccs.include.redist.c%
+ * 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_le.c     7.7 (Berkeley) %G%
+ *     @(#)if_le.c     8.2 (Berkeley) 11/16/93
  */
 
  */
 
-#include "le.h"
+#include <le.h>
 #if NLE > 0
 
 #if NLE > 0
 
-#include "bpfilter.h"
+#include <bpfilter.h>
 
 /*
  * AMD 7990 LANCE
 
 /*
  * AMD 7990 LANCE
@@ -24,6 +50,7 @@
  * This reasoning is dubious.
  */
 #include <sys/param.h>
  * This reasoning is dubious.
  */
 #include <sys/param.h>
+#include <sys/proc.h>
 #include <sys/systm.h>
 #include <sys/mbuf.h>
 #include <sys/buf.h>
 #include <sys/systm.h>
 #include <sys/mbuf.h>
 #include <sys/buf.h>
 #include <netns/ns_if.h>
 #endif
 
 #include <netns/ns_if.h>
 #endif
 
-#ifdef RMP
-#include <netrmp/rmp.h>
-#include <netrmp/rmp_var.h>
+#if defined (CCITT) && defined (LLC)
+#include <sys/socketvar.h>
+#include <netccitt/x25.h>
+extern llc_ctlinput(), cons_rtrequest();
 #endif
 
 #include <machine/machConst.h>
 #endif
 
 #include <machine/machConst.h>
+
+#include <pmax/pmax/pmaxtype.h>
+#include <pmax/pmax/kn01.h>
+#include <pmax/pmax/kmin.h>
+#include <pmax/pmax/asic.h>
+
 #include <pmax/dev/device.h>
 #include <pmax/dev/if_lereg.h>
 
 #include <pmax/dev/device.h>
 #include <pmax/dev/if_lereg.h>
 
@@ -84,40 +118,34 @@ struct     le_softc {
 #define        sc_if   sc_ac.ac_if     /* network-visible interface */
 #define        sc_addr sc_ac.ac_enaddr /* hardware Ethernet address */
        volatile struct lereg1 *sc_r1;  /* LANCE registers */
 #define        sc_if   sc_ac.ac_if     /* network-visible interface */
 #define        sc_addr sc_ac.ac_enaddr /* hardware Ethernet address */
        volatile struct lereg1 *sc_r1;  /* LANCE registers */
-       volatile struct lereg2 *sc_r2;  /* dual-port RAM */
+       volatile void *sc_r2;   /* dual-port RAM */
+       int     sc_ler2pad;     /* Do ring descriptors require short pads? */
+       void    (*sc_copytobuf)(); /* Copy to buffer */
+       void    (*sc_copyfrombuf)(); /* Copy from buffer */
+       void    (*sc_zerobuf)(); /* and Zero bytes in buffer */
        int     sc_rmd;         /* predicted next rmd to process */
        int     sc_tmd;         /* last tmd processed */
        int     sc_tmdnext;     /* next tmd to transmit with */
        int     sc_rmd;         /* predicted next rmd to process */
        int     sc_tmd;         /* last tmd processed */
        int     sc_tmdnext;     /* next tmd to transmit with */
+       /* stats */
        int     sc_runt;
        int     sc_runt;
-       int     sc_jab;
        int     sc_merr;
        int     sc_babl;
        int     sc_cerr;
        int     sc_miss;
        int     sc_merr;
        int     sc_babl;
        int     sc_cerr;
        int     sc_miss;
+       int     sc_rown;
        int     sc_xint;
        int     sc_xint;
-       int     sc_xown;
        int     sc_uflo;
        int     sc_rxlen;
        int     sc_rxoff;
        int     sc_txoff;
        int     sc_busy;
        short   sc_iflags;
        int     sc_uflo;
        int     sc_rxlen;
        int     sc_rxoff;
        int     sc_txoff;
        int     sc_busy;
        short   sc_iflags;
-#if NBPFILTER > 0
-       caddr_t sc_bpf;
-#endif
 } le_softc[NLE];
 
 } le_softc[NLE];
 
-#ifdef DS3100
 /* access LANCE registers */
 /* access LANCE registers */
+static void lewritereg();
 #define        LERDWR(cntl, src, dst)  { (dst) = (src); DELAY(10); }
 #define        LERDWR(cntl, src, dst)  { (dst) = (src); DELAY(10); }
-
-#define CPU_TO_CHIP_ADDR(cpu) \
-       (((unsigned)(&(((struct lereg2 *)0)->cpu))) >> 1)
-#endif
-
-#ifdef DS5000
-/* access LANCE registers */
-#define        LERDWR(cntl, src, dst)  (dst) = (src);
+#define        LEWREG(src, dst)        lewritereg(&(dst), (src))
 
 #define CPU_TO_CHIP_ADDR(cpu) \
        ((unsigned)(&(((struct lereg2 *)0)->cpu)))
 
 #define CPU_TO_CHIP_ADDR(cpu) \
        ((unsigned)(&(((struct lereg2 *)0)->cpu)))
@@ -125,7 +153,14 @@ struct     le_softc {
 #define LE_OFFSET_RAM          0x0
 #define LE_OFFSET_LANCE                0x100000
 #define LE_OFFSET_ROM          0x1c0000
 #define LE_OFFSET_RAM          0x0
 #define LE_OFFSET_LANCE                0x100000
 #define LE_OFFSET_ROM          0x1c0000
-#endif
+
+void copytobuf_contig(), copyfrombuf_contig(), bzerobuf_contig();
+void copytobuf_gap2(), copyfrombuf_gap2(), bzerobuf_gap2();
+void copytobuf_gap16(), copyfrombuf_gap16(), bzerobuf_gap16();
+
+extern int pmax_boardtype;
+extern u_long le_iomem;
+extern u_long asic_base;
 
 /*
  * Test to see if device is present.
 
 /*
  * Test to see if device is present.
@@ -142,51 +177,90 @@ leprobe(dp)
        struct ifnet *ifp = &le->sc_if;
        u_char *cp;
        int i;
        struct ifnet *ifp = &le->sc_if;
        u_char *cp;
        int i;
-       extern int leinit(), leioctl(), lestart(), ether_output();
-
-#ifdef DS3100
-       le->sc_r1 = ler1 = (volatile struct lereg1 *)dp->pmax_addr;
-       le->sc_r2 = (volatile struct lereg2 *)MACH_NETWORK_BUFFER_ADDR;
+       extern int leinit(), lereset(), leioctl(), lestart(), ether_output();
+
+       switch (pmax_boardtype) {
+       case DS_PMAX:
+               le->sc_r1 = ler1 = (volatile struct lereg1 *)dp->pmax_addr;
+               le->sc_r2 = (volatile void *)MACH_PHYS_TO_UNCACHED(0x19000000);
+               cp = (u_char *)(MACH_PHYS_TO_UNCACHED(KN01_SYS_CLOCK) + 1);
+               le->sc_ler2pad = 1;
+               le->sc_copytobuf = copytobuf_gap2;
+               le->sc_copyfrombuf = copyfrombuf_gap2;
+               le->sc_zerobuf = bzerobuf_gap2;
+               break;
+       case DS_3MIN:
+       case DS_MAXINE:
+       case DS_3MAXPLUS:
+               if (dp->pmax_unit == 0) {
+                       volatile u_int *ssr, *ldp;
+
+                       le->sc_r1 = ler1 = (volatile struct lereg1 *)
+                               ASIC_SYS_LANCE(asic_base);
+                       cp = (u_char *)ASIC_SYS_ETHER_ADDRESS(asic_base);
+                       le->sc_r2 = (volatile void *)
+                               MACH_PHYS_TO_UNCACHED(le_iomem);
+                       le->sc_ler2pad = 1;
+                       le->sc_copytobuf = copytobuf_gap16;
+                       le->sc_copyfrombuf = copyfrombuf_gap16;
+                       le->sc_zerobuf = bzerobuf_gap16;
 
 
-       /*
-        * Read the ethernet address.
-        * See "DECstation 3100 Desktop Workstation Functional Specification".
-        */
-       cp = (u_char *)(MACH_CLOCK_ADDR + 1);
-       for (i = 0; i < sizeof(le->sc_addr); i++) {
-               le->sc_addr[i] = *cp;
-               cp += 4;
-       }
-#endif
-#ifdef DS5000
-       le->sc_r1 = ler1 = (volatile struct lereg1 *)
-               (dp->pmax_addr + LE_OFFSET_LANCE);
-       le->sc_r2 = (volatile struct lereg2 *)(dp->pmax_addr + LE_OFFSET_RAM);
+                       /*
+                        * And enable Lance dma through the asic.
+                        */
+                       ssr = (volatile u_int *)ASIC_REG_CSR(asic_base);
+                       ldp = (volatile u_int *)
+                               ASIC_REG_LANCE_DMAPTR(asic_base);
+                       *ldp = (le_iomem << 3); /* phys addr << 3 */
+                       *ssr |= ASIC_CSR_DMAEN_LANCE;
+                       break;
+               }
+               /*
+                * Units other than 0 are turbochannel option boards and fall
+                * through to DS_3MAX.
+                */
+       case DS_3MAX:
+               le->sc_r1 = ler1 = (volatile struct lereg1 *)
+                       (dp->pmax_addr + LE_OFFSET_LANCE);
+               le->sc_r2 = (volatile void *)(dp->pmax_addr + LE_OFFSET_RAM);
+               cp = (u_char *)(dp->pmax_addr + LE_OFFSET_ROM + 2);
+               le->sc_ler2pad = 0;
+               le->sc_copytobuf = copytobuf_contig;
+               le->sc_copyfrombuf = copyfrombuf_contig;
+               le->sc_zerobuf = bzerobuf_contig;
+               break;
+       default:
+               printf("Unknown CPU board type %d\n", pmax_boardtype);
+               return (0);
+       };
 
        /*
 
        /*
-        * Read the ethernet address.
+        * Get the ethernet address out of rom
         */
         */
-       cp = (u_char *)(dp->pmax_addr + LE_OFFSET_ROM + 2);
        for (i = 0; i < sizeof(le->sc_addr); i++) {
                le->sc_addr[i] = *cp;
                cp += 4;
        }
        for (i = 0; i < sizeof(le->sc_addr); i++) {
                le->sc_addr[i] = *cp;
                cp += 4;
        }
-#endif
 
        /* make sure the chip is stopped */
 
        /* make sure the chip is stopped */
-       LERDWR(ler0, LE_CSR0, ler1->ler1_rap);
-       LERDWR(ler0, LE_STOP, ler1->ler1_rdp);
+       LEWREG(LE_CSR0, ler1->ler1_rap);
+       LEWREG(LE_STOP, ler1->ler1_rdp);
 
        ifp->if_unit = dp->pmax_unit;
        ifp->if_name = "le";
        ifp->if_mtu = ETHERMTU;
        ifp->if_init = leinit;
 
        ifp->if_unit = dp->pmax_unit;
        ifp->if_name = "le";
        ifp->if_mtu = ETHERMTU;
        ifp->if_init = leinit;
+       ifp->if_reset = lereset;
        ifp->if_ioctl = leioctl;
        ifp->if_output = ether_output;
        ifp->if_start = lestart;
        ifp->if_ioctl = leioctl;
        ifp->if_output = ether_output;
        ifp->if_start = lestart;
+#ifdef MULTICAST
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+#else
        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+#endif
 #if NBPFILTER > 0
 #if NBPFILTER > 0
-       bpfattach(&le->sc_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+       bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
 #endif
        if_attach(ifp);
 
 #endif
        if_attach(ifp);
 
@@ -196,22 +270,115 @@ leprobe(dp)
        return (1);
 }
 
        return (1);
 }
 
-ledrinit(ler2)
-       register volatile struct lereg2 *ler2;
+#ifdef MULTICAST
+/*
+ * Setup the logical address filter
+ */
+void
+lesetladrf(le)
+       register struct le_softc *le;
+{
+       register volatile struct lereg2 *ler2 = le->sc_r2;
+       register struct ifnet *ifp = &le->sc_if;
+       register struct ether_multi *enm;
+       register u_char *cp;
+       register u_long crc;
+       register u_long c;
+       register int i, len;
+       struct ether_multistep step;
+
+       /*
+        * Set up multicast address filter by passing all multicast
+        * addresses through a crc generator, and then using the high
+        * order 6 bits as a index into the 64 bit logical address
+        * filter. The high order two bits select the word, while the
+        * rest of the bits select the bit within the word.
+        */
+
+       LER2_ladrf0(ler2, 0);
+       LER2_ladrf1(ler2, 0);
+       ifp->if_flags &= ~IFF_ALLMULTI;
+       ETHER_FIRST_MULTI(step, &le->sc_ac, enm);
+       while (enm != NULL) {
+               if (bcmp((caddr_t)&enm->enm_addrlo,
+                   (caddr_t)&enm->enm_addrhi, sizeof(enm->enm_addrlo)) == 0) {
+                       /*
+                        * We must listen to a range of multicast
+                        * addresses. For now, just accept all
+                        * multicasts, rather than trying to set only
+                        * those filter bits needed to match the range.
+                        * (At this time, the only use of address
+                        * ranges is for IP multicast routing, for
+                        * which the range is big enough to require all
+                        * bits set.)
+                        */
+                       LER2_ladrf0(ler2, 0xff);
+                       LER2_ladrf1(ler2, 0xff);
+                       LER2_ladrf2(ler2, 0xff);
+                       LER2_ladrf3(ler2, 0xff);
+                       ifp->if_flags |= IFF_ALLMULTI;
+                       return;
+               }
+
+               cp = (unsigned char *)&enm->enm_addrlo;
+               c = *cp;
+               crc = 0xffffffff;
+               len = 6;
+               while (len-- > 0) {
+                       c = *cp;
+                       for (i = 0; i < 8; i++) {
+                               if ((c & 0x01) ^ (crc & 0x01)) {
+                                       crc >>= 1;
+                                       crc = crc ^ 0xedb88320;
+                               }
+                               else
+                                       crc >>= 1;
+                               c >>= 1;
+                       }
+                       cp++;
+               }
+               /* Just want the 6 most significant bits. */
+               crc = crc >> 26;
+
+               /* Turn on the corresponding bit in the filter. */
+               switch (crc >> 5) {
+               case 0:
+                       LER2_ladrf0(ler2, 1 << (crc & 0x1f));
+                       break;
+               case 1:
+                       LER2_ladrf1(ler2, 1 << (crc & 0x1f));
+                       break;
+               case 2:
+                       LER2_ladrf2(ler2, 1 << (crc & 0x1f));
+                       break;
+               case 3:
+                       LER2_ladrf3(ler2, 1 << (crc & 0x1f));
+               }
+
+               ETHER_NEXT_MULTI(step, enm);
+       }
+}
+#endif
+
+ledrinit(le)
+       struct le_softc *le;
 {
 {
+       register volatile void *rp;
        register int i;
 
        for (i = 0; i < LERBUF; i++) {
        register int i;
 
        for (i = 0; i < LERBUF; i++) {
-               ler2->ler2_rmd[i].rmd0 = CPU_TO_CHIP_ADDR(ler2_rbuf[i][0]);
-               ler2->ler2_rmd[i].rmd1 = LE_OWN;
-               ler2->ler2_rmd[i].rmd2 = -LEMTU;
-               ler2->ler2_rmd[i].rmd3 = 0;
+               rp = LER2_RMDADDR(le->sc_r2, i);
+               LER2_rmd0(rp, CPU_TO_CHIP_ADDR(ler2_rbuf[i][0]));
+               LER2_rmd1(rp, LE_OWN);
+               LER2_rmd2(rp, -LEMTU);
+               LER2_rmd3(rp, 0);
        }
        for (i = 0; i < LETBUF; i++) {
        }
        for (i = 0; i < LETBUF; i++) {
-               ler2->ler2_tmd[i].tmd0 = CPU_TO_CHIP_ADDR(ler2_tbuf[i][0]);
-               ler2->ler2_tmd[i].tmd1 = 0;
-               ler2->ler2_tmd[i].tmd2 = 0;
-               ler2->ler2_tmd[i].tmd3 = 0;
+               rp = LER2_TMDADDR(le->sc_r2, i);
+               LER2_tmd0(rp, CPU_TO_CHIP_ADDR(ler2_tbuf[i][0]));
+               LER2_tmd1(rp, 0);
+               LER2_tmd2(rp, 0);
+               LER2_tmd3(rp, 0);
        }
 }
 
        }
 }
 
@@ -220,74 +387,65 @@ lereset(unit)
 {
        register struct le_softc *le = &le_softc[unit];
        register volatile struct lereg1 *ler1 = le->sc_r1;
 {
        register struct le_softc *le = &le_softc[unit];
        register volatile struct lereg1 *ler1 = le->sc_r1;
-       register volatile struct lereg2 *ler2 = le->sc_r2;
+       register volatile void *ler2 = le->sc_r2;
        register int timo = 100000;
        register int stat;
 
 #ifdef lint
        stat = unit;
 #endif
        register int timo = 100000;
        register int stat;
 
 #ifdef lint
        stat = unit;
 #endif
+       LEWREG(LE_CSR0, ler1->ler1_rap);
+       LEWREG(LE_STOP, ler1->ler1_rdp);
+
+       /*
+        * Setup for transmit/receive
+        */
 #if NBPFILTER > 0
        if (le->sc_if.if_flags & IFF_PROMISC)
                /* set the promiscuous bit */
 #if NBPFILTER > 0
        if (le->sc_if.if_flags & IFF_PROMISC)
                /* set the promiscuous bit */
-               le->sc_r2->ler2_mode = LE_MODE|0x8000;
+               LER2_mode(ler2, LE_MODE | 0x8000);
        else
        else
-               le->sc_r2->ler2_mode = LE_MODE;
 #endif
 #endif
-       LERDWR(ler0, LE_CSR0, ler1->ler1_rap);
-       LERDWR(ler0, LE_STOP, ler1->ler1_rdp);
-
-       /*
-        * Setup for transmit/receive
-        */
-       ler2->ler2_mode = LE_MODE;
-       ler2->ler2_padr0 = (le->sc_addr[1] << 8) | le->sc_addr[0];
-       ler2->ler2_padr1 = (le->sc_addr[3] << 8) | le->sc_addr[2];
-       ler2->ler2_padr2 = (le->sc_addr[5] << 8) | le->sc_addr[4];
-#ifdef RMP
-       /*
-        * Set up logical addr filter to accept multicast 9:0:9:0:0:4
-        * This should be an ioctl() to the driver.  (XXX)
-        */
-       ler2->ler2_ladrf0 = 0x0010;
-       ler2->ler2_ladrf1 = 0x0;
-       ler2->ler2_ladrf2 = 0x0;
-       ler2->ler2_ladrf3 = 0x0;
+               LER2_mode(ler2, LE_MODE);
+       LER2_padr0(ler2, (le->sc_addr[1] << 8) | le->sc_addr[0]);
+       LER2_padr1(ler2, (le->sc_addr[3] << 8) | le->sc_addr[2]);
+       LER2_padr2(ler2, (le->sc_addr[5] << 8) | le->sc_addr[4]);
+       /* Setup the logical address filter */
+#ifdef MULTICAST
+       lesetladrf(le);
 #else
 #else
-       ler2->ler2_ladrf0 = 0;
-       ler2->ler2_ladrf1 = 0;
-       ler2->ler2_ladrf2 = 0;
-       ler2->ler2_ladrf3 = 0;
+       LER2_ladrf0(ler2, 0);
+       LER2_ladrf1(ler2, 0);
+       LER2_ladrf2(ler2, 0);
+       LER2_ladrf3(ler2, 0);
 #endif
 #endif
-       ler2->ler2_rlen = LE_RLEN;
-       ler2->ler2_rdra = CPU_TO_CHIP_ADDR(ler2_rmd[0]);
-       ler2->ler2_tlen = LE_TLEN;
-       ler2->ler2_tdra = CPU_TO_CHIP_ADDR(ler2_tmd[0]);
-       ledrinit(ler2);
+       LER2_rlen(ler2, LE_RLEN);
+       LER2_rdra(ler2, CPU_TO_CHIP_ADDR(ler2_rmd[0]));
+       LER2_tlen(ler2, LE_TLEN);
+       LER2_tdra(ler2, CPU_TO_CHIP_ADDR(ler2_tmd[0]));
+       ledrinit(le);
        le->sc_rmd = 0;
        le->sc_tmd = LETBUF - 1;
        le->sc_tmdnext = 0;
 
        le->sc_rmd = 0;
        le->sc_tmd = LETBUF - 1;
        le->sc_tmdnext = 0;
 
-       LERDWR(ler0, LE_CSR1, ler1->ler1_rap);
-       LERDWR(ler0, CPU_TO_CHIP_ADDR(ler2_mode), ler1->ler1_rdp);
-       LERDWR(ler0, LE_CSR2, ler1->ler1_rap);
-       LERDWR(ler0, 0, ler1->ler1_rdp);
-       LERDWR(ler0, LE_CSR3, ler1->ler1_rap);
-       LERDWR(ler0, 0, ler1->ler1_rdp);
-       LERDWR(ler0, LE_CSR0, ler1->ler1_rap);
+       LEWREG(LE_CSR1, ler1->ler1_rap);
+       LEWREG(CPU_TO_CHIP_ADDR(ler2_mode), ler1->ler1_rdp);
+       LEWREG(LE_CSR2, ler1->ler1_rap);
+       LEWREG(0, ler1->ler1_rdp);
+       LEWREG(LE_CSR3, ler1->ler1_rap);
+       LEWREG(0, ler1->ler1_rdp);
+       LEWREG(LE_CSR0, ler1->ler1_rap);
        LERDWR(ler0, LE_INIT, ler1->ler1_rdp);
        LERDWR(ler0, LE_INIT, ler1->ler1_rdp);
-       MachEmptyWriteBuffer();
        do {
                if (--timo == 0) {
                        printf("le%d: init timeout, stat = 0x%x\n",
                               unit, stat);
                        break;
                }
        do {
                if (--timo == 0) {
                        printf("le%d: init timeout, stat = 0x%x\n",
                               unit, stat);
                        break;
                }
-               LERDWR(ler0, ler1->ler1_rdp, stat);
+               stat = ler1->ler1_rdp;
        } while ((stat & LE_IDON) == 0);
        LERDWR(ler0, LE_IDON, ler1->ler1_rdp);
        LERDWR(ler0, LE_STRT | LE_INEA, ler1->ler1_rdp);
        } while ((stat & LE_IDON) == 0);
        LERDWR(ler0, LE_IDON, ler1->ler1_rdp);
        LERDWR(ler0, LE_STRT | LE_INEA, ler1->ler1_rdp);
-       MachEmptyWriteBuffer();
        le->sc_if.if_flags &= ~IFF_OACTIVE;
 }
 
        le->sc_if.if_flags &= ~IFF_OACTIVE;
 }
 
@@ -297,13 +455,16 @@ lereset(unit)
 leinit(unit)
        int unit;
 {
 leinit(unit)
        int unit;
 {
-       struct le_softc *le = &le_softc[unit];
-       register struct ifnet *ifp = &le->sc_if;
+       register struct ifnet *ifp = &le_softc[unit].sc_if;
+       register struct ifaddr *ifa;
        int s;
 
        /* not yet, if address still unknown */
        int s;
 
        /* not yet, if address still unknown */
-       if (ifp->if_addrlist == (struct ifaddr *)0)
-               return;
+       for (ifa = ifp->if_addrlist;; ifa = ifa->ifa_next)
+               if (ifa == 0)
+                       return;
+               else if (ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_LINK)
+                       break;
        if ((ifp->if_flags & IFF_RUNNING) == 0) {
                s = splnet();
                ifp->if_flags |= IFF_RUNNING;
        if ((ifp->if_flags & IFF_RUNNING) == 0) {
                s = splnet();
                ifp->if_flags |= IFF_RUNNING;
@@ -314,7 +475,9 @@ leinit(unit)
 }
 
 #define        LENEXTTMP \
 }
 
 #define        LENEXTTMP \
-       if (++bix == LETBUF) bix = 0, tmd = le->sc_r2->ler2_tmd; else ++tmd
+       if (++bix == LETBUF) \
+               bix = 0; \
+       tmd = LER2_TMDADDR(le->sc_r2, bix)
 
 /*
  * Start output on interface.  Get another datagram to send
 
 /*
  * Start output on interface.  Get another datagram to send
@@ -326,36 +489,35 @@ lestart(ifp)
 {
        register struct le_softc *le = &le_softc[ifp->if_unit];
        register int bix = le->sc_tmdnext;
 {
        register struct le_softc *le = &le_softc[ifp->if_unit];
        register int bix = le->sc_tmdnext;
-       register volatile struct letmd *tmd = &le->sc_r2->ler2_tmd[bix];
+       register volatile void *tmd = LER2_TMDADDR(le->sc_r2, bix);
        register struct mbuf *m;
        int len = 0;
 
        if ((le->sc_if.if_flags & IFF_RUNNING) == 0)
                return (0);
        while (bix != le->sc_tmd) {
        register struct mbuf *m;
        int len = 0;
 
        if ((le->sc_if.if_flags & IFF_RUNNING) == 0)
                return (0);
        while (bix != le->sc_tmd) {
-               if (tmd->tmd1 & LE_OWN)
+               if (LER2V_tmd1(tmd) & LE_OWN)
                        panic("lestart");
                IF_DEQUEUE(&le->sc_if.if_snd, m);
                if (m == 0)
                        break;
                        panic("lestart");
                IF_DEQUEUE(&le->sc_if.if_snd, m);
                if (m == 0)
                        break;
-               len = leput(le->sc_r2->ler2_tbuf[bix], m);
 #if NBPFILTER > 0
                /*
                 * If bpf is listening on this interface, let it
                 * see the packet before we commit it to the wire.
                 */
 #if NBPFILTER > 0
                /*
                 * If bpf is listening on this interface, let it
                 * see the packet before we commit it to the wire.
                 */
-               if (le->sc_bpf)
-                       bpf_tap(le->sc_bpf, le->sc_r2->ler2_tbuf[bix], len);
+               if (ifp->if_bpf)
+                       bpf_mtap(ifp->if_bpf, m);
 #endif
 #endif
-               tmd->tmd3 = 0;
-               tmd->tmd2 = -len;
-               tmd->tmd1 = LE_OWN | LE_STP | LE_ENP;
+               len = leput(le, LER2_TBUFADDR(le->sc_r2, bix), m);
+               LER2_tmd3(tmd, 0);
+               LER2_tmd2(tmd, -len);
+               LER2_tmd1(tmd, LE_OWN | LE_STP | LE_ENP);
                LENEXTTMP;
        }
        if (len != 0) {
                le->sc_if.if_flags |= IFF_OACTIVE;
                LERDWR(ler0, LE_TDMD | LE_INEA, le->sc_r1->ler1_rdp);
                LENEXTTMP;
        }
        if (len != 0) {
                le->sc_if.if_flags |= IFF_OACTIVE;
                LERDWR(ler0, LE_TDMD | LE_INEA, le->sc_r1->ler1_rdp);
-               MachEmptyWriteBuffer();
        }
        le->sc_tmdnext = bix;
        return (0);
        }
        le->sc_tmdnext = bix;
        return (0);
@@ -393,7 +555,6 @@ leintr(unit)
                if (stat & LE_MISS)
                        le->sc_miss++;
                LERDWR(ler0, LE_BABL|LE_CERR|LE_MISS|LE_INEA, ler1->ler1_rdp);
                if (stat & LE_MISS)
                        le->sc_miss++;
                LERDWR(ler0, LE_BABL|LE_CERR|LE_MISS|LE_INEA, ler1->ler1_rdp);
-               MachEmptyWriteBuffer();
        }
        if ((stat & LE_RXON) == 0) {
                le->sc_rxoff++;
        }
        if ((stat & LE_RXON) == 0) {
                le->sc_rxoff++;
@@ -411,7 +572,6 @@ leintr(unit)
        }
        if (stat & LE_TINT) {
                LERDWR(ler0, LE_TINT|LE_INEA, ler1->ler1_rdp);
        }
        if (stat & LE_TINT) {
                LERDWR(ler0, LE_TINT|LE_INEA, ler1->ler1_rdp);
-               MachEmptyWriteBuffer();
                lexint(unit);
        }
 }
                lexint(unit);
        }
 }
@@ -425,31 +585,31 @@ lexint(unit)
 {
        register struct le_softc *le = &le_softc[unit];
        register int bix = le->sc_tmd;
 {
        register struct le_softc *le = &le_softc[unit];
        register int bix = le->sc_tmd;
-       register volatile struct letmd *tmd = &le->sc_r2->ler2_tmd[bix];
+       register volatile void *tmd;
 
        if ((le->sc_if.if_flags & IFF_OACTIVE) == 0) {
                le->sc_xint++;
                return;
        }
        LENEXTTMP;
 
        if ((le->sc_if.if_flags & IFF_OACTIVE) == 0) {
                le->sc_xint++;
                return;
        }
        LENEXTTMP;
-       while (bix != le->sc_tmdnext && (tmd->tmd1 & LE_OWN) == 0) {
+       while (bix != le->sc_tmdnext && (LER2V_tmd1(tmd) & LE_OWN) == 0) {
                le->sc_tmd = bix;
                le->sc_tmd = bix;
-               if ((tmd->tmd1 & LE_ERR) || (tmd->tmd3 & LE_TBUFF)) {
+               if ((LER2V_tmd1(tmd) & LE_ERR) || (LER2V_tmd3(tmd) & LE_TBUFF)) {
                        lexerror(unit);
                        le->sc_if.if_oerrors++;
                        lexerror(unit);
                        le->sc_if.if_oerrors++;
-                       if (tmd->tmd3 & (LE_TBUFF|LE_UFLO)) {
+                       if (LER2V_tmd3(tmd) & (LE_TBUFF|LE_UFLO)) {
                                le->sc_uflo++;
                                lereset(unit);
                                break;
                        }
                                le->sc_uflo++;
                                lereset(unit);
                                break;
                        }
-                       else if (tmd->tmd3 & LE_LCOL)
+                       else if (LER2V_tmd3(tmd) & LE_LCOL)
                                le->sc_if.if_collisions++;
                                le->sc_if.if_collisions++;
-                       else if (tmd->tmd3 & LE_RTRY)
+                       else if (LER2V_tmd3(tmd) & LE_RTRY)
                                le->sc_if.if_collisions += 16;
                }
                                le->sc_if.if_collisions += 16;
                }
-               else if (tmd->tmd1 & LE_ONE)
+               else if (LER2V_tmd1(tmd) & LE_ONE)
                        le->sc_if.if_collisions++;
                        le->sc_if.if_collisions++;
-               else if (tmd->tmd1 & LE_MORE)
+               else if (LER2V_tmd1(tmd) & LE_MORE)
                        /* what is the real number? */
                        le->sc_if.if_collisions += 2;
                else
                        /* what is the real number? */
                        le->sc_if.if_collisions += 2;
                else
@@ -462,7 +622,9 @@ lexint(unit)
 }
 
 #define        LENEXTRMP \
 }
 
 #define        LENEXTRMP \
-       if (++bix == LERBUF) bix = 0, rmd = le->sc_r2->ler2_rmd; else ++rmd
+       if (++bix == LERBUF) \
+               bix = 0; \
+       rmd = LER2_RMDADDR(le->sc_r2, bix)
 
 /*
  * Ethernet interface receiver interrupt.
 
 /*
  * Ethernet interface receiver interrupt.
@@ -475,32 +637,31 @@ lerint(unit)
 {
        register struct le_softc *le = &le_softc[unit];
        register int bix = le->sc_rmd;
 {
        register struct le_softc *le = &le_softc[unit];
        register int bix = le->sc_rmd;
-       register volatile struct lermd *rmd = &le->sc_r2->ler2_rmd[bix];
+       register volatile void *rmd = LER2_RMDADDR(le->sc_r2, bix);
 
        /*
         * Out of sync with hardware, should never happen?
         */
 
        /*
         * Out of sync with hardware, should never happen?
         */
-       if (rmd->rmd1 & LE_OWN) {
+       if (LER2V_rmd1(rmd) & LE_OWN) {
+               le->sc_rown++;
                LERDWR(le->sc_r0, LE_RINT|LE_INEA, le->sc_r1->ler1_rdp);
                LERDWR(le->sc_r0, LE_RINT|LE_INEA, le->sc_r1->ler1_rdp);
-               MachEmptyWriteBuffer();
                return;
        }
 
        /*
         * Process all buffers with valid data
         */
                return;
        }
 
        /*
         * Process all buffers with valid data
         */
-       while ((rmd->rmd1 & LE_OWN) == 0) {
-               int len = rmd->rmd3;
+       while ((LER2V_rmd1(rmd) & LE_OWN) == 0) {
+               int len = LER2V_rmd3(rmd);
 
                /* Clear interrupt to avoid race condition */
                LERDWR(le->sc_r0, LE_RINT|LE_INEA, le->sc_r1->ler1_rdp);
 
                /* Clear interrupt to avoid race condition */
                LERDWR(le->sc_r0, LE_RINT|LE_INEA, le->sc_r1->ler1_rdp);
-               MachEmptyWriteBuffer();
 
 
-               if (rmd->rmd1 & LE_ERR) {
+               if (LER2V_rmd1(rmd) & LE_ERR) {
                        le->sc_rmd = bix;
                        lererror(unit, "bad packet");
                        le->sc_if.if_ierrors++;
                        le->sc_rmd = bix;
                        lererror(unit, "bad packet");
                        le->sc_if.if_ierrors++;
-               } else if ((rmd->rmd1 & (LE_STP|LE_ENP)) != (LE_STP|LE_ENP)) {
+               } else if ((LER2V_rmd1(rmd) & (LE_STP|LE_ENP)) != (LE_STP|LE_ENP)) {
                        /*
                         * Find the end of the packet so we can see how long
                         * it was.  We still throw it away.
                        /*
                         * Find the end of the packet so we can see how long
                         * it was.  We still throw it away.
@@ -508,11 +669,10 @@ lerint(unit)
                        do {
                                LERDWR(le->sc_r0, LE_RINT|LE_INEA,
                                       le->sc_r1->ler1_rdp);
                        do {
                                LERDWR(le->sc_r0, LE_RINT|LE_INEA,
                                       le->sc_r1->ler1_rdp);
-                               MachEmptyWriteBuffer();
-                               rmd->rmd3 = 0;
-                               rmd->rmd1 = LE_OWN;
+                               LER2_rmd3(rmd, 0);
+                               LER2_rmd1(rmd, LE_OWN);
                                LENEXTRMP;
                                LENEXTRMP;
-                       } while (!(rmd->rmd1 & (LE_OWN|LE_ERR|LE_STP|LE_ENP)));
+                       } while (!(LER2V_rmd1(rmd) & (LE_OWN|LE_ERR|LE_STP|LE_ENP)));
                        le->sc_rmd = bix;
                        lererror(unit, "chained buffer");
                        le->sc_rxlen++;
                        le->sc_rmd = bix;
                        lererror(unit, "chained buffer");
                        le->sc_rxlen++;
@@ -520,15 +680,15 @@ lerint(unit)
                         * If search terminated without successful completion
                         * we reset the hardware (conservative).
                         */
                         * If search terminated without successful completion
                         * we reset the hardware (conservative).
                         */
-                       if ((rmd->rmd1 & (LE_OWN|LE_ERR|LE_STP|LE_ENP)) !=
+                       if ((LER2V_rmd1(rmd) & (LE_OWN|LE_ERR|LE_STP|LE_ENP)) !=
                            LE_ENP) {
                                lereset(unit);
                                return;
                        }
                } else
                            LE_ENP) {
                                lereset(unit);
                                return;
                        }
                } else
-                       leread(unit, le->sc_r2->ler2_rbuf[bix], len);
-               rmd->rmd3 = 0;
-               rmd->rmd1 = LE_OWN;
+                       leread(unit, LER2_RBUFADDR(le->sc_r2, bix), len);
+               LER2_rmd3(rmd, 0);
+               LER2_rmd1(rmd, LE_OWN);
                LENEXTRMP;
        }
        MachEmptyWriteBuffer();         /* Paranoia */
                LENEXTRMP;
        }
        MachEmptyWriteBuffer();         /* Paranoia */
@@ -543,64 +703,31 @@ lerint(unit)
  */
 leread(unit, buf, len)
        int unit;
  */
 leread(unit, buf, len)
        int unit;
-       le_buf_t *buf;
+       volatile void *buf;
        int len;
 {
        register struct le_softc *le = &le_softc[unit];
        struct ether_header et;
        struct mbuf *m;
        int len;
 {
        register struct le_softc *le = &le_softc[unit];
        struct ether_header et;
        struct mbuf *m;
-       int off, resid;
-#ifdef DS3100
-       u_short sbuf[2];
-#endif
+       int off, resid, flags;
+       u_short sbuf[2], eth_type;
        extern struct mbuf *leget();
 
        le->sc_if.if_ipackets++;
        extern struct mbuf *leget();
 
        le->sc_if.if_ipackets++;
-#ifdef DS3100
-       CopyFromBuffer(buf, (char *)&et, sizeof(et));
-#endif
-#ifdef DS5000
-       bcopy(buf, (char *)&et, sizeof(et));
-#endif
-       et.ether_type = ntohs(et.ether_type);
+       (*le->sc_copyfrombuf)(buf, 0, (char *)&et, sizeof (et));
+       eth_type = ntohs(et.ether_type);
        /* adjust input length to account for header and CRC */
        len = len - sizeof(struct ether_header) - 4;
 
        /* adjust input length to account for header and CRC */
        len = len - sizeof(struct ether_header) - 4;
 
-#ifdef RMP
-       /*  (XXX)
-        *
-        *  If Ethernet Type field is < MaxPacketSize, we probably have
-        *  a IEEE802 packet here.  Make sure that the size is at least
-        *  that of the HP LLC.  Also do sanity checks on length of LLC
-        *  (old Ethernet Type field) and packet length.
-        *
-        *  Provided the above checks succeed, change `len' to reflect
-        *  the length of the LLC (i.e. et.ether_type) and change the
-        *  type field to ETHERTYPE_IEEE so we can switch() on it later.
-        *  Yes, this is a hack and will eventually be done "right".
-        */
-       if (et.ether_type <= IEEE802LEN_MAX && len >= sizeof(struct hp_llc) &&
-           len >= et.ether_type && len >= IEEE802LEN_MIN) {
-               len = et.ether_type;
-               et.ether_type = ETHERTYPE_IEEE; /* hack! */
-       }
-#endif
-
-       if (et.ether_type >= ETHERTYPE_TRAIL &&
-           et.ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
-               off = (et.ether_type - ETHERTYPE_TRAIL) * 512;
+       if (eth_type >= ETHERTYPE_TRAIL &&
+           eth_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+               off = (eth_type - ETHERTYPE_TRAIL) * 512;
                if (off >= ETHERMTU)
                        return;         /* sanity */
                if (off >= ETHERMTU)
                        return;         /* sanity */
-#ifdef DS3100
-               CopyFromBuffer(buf + (sizeof(et) + off),
-                       (char *)sbuf, sizeof(sbuf));
-               et.ether_type = ntohs(sbuf[0]);
+               (*le->sc_copyfrombuf)(buf, sizeof (et) + off, (char *)sbuf,
+                       sizeof (sbuf));
+               eth_type = ntohs(sbuf[0]);
                resid = ntohs(sbuf[1]);
                resid = ntohs(sbuf[1]);
-#endif
-#ifdef DS5000
-               et.ether_type = ntohs(((u_short *)(buf + (sizeof(et) + off)))[0]);
-               resid = ntohs(((u_short *)(buf + (sizeof(et) + off)))[1]);
-#endif
                if (off + resid > len)
                        return;         /* sanity */
                len = off + resid;
                if (off + resid > len)
                        return;         /* sanity */
                len = off + resid;
@@ -616,146 +743,77 @@ leread(unit, buf, len)
                le->sc_if.if_ierrors++;
                return;
        }
                le->sc_if.if_ierrors++;
                return;
        }
-#if NBPFILTER > 0
-       /*
-        * Check if there's a bpf filter listening on this interface.
-        * If so, hand off the raw packet to bpf, which must deal with
-        * trailers in its own way.
-        */
-       if (le->sc_bpf) {
-               bpf_tap(le->sc_bpf, buf, len + sizeof(struct ether_header));
+       flags = 0;
+       if (bcmp((caddr_t)etherbroadcastaddr,
+           (caddr_t)et.ether_dhost, sizeof(etherbroadcastaddr)) == 0)
+               flags |= M_BCAST;
+       if (et.ether_dhost[0] & 1)
+               flags |= M_MCAST;
 
 
-               /*
-                * Note that the interface cannot be in promiscuous mode if
-                * there are no bpf listeners.  And if we are in promiscuous
-                * mode, we have to check if this packet is really ours.
-                *
-                * XXX This test does not support multicasts.
-                */
-               if ((le->sc_if.if_flags & IFF_PROMISC)
-                   && bcmp(et.ether_dhost, le->sc_addr, 
-                           sizeof(et.ether_dhost)) != 0
-                   && bcmp(et.ether_dhost, etherbroadcastaddr, 
-                           sizeof(et.ether_dhost)) != 0)
-                       return;
-       }
-#endif
        /*
         * Pull packet off interface.  Off is nonzero if packet
         * has trailing header; leget 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.
         */
        /*
         * Pull packet off interface.  Off is nonzero if packet
         * has trailing header; leget 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 = leget(buf, len, off, &le->sc_if);
+       m = leget(le, buf, len, off, &le->sc_if);
        if (m == 0)
                return;
        if (m == 0)
                return;
-#ifdef RMP
+#if NBPFILTER > 0
        /*
        /*
-        * (XXX)
-        * This needs to be integrated with the ISO stuff in ether_input()
+        * Check if there's a bpf filter listening on this interface.
+        * If so, hand off the raw packet to enet.
         */
         */
-       if (et.ether_type == ETHERTYPE_IEEE) {
-               /*
-                *  Snag the Logical Link Control header (IEEE 802.2).
-                */
-               struct hp_llc *llc = &(mtod(m, struct rmp_packet *)->hp_llc);
+       if (le->sc_if.if_bpf) {
+               bpf_mtap(le->sc_if.if_bpf, m);
 
                /*
 
                /*
-                *  If the DSAP (and HP's extended DXSAP) indicate this
-                *  is an RMP packet, hand it to the raw input routine.
+                * Keep the packet if it's a broadcast or has our
+                * physical ethernet address (or if we support
+                * multicast and it's one).
                 */
                 */
-               if (llc->dsap == IEEE_DSAP_HP && llc->dxsap == HPEXT_DXSAP) {
-                       static struct sockproto rmp_sp = {AF_RMP,RMPPROTO_BOOT};
-                       static struct sockaddr rmp_src = {AF_RMP};
-                       static struct sockaddr rmp_dst = {AF_RMP};
-
-                       bcopy(et.ether_shost, rmp_src.sa_data,
-                             sizeof(et.ether_shost));
-                       bcopy(et.ether_dhost, rmp_dst.sa_data,
-                             sizeof(et.ether_dhost));
-
-                       raw_input(m, &rmp_sp, &rmp_src, &rmp_dst);
+               if (
+#ifdef MULTICAST
+                   (flags & (M_BCAST | M_MCAST)) == 0 &&
+#else
+                   (flags & M_BCAST) == 0 &&
+#endif
+                   bcmp(et.ether_dhost, le->sc_addr,
+                       sizeof(et.ether_dhost)) != 0) {
+                       m_freem(m);
                        return;
                }
        }
 #endif
                        return;
                }
        }
 #endif
+       m->m_flags |= flags;
+       et.ether_type = eth_type;
        ether_input(&le->sc_if, &et, m);
 }
 
 /*
  * Routine to copy from mbuf chain to transmit buffer in
  * network buffer memory.
        ether_input(&le->sc_if, &et, m);
 }
 
 /*
  * Routine to copy from mbuf chain to transmit buffer in
  * network buffer memory.
- * NOTE: On the DS3100, network memory can only be written one short at
- *     every other address.
  */
  */
-leput(lebuf, m)
-       register le_buf_t *lebuf;
+leput(le, lebuf, m)
+       struct le_softc *le;
+       register volatile void *lebuf;
        register struct mbuf *m;
 {
        register struct mbuf *mp;
        register int len, tlen = 0;
        register struct mbuf *m;
 {
        register struct mbuf *mp;
        register int len, tlen = 0;
-#ifdef DS3100
-       register char *cp;
-       int tmp, xfer;
-#endif
+       register int boff = 0;
 
        for (mp = m; mp; mp = mp->m_next) {
                len = mp->m_len;
                if (len == 0)
                        continue;
 
        for (mp = m; mp; mp = mp->m_next) {
                len = mp->m_len;
                if (len == 0)
                        continue;
-#ifdef DS3100
-               /* copy data for this mbuf */
-               cp = mtod(mp, char *);
-               if (tlen & 1) {
-                       /* handle odd length from previous mbuf */
-                       *lebuf = (cp[0] << 8) | tmp;
-                       lebuf += 2;
-                       cp++;
-                       len--;
-                       tlen++;
-               }
+               (*le->sc_copytobuf)(mtod(mp, char *), lebuf, boff, len);
                tlen += len;
                tlen += len;
-               if ((unsigned)cp & 1) {
-                       while (len > 1) {
-                               *lebuf = (cp[1] << 8) | cp[0];
-                               lebuf += 2;
-                               cp += 2;
-                               len -= 2;
-                       }
-               } else {
-                       /* optimize for aligned transfers */
-                       xfer = (int)((unsigned)len & ~0x1);
-                       CopyToBuffer((u_short *)cp, lebuf, xfer);
-                       lebuf += xfer;
-                       cp += xfer;
-                       len -= xfer;
-               }
-               if (len == 1)
-                       tmp = *cp;
-#endif
-#ifdef DS5000
-               tlen += len;
-               bcopy(mtod(mp, char *), lebuf, len);
-               lebuf += len;
-#endif
+               boff += len;
        }
        m_freem(m);
        }
        m_freem(m);
-#ifdef DS3100
-       /* handle odd length from previous mbuf */
-       if (tlen & 1)
-               *lebuf = tmp;
-#endif
        if (tlen < LEMINSIZE) {
        if (tlen < LEMINSIZE) {
-#ifdef DS3100
-               tlen = (tlen + 1) & ~1;
-               while (tlen < LEMINSIZE) {
-                       *lebuf++ = 0;
-                       tlen += 2;
-               }
-#endif
-#ifdef DS5000
-               bzero(lebuf, LEMINSIZE - tlen);
-#endif
+               (*le->sc_zerobuf)(lebuf, boff, LEMINSIZE - tlen);
                tlen = LEMINSIZE;
        }
        return(tlen);
                tlen = LEMINSIZE;
        }
        return(tlen);
@@ -763,26 +821,23 @@ leput(lebuf, m)
 
 /*
  * Routine to copy from network buffer memory into mbufs.
 
 /*
  * Routine to copy from network buffer memory into mbufs.
- * NOTE: On the DS3100, network memory can only be written one short at
- *     every other address.
  */
 struct mbuf *
  */
 struct mbuf *
-leget(lebuf, totlen, off, ifp)
-       le_buf_t *lebuf;
+leget(le, lebuf, totlen, off, ifp)
+       struct le_softc *le;
+       volatile void *lebuf;
        int totlen, off;
        struct ifnet *ifp;
 {
        register struct mbuf *m;
        struct mbuf *top = 0, **mp = &top;
        int totlen, off;
        struct ifnet *ifp;
 {
        register struct mbuf *m;
        struct mbuf *top = 0, **mp = &top;
-       register int len, resid;
-       register le_buf_t *sp;
+       register int len, resid, boff;
 
        /* NOTE: sizeof(struct ether_header) should be even */
 
        /* NOTE: sizeof(struct ether_header) should be even */
-       lebuf += sizeof(struct ether_header);
-       sp = lebuf;
+       boff = sizeof(struct ether_header);
        if (off) {
                /* NOTE: off should be even */
        if (off) {
                /* NOTE: off should be even */
-               sp += off + 2 * sizeof(u_short);
+               boff += off + 2 * sizeof(u_short);
                totlen -= 2 * sizeof(u_short);
                resid = totlen - off;
        } else
                totlen -= 2 * sizeof(u_short);
                resid = totlen - off;
        } else
@@ -818,28 +873,14 @@ leget(lebuf, totlen, off, ifp)
                        m->m_len = resid;
                }
                len = m->m_len;
                        m->m_len = resid;
                }
                len = m->m_len;
-#ifdef DS3100
-               if ((unsigned)sp & 2) {
-                       /*
-                        * Previous len was odd. Copy the single byte specially.
-                        * XXX Can this ever happen??
-                        */
-                       panic("le odd rcv");
-                       *mtod(m, char *) = ((volatile char *)sp)[-1];
-                       CopyFromBuffer(sp + 1, mtod(m, char *) + 1, len - 1);
-               } else
-                       CopyFromBuffer(sp, mtod(m, char *), len);
-#endif
-#ifdef DS5000
-               bcopy(sp, mtod(m, char *), len);
-#endif
-               sp += len;
+               (*le->sc_copyfrombuf)(lebuf, boff, mtod(m, char *), len);
+               boff += len;
                *mp = m;
                mp = &m->m_next;
                totlen -= len;
                resid -= len;
                if (resid == 0) {
                *mp = m;
                mp = &m->m_next;
                totlen -= len;
                resid -= len;
                if (resid == 0) {
-                       sp = lebuf;
+                       boff = sizeof (struct ether_header);
                        resid = totlen;
                }
        }
                        resid = totlen;
                }
        }
@@ -887,6 +928,7 @@ leioctl(ifp, cmd, data)
                                 * so reset everything
                                 */
                                ifp->if_flags &= ~IFF_RUNNING; 
                                 * so reset everything
                                 */
                                ifp->if_flags &= ~IFF_RUNNING; 
+                               LEWREG(LE_STOP, ler1->ler1_rdp);
                                bcopy((caddr_t)ina->x_host.c_host,
                                    (caddr_t)le->sc_addr, sizeof(le->sc_addr));
                        }
                                bcopy((caddr_t)ina->x_host.c_host,
                                    (caddr_t)le->sc_addr, sizeof(le->sc_addr));
                        }
@@ -900,11 +942,20 @@ leioctl(ifp, cmd, data)
                }
                break;
 
                }
                break;
 
+#if defined (CCITT) && defined (LLC)
+       case SIOCSIFCONF_X25:
+               ifp->if_flags |= IFF_UP;
+               ifa->ifa_rtrequest = cons_rtrequest;
+               error = x25_llcglue(PRC_IFUP, ifa->ifa_addr);
+               if (error == 0)
+                       leinit(ifp->if_unit);
+               break;
+#endif /* CCITT && LLC */
+
        case SIOCSIFFLAGS:
                if ((ifp->if_flags & IFF_UP) == 0 &&
                    ifp->if_flags & IFF_RUNNING) {
        case SIOCSIFFLAGS:
                if ((ifp->if_flags & IFF_UP) == 0 &&
                    ifp->if_flags & IFF_RUNNING) {
-                       LERDWR(le->sc_r0, LE_STOP, ler1->ler1_rdp);
-                       MachEmptyWriteBuffer();
+                       LEWREG(LE_STOP, ler1->ler1_rdp);
                        ifp->if_flags &= ~IFF_RUNNING;
                } else if (ifp->if_flags & IFF_UP &&
                    (ifp->if_flags & IFF_RUNNING) == 0)
                        ifp->if_flags &= ~IFF_RUNNING;
                } else if (ifp->if_flags & IFF_UP &&
                    (ifp->if_flags & IFF_RUNNING) == 0)
@@ -921,6 +972,25 @@ leioctl(ifp, cmd, data)
                }
                break;
 
                }
                break;
 
+#ifdef MULTICAST
+       case SIOCADDMULTI:
+       case SIOCDELMULTI:
+               /* Update our multicast list  */
+               error = (cmd == SIOCADDMULTI) ?
+                   ether_addmulti((struct ifreq *)data, &le->sc_ac) :
+                   ether_delmulti((struct ifreq *)data, &le->sc_ac);
+
+               if (error == ENETRESET) {
+                       /*
+                        * Multicast list has changed; set the hardware
+                        * filter accordingly.
+                        */
+                       lereset(ifp->if_unit);
+                       error = 0;
+               }
+               break;
+#endif
+
        default:
                error = EINVAL;
        }
        default:
                error = EINVAL;
        }
@@ -952,32 +1022,24 @@ lererror(unit, msg)
        char *msg;
 {
        register struct le_softc *le = &le_softc[unit];
        char *msg;
 {
        register struct le_softc *le = &le_softc[unit];
-       register volatile struct lermd *rmd;
+       register volatile void *rmd;
        u_char eaddr[6];
        u_char eaddr[6];
-       char *cp;
        int len;
 
        if (!ledebug)
                return;
 
        int len;
 
        if (!ledebug)
                return;
 
-       rmd = &le->sc_r2->ler2_rmd[le->sc_rmd];
-       len = rmd->rmd3;
-       if (len > 11) {
-#ifdef DS3100
-               CopyFromBuffer((char *)&le->sc_r2->ler2_rbuf[le->sc_rmd][6],
-                       eaddr, sizeof(eaddr));
-#endif
-#ifdef DS5000
-               bcopy((char *)&le->sc_r2->ler2_rbuf[le->sc_rmd][6],
-                       eaddr, sizeof(eaddr));
-#endif
-               cp = ether_sprintf(eaddr);
-       } else
-               cp = "unknown";
+       rmd = LER2_RMDADDR(le->sc_r2, le->sc_rmd);
+       len = LER2V_rmd3(rmd);
+       if (len > 11)
+               (*le->sc_copyfrombuf)(LER2_RBUFADDR(le->sc_r2, le->sc_rmd),
+                       6, eaddr, 6);
        log(LOG_WARNING,
            "le%d: ierror(%s): from %s: buf=%d, len=%d, rmd1=%b\n",
        log(LOG_WARNING,
            "le%d: ierror(%s): from %s: buf=%d, len=%d, rmd1=%b\n",
-           unit, msg, cp, le->sc_rmd, len,
-           rmd->rmd1,
+           unit, msg,
+           len > 11 ? ether_sprintf(eaddr) : "unknown",
+           le->sc_rmd, len,
+           LER2V_rmd1(rmd),
            "\20\20OWN\17ERR\16FRAM\15OFLO\14CRC\13RBUF\12STP\11ENP");
 }
 
            "\20\20OWN\17ERR\16FRAM\15OFLO\14CRC\13RBUF\12STP\11ENP");
 }
 
@@ -985,34 +1047,274 @@ lexerror(unit)
        int unit;
 {
        register struct le_softc *le = &le_softc[unit];
        int unit;
 {
        register struct le_softc *le = &le_softc[unit];
-       register volatile struct letmd *tmd;
+       register volatile void *tmd;
        u_char eaddr[6];
        u_char eaddr[6];
-       char *cp;
        int len;
 
        if (!ledebug)
                return;
 
        int len;
 
        if (!ledebug)
                return;
 
-       tmd = le->sc_r2->ler2_tmd;
-       len = -tmd->tmd2;
-       if (len > 5) {
-#ifdef DS3100
-               CopyFromBuffer((char *)&le->sc_r2->ler2_tbuf[le->sc_tmd][0],
-                       eaddr, sizeof(eaddr));
-#endif
-#ifdef DS5000
-               bcopy((char *)&le->sc_r2->ler2_tbuf[le->sc_tmd][0],
-                       eaddr, sizeof(eaddr));
-#endif
-               cp = ether_sprintf(eaddr);
-       } else
-               cp = "unknown";
+       tmd = LER2_TMDADDR(le->sc_r2, 0);
+       len = -LER2V_tmd2(tmd);
+       if (len > 5)
+               (*le->sc_copyfrombuf)(LER2_TBUFADDR(le->sc_r2, 0), 0, eaddr, 6);
        log(LOG_WARNING,
            "le%d: oerror: to %s: buf=%d, len=%d, tmd1=%b, tmd3=%b\n",
        log(LOG_WARNING,
            "le%d: oerror: to %s: buf=%d, len=%d, tmd1=%b, tmd3=%b\n",
-           unit, cp, le->sc_tmd, len,
-           tmd->tmd1,
+           unit,
+           len > 5 ? ether_sprintf(eaddr) : "unknown",
+           0, len,
+           LER2V_tmd1(tmd),
            "\20\20OWN\17ERR\16RES\15MORE\14ONE\13DEF\12STP\11ENP",
            "\20\20OWN\17ERR\16RES\15MORE\14ONE\13DEF\12STP\11ENP",
-           tmd->tmd3,
+           LER2V_tmd3(tmd),
            "\20\20BUFF\17UFLO\16RES\15LCOL\14LCAR\13RTRY");
 }
            "\20\20BUFF\17UFLO\16RES\15LCOL\14LCAR\13RTRY");
 }
-#endif
+
+/*
+ * Write a lance register port, reading it back to ensure success. This seems
+ * to be necessary during initialization, since the chip appears to be a bit
+ * pokey sometimes.
+ */
+static void
+lewritereg(regptr, val)
+       register volatile u_short *regptr;
+       register u_short val;
+{
+       register int i = 0;
+
+       while (*regptr != val) {
+               *regptr = val;
+               MachEmptyWriteBuffer();
+               if (++i > 10000) {
+                       printf("le: Reg did not settle (to x%x): x%x\n",
+                              val, *regptr);
+                       return;
+               }
+               DELAY(100);
+       }
+}
+
+/*
+ * Routines for accessing the transmit and receive buffers. Unfortunately,
+ * CPU addressing of these buffers is done in one of 3 ways:
+ * - contiguous (for the 3max and turbochannel option card)
+ * - gap2, which means shorts (2 bytes) interspersed with short (2 byte)
+ *   spaces (for the pmax)
+ * - gap16, which means 16bytes interspersed with 16byte spaces
+ *   for buffers which must begin on a 32byte boundary (for 3min and maxine)
+ * The buffer offset is the logical byte offset, assuming contiguous storage.
+ */
+void
+copytobuf_contig(from, lebuf, boff, len)
+       char *from;
+       volatile void *lebuf;
+       int boff;
+       int len;
+{
+
+       /*
+        * Just call bcopy() to do the work.
+        */
+       bcopy(from, ((char *)lebuf) + boff, len);
+}
+
+void
+copyfrombuf_contig(lebuf, boff, to, len)
+       volatile void *lebuf;
+       int boff;
+       char *to;
+       int len;
+{
+
+       /*
+        * Just call bcopy() to do the work.
+        */
+       bcopy(((char *)lebuf) + boff, to, len);
+}
+
+void
+bzerobuf_contig(lebuf, boff, len)
+       volatile void *lebuf;
+       int boff;
+       int len;
+{
+
+       /*
+        * Just let bzero() do the work
+        */
+       bzero(((char *)lebuf) + boff, len);
+}
+
+/*
+ * For the pmax the buffer consists of shorts (2 bytes) interspersed with
+ * short (2 byte) spaces and must be accessed with halfword load/stores.
+ * (don't worry about doing an extra byte)
+ */
+void
+copytobuf_gap2(from, lebuf, boff, len)
+       register char *from;
+       volatile void *lebuf;
+       int boff;
+       register int len;
+{
+       register volatile u_short *bptr;
+       register int xfer;
+
+       if (boff & 0x1) {
+               /* handle unaligned first byte */
+               bptr = ((volatile u_short *)lebuf) + (boff - 1);
+               *bptr = (*from++ << 8) | (*bptr & 0xff);
+               bptr += 2;
+               len--;
+       } else
+               bptr = ((volatile u_short *)lebuf) + boff;
+       if ((unsigned)from & 0x1) {
+               while (len > 1) {
+                       *bptr = (from[1] << 8) | (from[0] & 0xff);
+                       bptr += 2;
+                       from += 2;
+                       len -= 2;
+               }
+       } else {
+               /* optimize for aligned transfers */
+               xfer = (int)((unsigned)len & ~0x1);
+               CopyToBuffer((u_short *)from, bptr, xfer);
+               bptr += xfer;
+               from += xfer;
+               len -= xfer;
+       }
+       if (len == 1)
+               *bptr = (u_short)*from;
+}
+
+void
+copyfrombuf_gap2(lebuf, boff, to, len)
+       volatile void *lebuf;
+       int boff;
+       register char *to;
+       register int len;
+{
+       register volatile u_short *bptr;
+       register u_short tmp;
+       register int xfer;
+
+       if (boff & 0x1) {
+               /* handle unaligned first byte */
+               bptr = ((volatile u_short *)lebuf) + (boff - 1);
+               *to++ = (*bptr >> 8) & 0xff;
+               bptr += 2;
+               len--;
+       } else
+               bptr = ((volatile u_short *)lebuf) + boff;
+       if ((unsigned)to & 0x1) {
+               while (len > 1) {
+                       tmp = *bptr;
+                       *to++ = tmp & 0xff;
+                       *to++ = (tmp >> 8) & 0xff;
+                       bptr += 2;
+                       len -= 2;
+               }
+       } else {
+               /* optimize for aligned transfers */
+               xfer = (int)((unsigned)len & ~0x1);
+               CopyFromBuffer(bptr, to, xfer);
+               bptr += xfer;
+               to += xfer;
+               len -= xfer;
+       }
+       if (len == 1)
+               *to = *bptr & 0xff;
+}
+
+void
+bzerobuf_gap2(lebuf, boff, len)
+       volatile void *lebuf;
+       int boff;
+       int len;
+{
+       register volatile u_short *bptr;
+
+       if ((unsigned)boff & 0x1) {
+               bptr = ((volatile u_short *)lebuf) + (boff - 1);
+               *bptr &= 0xff;
+               bptr += 2;
+               len--;
+       } else
+               bptr = ((volatile u_short *)lebuf) + boff;
+       while (len > 0) {
+               *bptr = 0;
+               bptr += 2;
+               len -= 2;
+       }
+}
+
+/*
+ * For the 3min and maxine, the buffers are in main memory filled in with
+ * 16byte blocks interspersed with 16byte spaces.
+ */
+void
+copytobuf_gap16(from, lebuf, boff, len)
+       register char *from;
+       volatile void *lebuf;
+       int boff;
+       register int len;
+{
+       register char *bptr;
+       register int xfer;
+
+       bptr = ((char *)lebuf) + ((boff << 1) & ~0x1f);
+       boff &= 0xf;
+       xfer = min(len, 16 - boff);
+       while (len > 0) {
+               bcopy(from, ((char *)bptr) + boff, xfer);
+               from += xfer;
+               bptr += 32;
+               boff = 0;
+               len -= xfer;
+               xfer = min(len, 16);
+       }
+}
+
+void
+copyfrombuf_gap16(lebuf, boff, to, len)
+       volatile void *lebuf;
+       int boff;
+       register char *to;
+       register int len;
+{
+       register char *bptr;
+       register int xfer;
+
+       bptr = ((char *)lebuf) + ((boff << 1) & ~0x1f);
+       boff &= 0xf;
+       xfer = min(len, 16 - boff);
+       while (len > 0) {
+               bcopy(((char *)bptr) + boff, to, xfer);
+               to += xfer;
+               bptr += 32;
+               boff = 0;
+               len -= xfer;
+               xfer = min(len, 16);
+       }
+}
+
+void
+bzerobuf_gap16(lebuf, boff, len)
+       volatile void *lebuf;
+       int boff;
+       register int len;
+{
+       register char *bptr;
+       register int xfer;
+
+       bptr = ((char *)lebuf) + ((boff << 1) & ~0x1f);
+       boff &= 0xf;
+       xfer = min(len, 16 - boff);
+       while (len > 0) {
+               bzero(((char *)bptr) + boff, xfer);
+               bptr += 32;
+               boff = 0;
+               len -= xfer;
+               xfer = min(len, 16);
+       }
+}
+#endif /* NLE */