date and time created 92/07/13 00:44:06 by torek
authorChris Torek <torek@ucbvax.Berkeley.EDU>
Mon, 13 Jul 1992 15:44:06 +0000 (07:44 -0800)
committerChris Torek <torek@ucbvax.Berkeley.EDU>
Mon, 13 Jul 1992 15:44:06 +0000 (07:44 -0800)
SCCS-vsn: sys/sparc/sbus/espreg.h 7.1
SCCS-vsn: sys/sparc/sbus/if_le.c 7.1

usr/src/sys/sparc/sbus/espreg.h [new file with mode: 0644]
usr/src/sys/sparc/sbus/if_le.c [new file with mode: 0644]

diff --git a/usr/src/sys/sparc/sbus/espreg.h b/usr/src/sys/sparc/sbus/espreg.h
new file mode 100644 (file)
index 0000000..44ce7a0
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * %sccs.include.redist.c%
+ *
+ *     @(#)espreg.h    7.1 (Berkeley) %G%
+ *
+ * from: $Header: espreg.h,v 1.6 92/06/17 06:59:35 torek Exp $ (LBL)
+ *
+ * Derived from Mary Baker's devSCSIC90.c from the Berkeley
+ * Sprite project, which is:
+ *
+ * Copyright 1988 Regents of the University of California
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies.  The University of California
+ * makes no representations about the suitability of this
+ * software for any purpose.  It is provided "as is" without
+ * express or implied warranty.
+ */
+
+/*
+ * Emulex ESP100, ESP100A, and ESP200 registers, as found on the
+ * Sun-4c Sbus.
+ *
+ * The registers are all bytes, and all on longword boundaries.
+ * Grody to the max!
+ */
+struct espreg {
+       u_char  esp_tcl;        /* transfer count low (byte 0) (rw) */
+       u_char  esp_xxx0[3];
+       u_char  esp_tch;        /* transfer count high (byte 1) (rw) */
+       u_char  esp_xxx1[3];
+       u_char  esp_fifo;       /* fifo data register (rw) */
+       u_char  esp_xxx2[3];
+       u_char  esp_cmd;        /* command (rw) */
+       u_char  esp_xxx3[3];
+       u_char  esp_stat;       /* status (ro); scsi id (wo) */
+#define        esp_id  esp_stat
+       u_char  esp_xxx4[3];
+       u_char  esp_intr;       /* interrupt (ro); timeout (wo) */
+#define        esp_timeout esp_intr
+       u_char  esp_xxx5[3];
+       u_char  esp_step;       /* sequence step (ro); sync period (wo) */
+#define        esp_syncperiod esp_step
+       u_char  esp_xxx6[3];
+       u_char  esp_fflags;     /* fifo flags (ro); sync offset (wo) */
+#define        esp_syncoff esp_fflags
+       u_char  esp_xxx7[3];
+       u_char  esp_conf1;      /* configuration #1 (rw) */
+       u_char  esp_xxx8[3];
+       u_char  esp_ccf;        /* clock conversion factor (wo) */
+       u_char  esp_xxx9[3];
+       u_char  esp_test;       /* test (do not use) */
+       u_char  esp_xxxA[3];
+       u_char  esp_conf2;      /* configuration #2 (rw) */
+       u_char  esp_xxxB[3];
+       u_char  esp_conf3;      /* configuration #3 (rw, ESP-236) */
+       u_char  esp_xxxC[3];
+};
+
+/* THE REST OF THESE NAMES COULD STAND TO BE SHORTENED */
+
+/*
+ * Bits in esp_cmd.  Note that the cmd register is two levels deep (see
+ * Emulex documentation, p. 4-3); our typical usage is to set the command,
+ * then set it again with DMA.
+ * 
+ * Targets will use disconnected and target mode commands; initiators will use
+ * disconnected and initiator mode commands. Bit 0x40 indicates disconnected
+ * mode, 0x20 target mode, and 0x10 initiator mode.  (However, everyone can
+ * use the miscellaneous commands, which have none of those bits set.)
+ */
+#define        ESPCMD_DMA              0x80    /* flag => do DMA */
+
+/* miscellaneous */
+#define        ESPCMD_NOP              0x00    /* do nothing */
+#define        ESPCMD_FLUSH_FIFO       0x01    /* flush FIFO */
+#define        ESPCMD_RESET_CHIP       0x02    /* reset ESP chip */
+#define        ESPCMD_RESET_BUS        0x03    /* reset SCSI bus */
+
+/* disconnected */
+#define        ESPCMD_RESEL_SEQ        0x40    /* reselect sequence */
+#define        ESPCMD_SEL_NATN         0x41    /* select without ATN sequence */
+#define        ESPCMD_SEL_ATN          0x42    /* select with ATN sequence */
+#define        ESPCMD_SEL_ATNS         0x43    /* select with ATN & stop seq */
+#define        ESPCMD_SEL_ENA          0x44    /* enable selection/reselection */
+#define        ESPCMD_SEL_DIS          0x45    /* disable selection/reselection */
+#define        ESPCMD_SEL_ATN3         0x46    /* select with ATN3 sequence */
+
+/* target state */
+#define        ESPCMD_SEND_MSG         0x20    /* send message */
+#define        ESPCMD_SEND_STATUS      0x21    /* send status */
+#define        ESPCMD_SEND_DATA        0x22    /* send data */
+#define        ESPCMD_DIS_SEQ          0x23    /* disconnect sequence */
+#define        ESPCMD_TERM_SEQ         0x24    /* terminate sequence */
+#define        ESPCMD_TARG_COMP        0x25    /* target command complete sequence */
+#define        ESPCMD_DISCONNECT       0x27    /* disconnect */
+#define        ESPCMD_RCV_MSG          0x28    /* receive message sequence */
+#define        ESPCMD_RCV_CMD          0x29    /* receive command */
+#define        ESPCMD_RCV_DATA         0x2a    /* receive data */
+#define        ESPCMD_REC_CMD_SEQ      0x2b    /* receive command sequence */
+#define        ESPCMD_STOP_DMA         0x04    /* stop DMA (see p. 4-6) */
+/*     ESPCMD_TARG_ABORT       0x06       target abort sequence */
+
+/* initiator state */
+#define        ESPCMD_XFER_INFO        0x10    /* transfer information */
+#define        ESPCMD_INIT_COMP        0x11    /* initiator command complete seq */
+#define        ESPCMD_MSG_ACCEPT       0x12    /* message accepted */
+#define        ESPCMD_XFER_PAD         0x18    /* transfer pad (use only w/ DMA) */
+#define        ESPCMD_SET_ATN          0x1a    /* set ATN */
+#define        ESPCMD_RESET_ATN        0x1b    /* reset ATN */
+
+/*
+ * Bits in esp_stat.
+ * Bits 3 through 7 are latched until esp_intr is read;
+ * bits 0 through 2 (the phase) are not normally latched.
+ * The interrupt bit is set even if interrupts are disabled.
+ * Hardware or software reset, or reading esp_intr, will
+ * clear the interrupt and turn off ESPSTAT_INT.
+ */
+#ifdef notdef
+#define        ESPSTAT_INT             0x80    /* ASC interrupting processor */
+#else
+#define        ESPSTAT_XXX             0x80    /* rumored unreliable: use dma IP */
+#endif
+#define        ESPSTAT_GE              0x40    /* gross error */
+#define        ESPSTAT_PE              0x20    /* parity error */
+#define        ESPSTAT_ERR             0x60    /* pseudo composite */
+#define        ESPSTAT_TC              0x10    /* terminal count */
+#define        ESPSTAT_VGC             0x08    /* valid group code */
+#define        ESPSTAT_MSG             0x04    /* MSG line from SCSI bus */
+#define        ESPSTAT_CD              0x02    /* CD line from SCSI bus */
+#define        ESPSTAT_IO              0x01    /* IO line from SCSI bus */
+#define        ESPSTAT_PHASE           7       /* phase mask */
+#define        ESPPHASE_DATA_OUT       0       /* data out */
+#define        ESPPHASE_DATA_IN        1       /* data in */
+#define        ESPPHASE_CMD            2       /* command */
+#define        ESPPHASE_STATUS         3       /* status */
+#define        ESPPHASE_MSG_OUT        6       /* message out (w.r.t. initiator) */
+#define        ESPPHASE_MSG_IN         7       /* message in */
+
+#ifdef ESP_PHASE_NAMES
+/* printed as `... during %s phase' */
+char   *espphases[] =
+    { "data out", "data in", "command", "status",
+      "impossible(4)", "impossible(5)", "message out", "message in" };
+#endif
+
+#define        ESPSTAT_BITS    "\20\10INT\7GE\6PE\5TC\4VGC\3MSG\2CD\1IO"
+
+/*
+ * Bits in esp_intr.
+ */
+#define        ESPINTR_SBR     0x80    /* SCSI bus reset detected */
+#define        ESPINTR_ILC     0x40    /* illegal command */
+#define        ESPINTR_DSC     0x20    /* target disconnected, or timeout */
+#define        ESPINTR_SVC     0x10    /* a device wants bus service */
+#define        ESPINTR_CMP     0x08    /* function complete */
+#define        ESPINTR_RSL     0x04    /* reselected */
+#define        ESPINTR_SAT     0x02    /* selected with ATN */
+#define        ESPINTR_SEL     0x01    /* selected (no ATN) */
+
+#define        ESPINTR_BITS "\20\10SBR\7ILC\6DSC\5SVC\4CMP\3RSL\2SAT\1SEL"
+
+/*
+ * Formula for select/reselect timeout (esp_timeout).
+ *     TU = 7682 * CCF * TCP
+ *     T / TU = register value
+ *     CCF = clock conversion factor
+ *     TCP = input clock period (in same units as T)
+ *     TU = time unit (i.e., the esp_timeout register counts in TUs)
+ *     T = desired timeout
+ * (i.e., we want ceil(timeout / (7682*ccf*tcp))).  If timeout is in ms.,
+ * and tcp is in MHz, then (ccf * 7682)/tcp gives us 1000*TU, and
+ * 1000*timeout/(1000*TU) gives us our result (but remember to round up).
+ *
+ * N.B.: The register value 0 gives a TU of 256.
+ */
+#define        ESPTIMO_REGVAL(timo_ms, ccf, mhz) \
+       howmany(1000 * (timo_ms), ((ccf) * 7682) / (mhz))
+
+/*
+ * Value in esp_step.  These tell us how much of a `sequence' completed,
+ * and apply to the following sequenced operations:
+ *  [initiator]
+ *     select without ATN
+ *     select with ATN
+ *     select with ATN3
+ *     select with ATN and stop
+ *  [target]
+ *     bus-initiated select with ATN
+ *     bus-initiated select
+ *     receive command sequence
+ *     command complete sequence
+ *     disconnect sequence
+ *     terminate sequence
+ * The actual values are too complicated to define here, except that
+ * code 4 always means `everything worked and the command went out'
+ * (and is thus typical for everything except ATN-and-stop).
+ */
+#define        ESPSTEP_MASK            0x07    /* only these bits are valid */
+#define        ESPSTEP_DONE            4       /* command went out */
+
+/*
+ * Synchronous transfer period (esp_syncper, 5 bits).
+ * The minimum clocks-per-period is 5 and the max is 35;
+ * the default on reset is 5.  Note that a period value of 4
+ * actually gives 5 clocks.
+ */
+#define        ESP_CLOCKS_TO_PERIOD(nclocks) ((nclocks) & 31)
+
+/*
+ * Bits in fifo flags (esp_fflags) register.  The FIFO itself
+ * is only 16 bytes, so the byte count fits in 5 bits.  Normally
+ * a copy of the sequence step register appears in the top 3 bits,
+ * but in test mode the chip re-uses one of those for a synchronous
+ * offset bit.
+ */
+#define        ESP_NFIFO(esp)  ((esp)->esp_fflags & 0x1f)
+#define        ESP_FFSTEP(esp) (((esp)->esp_fflags >> 5) & 3)
+
+#define        ESPFFLAGS_TM_SOFFNZ     0x20    /* nonzero sync offset (test mode) */
+
+/*
+ * Bits in esp_conf1.
+ */
+#define        ESPCONF1_SLOW_CABLE     0x80    /* ``slow cable'' mode */
+#define        ESPCONF1_REPORT         0x40    /* disable reporting of interrupts
+                                          from scsi bus reset command */
+#define        ESPCONF1_PARTST         0x20    /* parity test mode */
+#define        ESPCONF1_PARENB         0x10    /* enable parity */
+#define        ESPCONF1_TEST           0x08    /* chip test mode */
+#define        ESPCONF1_ID_MASK        0x07    /* SCSI bus ID field */
+
+#define        ESPCONF1_BITS   "\20\10SLOW_CABLE\7REPORT\6PARTST\5PARENB\4TEST"
+
+/*
+ * Values for clock conversion factor (esp_ccf).
+ */
+#define        ESPCCF_FROMMHZ(mhz)     (((mhz) + 4) / 5)
+#define        ESPCCF_MIN              2       /* minimum CCF value */
+
+/*
+ * Bits in esp_test (for board testing only; can only be used in test mode).
+ */
+#define        ESPTEST_MBZ             0xf8    /* reserved; must be 0 */
+#define        ESPTEST_TRISTATE        0x04    /* all output pins tristated */
+#define        ESPTEST_INITIATOR       0x02    /* operate as initiator */
+#define        ESPTEST_TARGET          0x01    /* operate as target */
+
+/*
+ * Bits in esp_conf2.
+ */
+#define        ESPCONF2_RSVD           0xe0    /* reserved */
+#define        ESPCONF2_TRISTATE_DMA   0x10    /* tristate the DMA REQ pin */
+#define        ESPCONF2_SCSI2          0x08    /* enable SCSI 2 (see p. 4-18) */
+#define        ESPCONF2_TBPA           0x04    /* enable target bad parity abort */
+#define        ESPCONF2_RPE            0x02    /* register parity ena (ESP2xx only) */
+#define        ESPCONF2_DPE            0x01    /* DMA parity enable (ESP2xx only) */
diff --git a/usr/src/sys/sparc/sbus/if_le.c b/usr/src/sys/sparc/sbus/if_le.c
new file mode 100644 (file)
index 0000000..60a5a69
--- /dev/null
@@ -0,0 +1,1024 @@
+/*-
+ * Copyright (c) 1982, 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ *
+ *     @(#)if_le.c     7.1 (Berkeley) %G%
+ *
+ * from: $Header: if_le.c,v 1.17 92/07/10 06:45:17 torek Exp $
+ */
+
+#include "bpfilter.h"
+
+/*
+ * AMD 7990 LANCE
+ *
+ * This driver will generate and accept tailer encapsulated packets even
+ * though it buys us nothing.  The motivation was to avoid incompatibilities
+ * with VAXen, SUNs, and others that handle and benefit from them.
+ * This reasoning is dubious.
+ */
+#include "sys/param.h"
+#include "sys/device.h"
+#include "sys/systm.h"
+#include "sys/kernel.h"
+#include "sys/mbuf.h"
+#include "sys/buf.h"
+#include "sys/socket.h"
+#include "sys/syslog.h"
+#include "sys/ioctl.h"
+#include "sys/malloc.h"
+#include "sys/errno.h"
+
+#include "../net/if.h"
+#include "../net/netisr.h"
+#include "../net/route.h"
+#if NBPFILTER > 0
+#include "sys/select.h"
+#include "../net/bpf.h"
+#include "../net/bpfdesc.h"
+#endif
+
+#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
+
+#ifdef APPLETALK
+#include "../netddp/atalk.h"
+#endif
+
+#include "machine/autoconf.h"
+#include "machine/cpu.h"
+#include "machine/pmap.h"
+
+#include "if_lereg.h"
+#include "sbusvar.h"
+
+/* DVMA address to LANCE address -- the Sbus/MMU will resupply the 0xff */
+#define        LANCE_ADDR(x)   ((int)(x) & ~0xff000000)
+
+int    ledebug = 0;            /* console error messages */
+
+#ifdef PACKETSTATS
+long   lexpacketsizes[LEMTU+1];
+long   lerpacketsizes[LEMTU+1];
+#endif
+
+/* Per interface statistics */
+/* XXX this should go in something like if_levar.h */
+struct lestats {
+       long    lexints;        /* transmitter interrupts */
+       long    lerints;        /* receiver interrupts */
+       long    lerbufs;        /* total buffers received during interrupts */
+       long    lerhits;        /* times current rbuf was full */
+       long    lerscans;       /* rbufs scanned before finding first full */
+};
+
+/*
+ * Ethernet software status per interface.
+ *
+ * Each interface is referenced by a network interface structure,
+ * le_if, which the routing code uses to locate the interface.
+ * This structure contains the output queue for the interface, its address, ...
+ */
+struct le_softc {
+       struct  device sc_dev;          /* base device */
+       struct  sbusdev sc_sd;          /* sbus device */
+       struct  intrhand sc_ih;         /* interrupt vectoring */
+       int     sc_interrupts;          /* number of interrupts taken */
+
+       struct  arpcom sc_ac;           /* common Ethernet structures */
+#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 */
+       int     sc_rmd;                 /* predicted next rmd to process */
+       int     sc_runt;
+       int     sc_jab;
+       int     sc_merr;
+       int     sc_babl;
+       int     sc_cerr;
+       int     sc_miss;
+       int     sc_xint;
+       int     sc_xown;
+       int     sc_uflo;
+       int     sc_rxlen;
+       int     sc_rxoff;
+       int     sc_txoff;
+       int     sc_busy;
+       short   sc_iflags;
+       struct  lestats sc_lestats;     /* per interface statistics */
+#if NBPFILTER > 0
+       caddr_t sc_bpf;
+#endif
+};
+
+
+/* autoconfiguration driver */
+void   leattach(struct device *, struct device *, void *);
+struct cfdriver lecd =
+    { NULL, "le", matchbyname, leattach, DV_IFNET, sizeof(struct le_softc) };
+
+/* Forwards */
+void   leattach(struct device *, struct device *, void *);
+#ifdef MULTICAST
+void   lesetladrf(struct le_softc *);
+#endif
+void   lereset(struct device *);
+int    leinit(int);
+int    lestart(struct ifnet *);
+int    leintr(void *);
+void   lexint(struct le_softc *);
+void   lerint(struct le_softc *);
+void   leread(struct le_softc *, char *, int);
+int    leput(char *, struct mbuf *);
+struct mbuf *leget(char *, int, int, struct ifnet *);
+int    leioctl(struct ifnet *, int, caddr_t);
+void   leerror(struct le_softc *, int);
+void   lererror(struct le_softc *, char *);
+void   lexerror(struct le_softc *);
+
+/*
+ * Interface exists: make available by filling in network interface
+ * record.  System will initialize the interface when it is ready
+ * to accept packets.
+ */
+void
+leattach(parent, self, args)
+       struct device *parent;
+       struct device *self;
+       void *args;
+{
+       register struct le_softc *sc = (struct le_softc *)self;
+       register struct sbus_attach_args *sa = args;
+       register volatile struct lereg2 *ler2;
+       struct ifnet *ifp = &sc->sc_if;
+       register int a, pri;
+#define        ISQUADALIGN(a) ((((long) a) & 0x3) == 0)
+
+       /* XXX the following declarations should be elsewhere */
+       extern void myetheraddr(u_char *);
+       extern caddr_t dvma_malloc(size_t);
+
+       if (sa->sa_ra.ra_nintr != 1) {
+               printf(": expected 1 interrupt, got %d\n", sa->sa_ra.ra_nintr);
+               return;
+       }
+       pri = sa->sa_ra.ra_intr[0].int_pri;
+       printf(" pri %d", pri);
+       sc->sc_r1 = (volatile struct lereg1 *)
+           mapiodev(sa->sa_ra.ra_paddr, sizeof(struct lereg1));
+       ler2 = sc->sc_r2 = (volatile struct lereg2 *)
+           dvma_malloc(sizeof(struct lereg2));
+if (!ISQUADALIGN(ler2))
+       printf("? not quad aligned (0x%x)\n", ler2);
+
+       myetheraddr(sc->sc_addr);
+       printf(": hardware address %s\n", ether_sprintf(sc->sc_addr));
+
+       /*
+        * Setup for transmit/receive
+        *
+        * According to Van, some versions of the Lance only use this
+        * address to receive packets; it doesn't put them in
+        * output packets. We'll want to make sure that lestart()
+        * installs the address.
+        */
+       ler2->ler2_padr[0] = sc->sc_addr[1];
+       ler2->ler2_padr[1] = sc->sc_addr[0];
+       ler2->ler2_padr[2] = sc->sc_addr[3];
+       ler2->ler2_padr[3] = sc->sc_addr[2];
+       ler2->ler2_padr[4] = sc->sc_addr[5];
+       ler2->ler2_padr[5] = sc->sc_addr[4];
+       a = LANCE_ADDR(&ler2->ler2_rmd);
+if (!ISQUADALIGN(a))
+       printf("rdra not quad aligned (0x%x)\n", a);
+       ler2->ler2_rlen = LE_RLEN | (a >> 16);
+       ler2->ler2_rdra = a;
+       a = LANCE_ADDR(&ler2->ler2_tmd);
+if (!ISQUADALIGN(a))
+       printf("tdra not quad aligned (0x%x)\n", a);
+       ler2->ler2_tlen = LE_TLEN | (a >> 16);
+       ler2->ler2_tdra = a;
+
+       /*
+        * Link into sbus, and establish interrupt handler.
+        */
+       sc->sc_sd.sd_reset = lereset;
+       sbus_establish(&sc->sc_sd, &sc->sc_dev);
+       sc->sc_ih.ih_fun = leintr;
+       sc->sc_ih.ih_arg = sc;
+       intr_establish(pri, &sc->sc_ih);
+
+       ifp->if_unit = sc->sc_dev.dv_unit;
+       ifp->if_name = "le";
+       ifp->if_mtu = ETHERMTU;
+       ifp->if_init = leinit;
+       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;
+#endif
+#ifdef IFF_NOTRAILERS
+       /* XXX still compile when the blasted things are gone... */
+       ifp->if_flags |= IFF_NOTRAILERS;
+#endif
+#if NBPFILTER > 0
+       bpfattach(&sc->sc_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+       if_attach(ifp);
+}
+
+#ifdef MULTICAST
+/*
+ * Setup the logical address filter
+ */
+void
+lesetladrf(sc)
+       register struct le_softc *sc;
+{
+       register volatile struct lereg2 *ler2 = sc->sc_r2;
+       register struct ifnet *ifp = &sc->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->ler2_ladrf[0] = 0;
+       ler2->ler2_ladrf[1] = 0;
+       ifp->if_flags &= ~IFF_ALLMULTI;
+       ETHER_FIRST_MULTI(step, &sc->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->ler2_ladrf[0] = 0xffffffff;
+                       ler2->ler2_ladrf[1] = 0xffffffff;
+                       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. */
+               ler2->ler2_ladrf[crc >> 5] |= 1 << (crc & 0x1f);
+
+               ETHER_NEXT_MULTI(step, enm);
+       }
+}
+#endif
+
+void
+lereset(dev)
+       struct device *dev;
+{
+       register struct le_softc *sc = (struct le_softc *)dev;
+       register volatile struct lereg1 *ler1 = sc->sc_r1;
+       register volatile struct lereg2 *ler2 = sc->sc_r2;
+       register int i, a, timo, stat;
+
+#if NBPFILTER > 0
+       if (sc->sc_if.if_flags & IFF_PROMISC)
+               ler2->ler2_mode = LE_MODE_NORMAL | LE_MODE_PROM;
+       else
+#endif
+               ler2->ler2_mode = LE_MODE_NORMAL;
+       ler1->ler1_rap = LE_CSR0;
+       ler1->ler1_rdp = LE_C0_STOP;
+
+       /* Setup the logical address filter */
+#ifdef MULTICAST
+       lesetladrf(sc);
+#else
+       ler2->ler2_ladrf[0] = 0;
+       ler2->ler2_ladrf[1] = 0;
+#endif
+
+       /* init receive and transmit rings */
+a = LANCE_ADDR(&ler2->ler2_rbuf[0][0]);
+if (!ISQUADALIGN(a))
+       printf("rbuf not quad aligned (0x%x)\n", a);
+       for (i = 0; i < LERBUF; i++) {
+               a = LANCE_ADDR(&ler2->ler2_rbuf[i][0]);
+               ler2->ler2_rmd[i].rmd0 = a;
+               ler2->ler2_rmd[i].rmd1_hadr = a >> 16;
+               ler2->ler2_rmd[i].rmd1_bits = LE_R1_OWN;
+               ler2->ler2_rmd[i].rmd2 = -LEMTU;
+               ler2->ler2_rmd[i].rmd3 = 0;
+       }
+a = LANCE_ADDR(&ler2->ler2_tbuf[0][0]);
+if (!ISQUADALIGN(a))
+       printf("tbuf not quad aligned (0x%x)\n", a);
+       for (i = 0; i < LETBUF; i++) {
+               a = LANCE_ADDR(&ler2->ler2_tbuf[i][0]);
+               ler2->ler2_tmd[i].tmd0 = a;
+               ler2->ler2_tmd[i].tmd1_hadr = a >> 16;
+               ler2->ler2_tmd[i].tmd1_bits = 0;
+               ler2->ler2_tmd[i].tmd2 = 0;
+               ler2->ler2_tmd[i].tmd3 = 0;
+       }
+
+bzero(&ler2->ler2_rbuf[0][0], (LERBUF + LETBUF) * LEMTU);
+       /* lance will stuff packet into receive buffer 0 next */
+       sc->sc_rmd = 0;
+
+       /* tell the chip where to find the initialization block */
+       a = LANCE_ADDR(&ler2->ler2_mode);
+       ler1->ler1_rap = LE_CSR1;
+       ler1->ler1_rdp = a;
+       ler1->ler1_rap = LE_CSR2;
+       ler1->ler1_rdp = a >> 16;
+       ler1->ler1_rap = LE_CSR3;
+       ler1->ler1_rdp = LE_C3_BSWP | LE_C3_ACON | LE_C3_BCON;
+       ler1->ler1_rap = LE_CSR0;
+       ler1->ler1_rdp = LE_C0_INIT;
+       timo = 100000;
+       while (((stat = ler1->ler1_rdp) & (LE_C0_ERR | LE_C0_IDON)) == 0) {
+               if (--timo == 0) {
+                       printf("%s: init timeout, stat=%b\n",
+                           sc->sc_dev.dv_xname, stat, LE_C0_BITS);
+                       break;
+               }
+       }
+       if (stat & LE_C0_ERR)
+               printf("%s: init failed, stat=%b\n",
+                   sc->sc_dev.dv_xname, stat, LE_C0_BITS);
+       else
+               ler1->ler1_rdp = LE_C0_IDON;    /* clear IDON */
+       ler1->ler1_rdp = LE_C0_STRT | LE_C0_INEA;
+       sc->sc_if.if_flags &= ~IFF_OACTIVE;
+}
+
+/*
+ * Initialization of interface
+ */
+int
+leinit(unit)
+       int unit;
+{
+       register struct le_softc *sc = lecd.cd_devs[unit];
+       register struct ifnet *ifp = &sc->sc_if;
+       register int s;
+
+       /* not yet, if address still unknown */
+       if (ifp->if_addrlist == (struct ifaddr *)0)
+               return (0);
+       if ((ifp->if_flags & IFF_RUNNING) == 0) {
+               s = splimp();
+               ifp->if_flags |= IFF_RUNNING;
+               lereset((struct device *)sc);
+               lestart(ifp);
+               splx(s);
+       }
+       return (0);
+}
+
+/*
+ * Start output on interface.  Get another datagram to send
+ * off of the interface queue, and copy it to the interface
+ * before starting the output.
+ */
+int
+lestart(ifp)
+       register struct ifnet *ifp;
+{
+       register struct le_softc *sc = lecd.cd_devs[ifp->if_unit];
+       register volatile struct letmd *tmd;
+       register struct mbuf *m;
+       register int len;
+
+       if ((sc->sc_if.if_flags & IFF_RUNNING) == 0)
+               return (0);
+       IF_DEQUEUE(&sc->sc_if.if_snd, m);
+       if (m == 0)
+               return (0);
+       len = leput(sc->sc_r2->ler2_tbuf[0], m);
+#if NBPFILTER > 0
+       /*
+        * If bpf is listening on this interface, let it
+        * see the packet before we commit it to the wire.
+        */
+       if (sc->sc_bpf)
+               bpf_tap(sc->sc_bpf, sc->sc_r2->ler2_tbuf[0], len);
+#endif
+
+#ifdef PACKETSTATS
+       if (len <= LEMTU)
+               lexpacketsizes[len]++;
+#endif
+       tmd = sc->sc_r2->ler2_tmd;
+       tmd->tmd3 = 0;
+       tmd->tmd2 = -len;
+       tmd->tmd1_bits = LE_T1_OWN | LE_T1_STP | LE_T1_ENP;
+       sc->sc_if.if_flags |= IFF_OACTIVE;
+       return (0);
+}
+
+int
+leintr(dev)
+       register void *dev;
+{
+       register struct le_softc *sc = dev;
+       register volatile struct lereg1 *ler1 = sc->sc_r1;
+       register int csr0;
+
+       csr0 = ler1->ler1_rdp;
+       if ((csr0 & LE_C0_INTR) == 0)
+               return (0);
+       sc->sc_interrupts++;
+
+       if (csr0 & LE_C0_ERR) {
+               leerror(sc, csr0);
+               if (csr0 & LE_C0_MERR) {
+                       sc->sc_merr++;
+                       lereset((struct device *)sc);
+                       return (1);
+               }
+               if (csr0 & LE_C0_BABL)
+                       sc->sc_babl++;
+               if (csr0 & LE_C0_CERR)
+                       sc->sc_cerr++;
+               if (csr0 & LE_C0_MISS)
+                       sc->sc_miss++;
+               ler1->ler1_rdp = LE_C0_BABL|LE_C0_CERR|LE_C0_MISS|LE_C0_INEA;
+       }
+       if ((csr0 & LE_C0_RXON) == 0) {
+               sc->sc_rxoff++;
+               lereset((struct device *)sc);
+               return (1);
+       }
+       if ((csr0 & LE_C0_TXON) == 0) {
+               sc->sc_txoff++;
+               lereset((struct device *)sc);
+               return (1);
+       }
+       if (csr0 & LE_C0_RINT) {
+               /* interrupt is cleared in lerint */
+               lerint(sc);
+       }
+       if (csr0 & LE_C0_TINT) {
+               ler1->ler1_rdp = LE_C0_TINT|LE_C0_INEA;
+               lexint(sc);
+       }
+       return (1);
+}
+
+/*
+ * Ethernet interface transmitter interrupt.
+ * Start another output if more data to send.
+ */
+void
+lexint(sc)
+       register struct le_softc *sc;
+{
+       register volatile struct letmd *tmd = sc->sc_r2->ler2_tmd;
+
+       sc->sc_lestats.lexints++;
+       if ((sc->sc_if.if_flags & IFF_OACTIVE) == 0) {
+               sc->sc_xint++;
+               return;
+       }
+       if (tmd->tmd1_bits & LE_T1_OWN) {
+               sc->sc_xown++;
+               return;
+       }
+       if (tmd->tmd1_bits & LE_T1_ERR) {
+err:
+               lexerror(sc);
+               sc->sc_if.if_oerrors++;
+               if (tmd->tmd3 & (LE_T3_BUFF|LE_T3_UFLO)) {
+                       sc->sc_uflo++;
+                       lereset((struct device *)sc);
+               } else if (tmd->tmd3 & LE_T3_LCOL)
+                       sc->sc_if.if_collisions++;
+               else if (tmd->tmd3 & LE_T3_RTRY)
+                       sc->sc_if.if_collisions += 16;
+       }
+       else if (tmd->tmd3 & LE_T3_BUFF)
+               /* XXX documentation says BUFF not included in ERR */
+               goto err;
+       else if (tmd->tmd1_bits & LE_T1_ONE)
+               sc->sc_if.if_collisions++;
+       else if (tmd->tmd1_bits & LE_T1_MORE)
+               /* what is the real number? */
+               sc->sc_if.if_collisions += 2;
+       else
+               sc->sc_if.if_opackets++;
+       sc->sc_if.if_flags &= ~IFF_OACTIVE;
+       lestart(&sc->sc_if);
+}
+
+#define        LENEXTRMP \
+       if (++bix == LERBUF) bix = 0, rmd = sc->sc_r2->ler2_rmd; else ++rmd
+
+/*
+ * Ethernet interface receiver interrupt.
+ * If input error just drop packet.
+ * Decapsulate packet based on type and pass to type specific
+ * higher-level input routine.
+ */
+void
+lerint(sc)
+       register struct le_softc *sc;
+{
+       register int bix = sc->sc_rmd;
+       register volatile struct lermd *rmd = &sc->sc_r2->ler2_rmd[bix];
+
+       sc->sc_lestats.lerints++;
+       /*
+        * Out of sync with hardware, should never happen?
+        */
+       if (rmd->rmd1_bits & LE_R1_OWN) {
+               do {
+                       sc->sc_lestats.lerscans++;
+                       LENEXTRMP;
+               } while ((rmd->rmd1_bits & LE_R1_OWN) && bix != sc->sc_rmd);
+               if (bix == sc->sc_rmd)
+                       printf("%s: RINT with no buffer\n",
+                           sc->sc_dev.dv_xname);
+       } else
+               sc->sc_lestats.lerhits++;
+
+       /*
+        * Process all buffers with valid data
+        */
+       while ((rmd->rmd1_bits & LE_R1_OWN) == 0) {
+               int len = rmd->rmd3;
+
+               /* Clear interrupt to avoid race condition */
+               sc->sc_r1->ler1_rdp = LE_C0_RINT|LE_C0_INEA;
+
+               if (rmd->rmd1_bits & LE_R1_ERR) {
+                       sc->sc_rmd = bix;
+                       lererror(sc, "bad packet");
+                       sc->sc_if.if_ierrors++;
+               } else if ((rmd->rmd1_bits & (LE_R1_STP|LE_R1_ENP)) !=
+                   (LE_R1_STP|LE_R1_ENP)) {
+                       /* XXX make a define for LE_R1_STP|LE_R1_ENP? */
+                       /*
+                        * Find the end of the packet so we can see how long
+                        * it was.  We still throw it away.
+                        */
+                       do {
+                               sc->sc_r1->ler1_rdp = LE_C0_RINT|LE_C0_INEA;
+                               rmd->rmd3 = 0;
+                               rmd->rmd1_bits = LE_R1_OWN;
+                               LENEXTRMP;
+                       } while (!(rmd->rmd1_bits &
+                           (LE_R1_OWN|LE_R1_ERR|LE_R1_STP|LE_R1_ENP)));
+                       sc->sc_rmd = bix;
+                       lererror(sc, "chained buffer");
+                       sc->sc_rxlen++;
+                       /*
+                        * If search terminated without successful completion
+                        * we reset the hardware (conservative).
+                        */
+                       if ((rmd->rmd1_bits &
+                           (LE_R1_OWN|LE_R1_ERR|LE_R1_STP|LE_R1_ENP)) !=
+                           LE_R1_ENP) {
+                               lereset((struct device *)sc);
+                               return;
+                       }
+               } else {
+                       leread(sc, sc->sc_r2->ler2_rbuf[bix], len);
+#ifdef PACKETSTATS
+                       lerpacketsizes[len]++;
+#endif
+                       sc->sc_lestats.lerbufs++;
+               }
+               rmd->rmd3 = 0;
+               rmd->rmd1_bits = LE_R1_OWN;
+               LENEXTRMP;
+       }
+       sc->sc_rmd = bix;
+}
+
+void
+leread(sc, pkt, len)
+       register struct le_softc *sc;
+       char *pkt;
+       int len;
+{
+       register struct ether_header *et;
+       register struct ifnet *ifp = &sc->sc_if;
+       struct mbuf *m;
+       struct ifqueue *inq;
+       int flags;
+
+       ifp->if_ipackets++;
+       et = (struct ether_header *)pkt;
+       et->ether_type = ntohs((u_short)et->ether_type);
+       /* adjust input length to account for header and CRC */
+       len -= sizeof(struct ether_header) + 4;
+
+       if (len <= 0) {
+               if (ledebug)
+                       log(LOG_WARNING,
+                           "%s: ierror(runt packet): from %s: len=%d\n",
+                           sc->sc_dev.dv_xname,
+                           ether_sprintf(et->ether_shost), len);
+               sc->sc_runt++;
+               ifp->if_ierrors++;
+               return;
+       }
+
+       /* Setup mbuf flags we'll need later */
+       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;
+
+#if NBPFILTER > 0
+       /*
+        * Check if there's a bpf filter listening on this interface.
+        * If so, hand off the raw packet to enet.
+        */
+       if (sc->sc_bpf) {
+               bpf_tap(sc->sc_bpf, pkt, len + sizeof(struct ether_header));
+
+               /*
+                * Keep the packet if it's a broadcast or has our
+                * physical ethernet address (or if we support
+                * multicast and it's one).
+                */
+               if (
+#ifdef MULTICAST
+                   (flags & (M_BCAST | M_MCAST)) == 0 &&
+#else
+                   (flags & M_BCAST) == 0 &&
+#endif
+                   bcmp(et->ether_dhost, sc->sc_addr,
+                       sizeof(et->ether_dhost)) != 0)
+                       return;
+       }
+#endif
+       m = leget(pkt, len, 0, ifp);
+       if (m == 0)
+               return;
+
+       /* XXX this code comes from ether_input() */
+       ifp->if_lastchange = time;
+       ifp->if_ibytes += m->m_pkthdr.len + sizeof (*et);
+       if (flags) {
+               m->m_flags |= flags;
+               ifp->if_imcasts++;
+       }
+       /* XXX end of code from ether_input() */
+
+       switch (et->ether_type) {
+
+#ifdef INET
+       case ETHERTYPE_IP:
+               schednetisr(NETISR_IP);
+               inq = &ipintrq;
+               break;
+
+       case ETHERTYPE_ARP:
+               schednetisr(NETISR_ARP);
+               inq = &arpintrq;
+               break;
+#endif
+#ifdef NS
+       case ETHERTYPE_NS:
+               schednetisr(NETISR_NS);
+               inq = &nsintrq;
+               break;
+#endif
+
+#ifdef UTAHONLY
+#ifdef APPLETALK
+       case ETHERTYPE_APPLETALK:
+               schednetisr(NETISR_DDP);
+               inq = &ddpintq;
+               break;
+
+       case ETHERTYPE_AARP:
+               aarpinput(&sc->sc_ac, m);
+               return;
+#endif
+#endif
+       default:
+               m_freem(m);
+               return;
+       }
+
+       if (IF_QFULL(inq)) {
+               IF_DROP(inq);
+               m_freem(m);
+               return;
+       }
+       IF_ENQUEUE(inq, m);
+}
+
+/*
+ * Routine to copy from mbuf chain to transmit
+ * buffer in board local memory.
+ *
+ * ### this can be done by remapping in some cases
+ */
+int
+leput(lebuf, m)
+       register char *lebuf;
+       register struct mbuf *m;
+{
+       register struct mbuf *mp;
+       register int len, tlen = 0;
+
+       for (mp = m; mp; mp = mp->m_next) {
+               len = mp->m_len;
+               if (len == 0)
+                       continue;
+               tlen += len;
+               bcopy(mtod(mp, char *), lebuf, len);
+               lebuf += len;
+       }
+       m_freem(m);
+       if (tlen < LEMINSIZE) {
+               bzero(lebuf, LEMINSIZE - tlen);
+               tlen = LEMINSIZE;
+       }
+       return (tlen);
+}
+
+/*
+ * Routine to copy from board local memory into mbufs.
+ */
+struct mbuf *
+leget(lebuf, totlen, off0, ifp)
+       char *lebuf;
+       int totlen, off0;
+       struct ifnet *ifp;
+{
+       register struct mbuf *m;
+       struct mbuf *top = 0, **mp = &top;
+       register int off = off0, len;
+       register char *cp;
+       char *epkt;
+
+       lebuf += sizeof(struct ether_header);
+       cp = lebuf;
+       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;
+
+       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 = lebuf;
+       }
+       return (top);
+}
+
+/*
+ * Process an ioctl request.
+ */
+int
+leioctl(ifp, cmd, data)
+       register struct ifnet *ifp;
+       int cmd;
+       caddr_t data;
+{
+       register struct ifaddr *ifa;
+       register struct le_softc *sc = lecd.cd_devs[ifp->if_unit];
+       register volatile struct lereg1 *ler1;
+       int s = splimp(), error = 0;
+
+       switch (cmd) {
+
+       case SIOCSIFADDR:
+               ifa = (struct ifaddr *)data;
+               ifp->if_flags |= IFF_UP;
+               switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+               case AF_INET:
+                       (void)leinit(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->sc_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)sc->sc_addr, sizeof(sc->sc_addr));
+                       }
+                       (void)leinit(ifp->if_unit);     /* does le_setaddr() */
+                       break;
+                   }
+#endif
+               default:
+                       (void)leinit(ifp->if_unit);
+                       break;
+               }
+               break;
+
+       case SIOCSIFFLAGS:
+               ler1 = sc->sc_r1;
+               if ((ifp->if_flags & IFF_UP) == 0 &&
+                   ifp->if_flags & IFF_RUNNING) {
+                       ler1->ler1_rdp = LE_C0_STOP;
+                       ifp->if_flags &= ~IFF_RUNNING;
+               } else if (ifp->if_flags & IFF_UP &&
+                   (ifp->if_flags & IFF_RUNNING) == 0)
+                       (void)leinit(ifp->if_unit);
+               /*
+                * If the state of the promiscuous bit changes, the interface
+                * must be reset to effect the change.
+                */
+               if (((ifp->if_flags ^ sc->sc_iflags) & IFF_PROMISC) &&
+                   (ifp->if_flags & IFF_RUNNING)) {
+                       sc->sc_iflags = ifp->if_flags;
+                       lereset((struct device *)sc);
+                       lestart(ifp);
+               }
+               break;
+
+#ifdef MULTICAST
+       case SIOCADDMULTI:
+       case SIOCDELMULTI:
+               /* Update our multicast list  */
+               error = (cmd == SIOCADDMULTI) ?
+                   ether_addmulti((struct ifreq *)data, &sc->sc_ac) :
+                   ether_delmulti((struct ifreq *)data, &sc->sc_ac);
+
+               if (error == ENETRESET) {
+                       /*
+                        * Multicast list has changed; set the hardware
+                        * filter accordingly.
+                        */
+                       lereset((struct device *)sc);
+                       error = 0;
+               }
+               break;
+#endif
+
+       default:
+               error = EINVAL;
+       }
+       splx(s);
+       return (error);
+}
+
+void
+leerror(sc, stat)
+       register struct le_softc *sc;
+       int stat;
+{
+       if (!ledebug)
+               return;
+
+       /*
+        * Not all transceivers implement heartbeat
+        * so we only log CERR once.
+        */
+       if ((stat & LE_C0_CERR) && sc->sc_cerr)
+               return;
+       log(LOG_WARNING, "%s: error: stat=%b\n",
+           sc->sc_dev.dv_xname, stat, LE_C0_BITS);
+}
+
+void
+lererror(sc, msg)
+       register struct le_softc *sc;
+       char *msg;
+{
+       register volatile struct lermd *rmd;
+       int len;
+
+       if (!ledebug)
+               return;
+
+       rmd = &sc->sc_r2->ler2_rmd[sc->sc_rmd];
+       len = rmd->rmd3;
+       log(LOG_WARNING, "%s: ierror(%s): from %s: buf=%d, len=%d, rmd1=%b\n",
+           sc->sc_dev.dv_xname, msg, len > 11 ?
+           ether_sprintf((u_char *)&sc->sc_r2->ler2_rbuf[sc->sc_rmd][6]) :
+           "unknown",
+           sc->sc_rmd, len, rmd->rmd1_bits, LE_R1_BITS);
+}
+
+void
+lexerror(sc)
+       register struct le_softc *sc;
+{
+       register volatile struct letmd *tmd;
+       register int len, tmd3, tdr;
+
+       if (!ledebug)
+               return;
+
+       tmd = sc->sc_r2->ler2_tmd;
+       tmd3 = tmd->tmd3;
+       tdr = tmd3 & LE_T3_TDR_MASK;
+       len = -tmd->tmd2;
+       log(LOG_WARNING,
+    "%s: oerror: to %s: buf=%d, len=%d, tmd1=%b, tmd3=%b, tdr=%d (%d nsecs)\n",
+           sc->sc_dev.dv_xname, len > 5 ?
+           ether_sprintf((u_char *)&sc->sc_r2->ler2_tbuf[0][0]) : "unknown",
+           0, len,
+           tmd->tmd1_bits, LE_T1_BITS,
+           tmd3, LE_T3_BITS, tdr, tdr * 100);
+}