summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1999-07-01 23:10:41 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1999-07-01 23:10:41 +0000
commitd57519a10bc4e9b97cd8834c226523efa9ecd5f3 (patch)
tree02fe2bc988bc40865f18c4f5b338cac4fe903553 /sys/dev
parent8d13bb0655915eba0d1aea2ed9a130078b208205 (diff)
lmc driver; ported by chris@dqc.org
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/files.pci10
-rw-r--r--sys/dev/pci/if_lmc.c1393
-rw-r--r--sys/dev/pci/if_lmc_common.c408
-rw-r--r--sys/dev/pci/if_lmc_media.c797
-rw-r--r--sys/dev/pci/if_lmc_obsd.c427
-rw-r--r--sys/dev/pci/if_lmc_types.h88
-rw-r--r--sys/dev/pci/if_lmcioctl.h243
-rw-r--r--sys/dev/pci/if_lmcvar.h609
8 files changed, 3974 insertions, 1 deletions
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index ab0064d5db6..6b005ca9645 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.38 1999/04/28 23:20:59 alex Exp $
+# $OpenBSD: files.pci,v 1.39 1999/07/01 23:10:39 deraadt Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -93,6 +93,14 @@ file dev/pci/ppb.c ppb
attach cy at pci with cy_pci
file dev/pci/cy_pci.c cy_pci
+# Lan Media Corporation T1/HSSI/DS3 adapter
+device lmc: ifnet, sppp
+attach lmc at pci
+file dev/pci/if_lmc.c lmc
+file dev/pci/if_lmc_common.c lmc
+file dev/pci/if_lmc_media.c lmc
+file dev/pci/if_lmc_obsd.c lmc
+
# Intel EtherExpress PRO 10/100B
device fxp: ether, ifnet, mii, ifmedia
attach fxp at pci
diff --git a/sys/dev/pci/if_lmc.c b/sys/dev/pci/if_lmc.c
new file mode 100644
index 00000000000..f9f10794ca9
--- /dev/null
+++ b/sys/dev/pci/if_lmc.c
@@ -0,0 +1,1393 @@
+/* $NetBSD: if_lmc.c,v 1.1 1999/03/25 03:32:43 explorer Exp $ */
+
+/*-
+ * Copyright (c) 1997-1999 LAN Media Corporation (LMC)
+ * All rights reserved. www.lanmedia.com
+ *
+ * This code is written by Michael Graff <graff@vix.com> for LMC.
+ * The code is derived from permitted modifications to software created
+ * by Matt Thomas (matt@3am-software.com).
+ *
+ * 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 marketing or advertising materials mentioning features or
+ * use of this software must display the following acknowledgement:
+ * This product includes software developed by LAN Media Corporation
+ * and its contributors.
+ * 4. Neither the name of LAN Media Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION 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 FOUNDATION 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.
+ */
+
+/*-
+ * Copyright (c) 1994-1997 Matt Thomas (matt@3am-software.com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+/*#include <sys/proc.h> only for declaration of wakeup() used by vm.h */
+#if defined(__FreeBSD__)
+#include <machine/clock.h>
+#elif defined(__bsdi__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#endif
+
+#if defined(__OpenBSD__)
+#include <dev/pci/pcidevs.h>
+#endif
+
+#if defined(__NetBSD__)
+#include <dev/pci/pcidevs.h>
+#include "rnd.h"
+#if NRND > 0
+#include <sys/rnd.h>
+#endif
+#endif
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/netisr.h>
+
+#include "bpfilter.h"
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <net/if_sppp.h>
+#endif
+
+#if defined(__bsdi__)
+#if INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net/netisr.h>
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/if_types.h>
+#include <net/if_p2p.h>
+#include <net/if_c_hdlc.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <vm/pmap.h>
+#include <pci.h>
+#if NPCI > 0
+#include <pci/pcivar.h>
+#include <pci/dc21040reg.h>
+#define INCLUDE_PATH_PREFIX "pci/"
+#endif
+#endif /* __FreeBSD__ */
+
+#if defined(__bsdi__)
+#include <i386/pci/ic/dc21040.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/dma.h>
+#include <i386/isa/isavar.h>
+#include <i386/pci/pci.h>
+
+#define INCLUDE_PATH_PREFIX "i386/pci/"
+#endif /* __bsdi__ */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <machine/bus.h>
+#if defined(__alpha__) && defined(__NetBSD__)
+#include <machine/intr.h>
+#endif
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/ic/dc21040reg.h>
+#define INCLUDE_PATH_PREFIX "dev/pci/"
+#endif /* __NetBSD__ */
+
+#if defined(__OpenBSD__)
+#define d_length1 u.bd_length1
+#define d_length2 u.bd_length2
+#define d_flag u.bd_flag
+#endif
+
+/*
+ * Sigh. Every OS puts these in different places. NetBSD and FreeBSD use
+ * a C preprocessor that allows this hack, but BSDI does not. Grr.
+ */
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+#include INCLUDE_PATH_PREFIX "if_lmc_types.h"
+#include INCLUDE_PATH_PREFIX "if_lmcioctl.h"
+#include INCLUDE_PATH_PREFIX "if_lmcvar.h"
+#else /* BSDI */
+#include "i386/pci/if_lmctypes.h"
+#include "i386/pci/if_lmcioctl.h"
+#include "i386/pci/if_lmcvar.h"
+#endif
+
+/*
+ * This module supports
+ * the DEC 21140A pass 2.2 PCI Fast Ethernet Controller.
+ */
+static ifnet_ret_t lmc_ifstart_one(struct ifnet *ifp);
+static ifnet_ret_t lmc_ifstart(struct ifnet *ifp);
+static struct mbuf *lmc_txput(lmc_softc_t * const sc, struct mbuf *m);
+static void lmc_rx_intr(lmc_softc_t * const sc);
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+static void lmc_watchdog(struct ifnet *ifp);
+#endif
+#if defined(__bsdi__)
+static int lmc_watchdog(int);
+#endif
+static void lmc_ifup(lmc_softc_t * const sc);
+static void lmc_ifdown(lmc_softc_t * const sc);
+
+
+/*
+ * Code the read the SROM and MII bit streams (I2C)
+ */
+static inline void
+lmc_delay_300ns(lmc_softc_t * const sc)
+{
+ int idx;
+ for (idx = (300 / 33) + 1; idx > 0; idx--)
+ (void)LMC_CSR_READ(sc, csr_busmode);
+}
+
+
+#define EMIT \
+do { \
+ LMC_CSR_WRITE(sc, csr_srom_mii, csr); \
+ lmc_delay_300ns(sc); \
+} while (0)
+
+static inline void
+lmc_srom_idle(lmc_softc_t * const sc)
+{
+ unsigned bit, csr;
+
+ csr = SROMSEL ; EMIT;
+ csr = SROMSEL | SROMRD; EMIT;
+ csr ^= SROMCS; EMIT;
+ csr ^= SROMCLKON; EMIT;
+
+ /*
+ * Write 25 cycles of 0 which will force the SROM to be idle.
+ */
+ for (bit = 3 + SROM_BITWIDTH + 16; bit > 0; bit--) {
+ csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */
+ csr ^= SROMCLKON; EMIT; /* clock high; data valid */
+ }
+ csr ^= SROMCLKOFF; EMIT;
+ csr ^= SROMCS; EMIT;
+ csr = 0; EMIT;
+}
+
+
+static void
+lmc_srom_read(lmc_softc_t * const sc)
+{
+ unsigned idx;
+ const unsigned bitwidth = SROM_BITWIDTH;
+ const unsigned cmdmask = (SROMCMD_RD << bitwidth);
+ const unsigned msb = 1 << (bitwidth + 3 - 1);
+ unsigned lastidx = (1 << bitwidth) - 1;
+
+ lmc_srom_idle(sc);
+
+ for (idx = 0; idx <= lastidx; idx++) {
+ unsigned lastbit, data, bits, bit, csr;
+ csr = SROMSEL ; EMIT;
+ csr = SROMSEL | SROMRD; EMIT;
+ csr ^= SROMCSON; EMIT;
+ csr ^= SROMCLKON; EMIT;
+
+ lastbit = 0;
+ for (bits = idx|cmdmask, bit = bitwidth + 3
+ ; bit > 0
+ ; bit--, bits <<= 1) {
+ const unsigned thisbit = bits & msb;
+ csr ^= SROMCLKOFF; EMIT; /* clock L data invalid */
+ if (thisbit != lastbit) {
+ csr ^= SROMDOUT; EMIT;/* clock L invert data */
+ } else {
+ EMIT;
+ }
+ csr ^= SROMCLKON; EMIT; /* clock H data valid */
+ lastbit = thisbit;
+ }
+ csr ^= SROMCLKOFF; EMIT;
+
+ for (data = 0, bits = 0; bits < 16; bits++) {
+ data <<= 1;
+ csr ^= SROMCLKON; EMIT; /* clock H data valid */
+ data |= LMC_CSR_READ(sc, csr_srom_mii) & SROMDIN ? 1 : 0;
+ csr ^= SROMCLKOFF; EMIT; /* clock L data invalid */
+ }
+ sc->lmc_rombuf[idx*2] = data & 0xFF;
+ sc->lmc_rombuf[idx*2+1] = data >> 8;
+ csr = SROMSEL | SROMRD; EMIT;
+ csr = 0; EMIT;
+ }
+ lmc_srom_idle(sc);
+}
+
+#define MII_EMIT do { LMC_CSR_WRITE(sc, csr_srom_mii, csr); lmc_delay_300ns(sc); } while (0)
+
+static inline void
+lmc_mii_writebits(lmc_softc_t * const sc, unsigned data, unsigned bits)
+{
+ unsigned msb = 1 << (bits - 1);
+ unsigned csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK);
+ unsigned lastbit = (csr & MII_DOUT) ? msb : 0;
+
+ csr |= MII_WR; MII_EMIT; /* clock low; assert write */
+
+ for (; bits > 0; bits--, data <<= 1) {
+ const unsigned thisbit = data & msb;
+ if (thisbit != lastbit) {
+ csr ^= MII_DOUT; MII_EMIT; /* clock low; invert data */
+ }
+ csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */
+ lastbit = thisbit;
+ csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */
+ }
+}
+
+static void
+lmc_mii_turnaround(lmc_softc_t * const sc, u_int32_t cmd)
+{
+ u_int32_t csr;
+
+ csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK);
+ if (cmd == MII_WRCMD) {
+ csr |= MII_DOUT; MII_EMIT; /* clock low; change data */
+ csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */
+ csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */
+ csr ^= MII_DOUT; MII_EMIT; /* clock low; change data */
+ } else {
+ csr |= MII_RD; MII_EMIT; /* clock low; switch to read */
+ }
+ csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */
+ csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */
+}
+
+static u_int32_t
+lmc_mii_readbits(lmc_softc_t * const sc)
+{
+ u_int32_t data;
+ u_int32_t csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK);
+ int idx;
+
+ for (idx = 0, data = 0; idx < 16; idx++) {
+ data <<= 1; /* this is NOOP on the first pass through */
+ csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */
+ if (LMC_CSR_READ(sc, csr_srom_mii) & MII_DIN)
+ data |= 1;
+ csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */
+ }
+ csr ^= MII_RD; MII_EMIT; /* clock low; turn off read */
+
+ return data;
+}
+
+u_int32_t
+lmc_mii_readreg(lmc_softc_t * const sc, u_int32_t devaddr, u_int32_t regno)
+{
+ u_int32_t csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK);
+ u_int32_t data;
+
+ csr &= ~(MII_RD|MII_CLK); MII_EMIT;
+ lmc_mii_writebits(sc, MII_PREAMBLE, 32);
+ lmc_mii_writebits(sc, MII_RDCMD, 8);
+ lmc_mii_writebits(sc, devaddr, 5);
+ lmc_mii_writebits(sc, regno, 5);
+ lmc_mii_turnaround(sc, MII_RDCMD);
+
+ data = lmc_mii_readbits(sc);
+ return (data);
+}
+
+void
+lmc_mii_writereg(lmc_softc_t * const sc, u_int32_t devaddr,
+ u_int32_t regno, u_int32_t data)
+{
+ u_int32_t csr;
+
+ csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK);
+ csr &= ~(MII_RD|MII_CLK); MII_EMIT;
+ lmc_mii_writebits(sc, MII_PREAMBLE, 32);
+ lmc_mii_writebits(sc, MII_WRCMD, 8);
+ lmc_mii_writebits(sc, devaddr, 5);
+ lmc_mii_writebits(sc, regno, 5);
+ lmc_mii_turnaround(sc, MII_WRCMD);
+ lmc_mii_writebits(sc, data, 16);
+}
+
+int
+lmc_read_macaddr(lmc_softc_t * const sc)
+{
+ lmc_srom_read(sc);
+
+ bcopy(sc->lmc_rombuf + 20, sc->lmc_enaddr, 6);
+
+ return 0;
+}
+
+/*
+ * Check to make certain there is a signal from the modem, and flicker
+ * lights as needed.
+ */
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+static void
+lmc_watchdog(struct ifnet *ifp)
+#endif
+#if defined(__bsdi__)
+static int
+lmc_watchdog(int unit)
+#endif
+{
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp);
+#endif
+#if defined(__bsdi__)
+ lmc_softc_t * const sc = LMC_UNIT_TO_SOFTC(unit);
+ struct ifnet *ifp = &sc->lmc_if;
+#endif
+ int state;
+ u_int32_t ostatus;
+ u_int32_t link_status;
+ u_int32_t ticks;
+
+ state = 0;
+
+ link_status = sc->lmc_media->get_link_status(sc);
+ ostatus = ((sc->lmc_flags & LMC_MODEMOK) == LMC_MODEMOK);
+
+ /*
+ * hardware level link lost, but the interface is marked as up.
+ * Mark it as down.
+ */
+ if (link_status == 0 && ostatus) {
+ printf(LMC_PRINTF_FMT ": physical link down\n",
+ LMC_PRINTF_ARGS);
+ sc->lmc_flags &= ~LMC_MODEMOK;
+ lmc_led_off(sc, LMC_MII16_LED1);
+ }
+
+ /*
+ * hardware link is up, but the interface is marked as down.
+ * Bring it back up again.
+ */
+ if (link_status != 0 && !ostatus) {
+ printf(LMC_PRINTF_FMT ": physical link up\n",
+ LMC_PRINTF_ARGS);
+ if (sc->lmc_flags & LMC_IFUP)
+ lmc_ifup(sc);
+ sc->lmc_flags |= LMC_MODEMOK;
+ lmc_led_on(sc, LMC_MII16_LED1);
+ return;
+ }
+
+ /*
+ * remember the timer value
+ */
+ ticks = LMC_CSR_READ(sc, csr_gp_timer);
+ LMC_CSR_WRITE(sc, csr_gp_timer, 0xffffffffUL);
+ sc->ictl.ticks = 0x0000ffff - (ticks & 0x0000ffff);
+
+ ifp->if_timer = 1;
+}
+
+/*
+ * Mark the interface as "up" and enable TX/RX and TX/RX interrupts.
+ * This also does a full software reset.
+ */
+static void
+lmc_ifup(lmc_softc_t * const sc)
+{
+ sc->lmc_if.if_timer = 0;
+
+ lmc_dec_reset(sc);
+ lmc_reset(sc);
+
+ sc->lmc_media->set_link_status(sc, 1);
+ sc->lmc_media->set_status(sc, NULL);
+
+ sc->lmc_flags |= LMC_IFUP;
+
+ lmc_led_on(sc, LMC_MII16_LED1);
+
+ /*
+ * select what interrupts we want to get
+ */
+ sc->lmc_intrmask |= (TULIP_STS_NORMALINTR
+ | TULIP_STS_RXINTR
+ | TULIP_STS_TXINTR
+ | TULIP_STS_ABNRMLINTR
+ | TULIP_STS_SYSERROR
+ | TULIP_STS_TXSTOPPED
+ | TULIP_STS_TXUNDERFLOW
+ | TULIP_STS_RXSTOPPED
+ );
+ LMC_CSR_WRITE(sc, csr_intr, sc->lmc_intrmask);
+
+ sc->lmc_cmdmode |= TULIP_CMD_TXRUN;
+ sc->lmc_cmdmode |= TULIP_CMD_RXRUN;
+ LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode);
+
+ sc->lmc_if.if_timer = 1;
+}
+
+/*
+ * Mark the interface as "down" and disable TX/RX and TX/RX interrupts.
+ * This is done by performing a full reset on the interface.
+ */
+static void
+lmc_ifdown(lmc_softc_t * const sc)
+{
+ sc->lmc_if.if_timer = 0;
+ sc->lmc_flags &= ~LMC_IFUP;
+
+ sc->lmc_media->set_link_status(sc, 0);
+ lmc_led_off(sc, LMC_MII16_LED1);
+
+ lmc_dec_reset(sc);
+ lmc_reset(sc);
+ sc->lmc_media->set_status(sc, NULL);
+}
+
+static void
+lmc_rx_intr(lmc_softc_t * const sc)
+{
+ lmc_ringinfo_t * const ri = &sc->lmc_rxinfo;
+ struct ifnet * const ifp = &sc->lmc_if;
+ int fillok = 1;
+
+ sc->lmc_rxtick++;
+
+ for (;;) {
+ tulip_desc_t *eop = ri->ri_nextin;
+ int total_len = 0, last_offset = 0;
+ struct mbuf *ms = NULL, *me = NULL;
+ int accept = 0;
+
+ if (fillok && sc->lmc_rxq.ifq_len < LMC_RXQ_TARGET)
+ goto queue_mbuf;
+
+ /*
+ * If the TULIP has no descriptors, there can't be any receive
+ * descriptors to process.
+ */
+ if (eop == ri->ri_nextout)
+ break;
+
+ /*
+ * 90% of the packets will fit in one descriptor. So we
+ * optimize for that case.
+ */
+ if ((((volatile tulip_desc_t *) eop)->d_status & (TULIP_DSTS_OWNER|TULIP_DSTS_RxFIRSTDESC|TULIP_DSTS_RxLASTDESC)) == (TULIP_DSTS_RxFIRSTDESC|TULIP_DSTS_RxLASTDESC)) {
+ IF_DEQUEUE(&sc->lmc_rxq, ms);
+ me = ms;
+ } else {
+ /*
+ * If still owned by the TULIP, don't touch it.
+ */
+ if (((volatile tulip_desc_t *)eop)->d_status & TULIP_DSTS_OWNER)
+ break;
+
+ /*
+ * It is possible (though improbable unless the
+ * BIG_PACKET support is enabled or MCLBYTES < 1518)
+ * for a received packet to cross more than one
+ * receive descriptor.
+ */
+ while ((((volatile tulip_desc_t *) eop)->d_status & TULIP_DSTS_RxLASTDESC) == 0) {
+ if (++eop == ri->ri_last)
+ eop = ri->ri_first;
+ if (eop == ri->ri_nextout || ((((volatile tulip_desc_t *) eop)->d_status & TULIP_DSTS_OWNER))) {
+ return;
+ }
+ total_len++;
+ }
+
+ /*
+ * Dequeue the first buffer for the start of the
+ * packet. Hopefully this will be the only one we
+ * need to dequeue. However, if the packet consumed
+ * multiple descriptors, then we need to dequeue
+ * those buffers and chain to the starting mbuf.
+ * All buffers but the last buffer have the same
+ * length so we can set that now. (we add to
+ * last_offset instead of multiplying since we
+ * normally won't go into the loop and thereby
+ * saving a ourselves from doing a multiplication
+ * by 0 in the normal case).
+ */
+ IF_DEQUEUE(&sc->lmc_rxq, ms);
+ for (me = ms; total_len > 0; total_len--) {
+ me->m_len = LMC_RX_BUFLEN;
+ last_offset += LMC_RX_BUFLEN;
+ IF_DEQUEUE(&sc->lmc_rxq, me->m_next);
+ me = me->m_next;
+ }
+ }
+
+ /*
+ * Now get the size of received packet (minus the CRC).
+ */
+ total_len = ((eop->d_status >> 16) & 0x7FFF);
+ if (sc->ictl.crc_length == 16)
+ total_len -= 2;
+ else
+ total_len -= 4;
+
+ if ((sc->lmc_flags & LMC_RXIGNORE) == 0
+ && ((eop->d_status & LMC_DSTS_ERRSUM) == 0
+#ifdef BIG_PACKET
+ || (total_len <= sc->lmc_if.if_mtu + PPP_HEADER_LEN
+ && (eop->d_status & TULIP_DSTS_RxOVERFLOW) == 0)
+#endif
+ )) {
+ me->m_len = total_len - last_offset;
+#if NBPFILTER > 0
+ if (sc->lmc_bpf != NULL) {
+ if (me == ms)
+ LMC_BPF_TAP(sc, mtod(ms, caddr_t), total_len);
+ else
+ LMC_BPF_MTAP(sc, ms);
+ }
+#endif
+ sc->lmc_flags |= LMC_RXACT;
+ accept = 1;
+ } else {
+ ifp->if_ierrors++;
+ if (eop->d_status & TULIP_DSTS_RxOVERFLOW) {
+ sc->lmc_dot3stats.dot3StatsInternalMacReceiveErrors++;
+ }
+ }
+
+ ifp->if_ipackets++;
+ if (++eop == ri->ri_last)
+ eop = ri->ri_first;
+ ri->ri_nextin = eop;
+
+ queue_mbuf:
+ /*
+ * Either we are priming the TULIP with mbufs (m == NULL)
+ * or we are about to accept an mbuf for the upper layers
+ * so we need to allocate an mbuf to replace it. If we
+ * can't replace it, send up it anyways. This may cause
+ * us to drop packets in the future but that's better than
+ * being caught in livelock.
+ *
+ * Note that if this packet crossed multiple descriptors
+ * we don't even try to reallocate all the mbufs here.
+ * Instead we rely on the test of the beginning of
+ * the loop to refill for the extra consumed mbufs.
+ */
+ if (accept || ms == NULL) {
+ struct mbuf *m0;
+ MGETHDR(m0, M_DONTWAIT, MT_DATA);
+ if (m0 != NULL) {
+ MCLGET(m0, M_DONTWAIT);
+ if ((m0->m_flags & M_EXT) == 0) {
+ m_freem(m0);
+ m0 = NULL;
+ }
+ }
+ if (accept) {
+ ms->m_pkthdr.len = total_len;
+ ms->m_pkthdr.rcvif = ifp;
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ sppp_input(ifp, ms);
+#endif
+#if defined(__bsdi__)
+ sc->lmc_p2pcom.p2p_input(&sc->lmc_p2pcom, ms);
+#endif
+ }
+ ms = m0;
+ }
+ if (ms == NULL) {
+ /*
+ * Couldn't allocate a new buffer. Don't bother
+ * trying to replenish the receive queue.
+ */
+ fillok = 0;
+ sc->lmc_flags |= LMC_RXBUFSLOW;
+ continue;
+ }
+ /*
+ * Now give the buffer(s) to the TULIP and save in our
+ * receive queue.
+ */
+ do {
+ ri->ri_nextout->d_length1 = LMC_RX_BUFLEN;
+ ri->ri_nextout->d_addr1 = LMC_KVATOPHYS(sc, mtod(ms, caddr_t));
+ ri->ri_nextout->d_status = TULIP_DSTS_OWNER;
+ if (++ri->ri_nextout == ri->ri_last)
+ ri->ri_nextout = ri->ri_first;
+ me = ms->m_next;
+ ms->m_next = NULL;
+ IF_ENQUEUE(&sc->lmc_rxq, ms);
+ } while ((ms = me) != NULL);
+
+ if (sc->lmc_rxq.ifq_len >= LMC_RXQ_TARGET)
+ sc->lmc_flags &= ~LMC_RXBUFSLOW;
+ }
+}
+
+static int
+lmc_tx_intr(lmc_softc_t * const sc)
+{
+ lmc_ringinfo_t * const ri = &sc->lmc_txinfo;
+ struct mbuf *m;
+ int xmits = 0;
+ int descs = 0;
+
+ sc->lmc_txtick++;
+
+ while (ri->ri_free < ri->ri_max) {
+#ifdef __OpenBSD__
+ u_int32_t duh_flag;
+#else
+ u_int32_t d_flag;
+#endif
+
+ if (((volatile tulip_desc_t *) ri->ri_nextin)->d_status & TULIP_DSTS_OWNER)
+ break;
+
+#ifdef __OpenBSD__
+ duh_flag = ri->ri_nextin->d_flag;
+ if (duh_flag & TULIP_DFLAG_TxLASTSEG) {
+#else
+ d_flag = ri->ri_nextin->d_flag;
+ if (d_flag & TULIP_DFLAG_TxLASTSEG) {
+#endif
+ const u_int32_t d_status = ri->ri_nextin->d_status;
+ IF_DEQUEUE(&sc->lmc_txq, m);
+ if (m != NULL) {
+#if NBPFILTER > 0
+ if (sc->lmc_bpf != NULL)
+ LMC_BPF_MTAP(sc, m);
+#endif
+ m_freem(m);
+#if defined(LMC_DEBUG)
+ } else {
+ printf(LMC_PRINTF_FMT ": tx_intr: failed to dequeue mbuf?!?\n", LMC_PRINTF_ARGS);
+#endif
+ }
+ xmits++;
+ if (d_status & LMC_DSTS_ERRSUM) {
+ sc->lmc_if.if_oerrors++;
+ if (d_status & TULIP_DSTS_TxUNDERFLOW)
+ sc->lmc_dot3stats.dot3StatsInternalTransmitUnderflows++;
+ } else {
+ if (d_status & TULIP_DSTS_TxDEFERRED)
+ sc->lmc_dot3stats.dot3StatsDeferredTransmissions++;
+ }
+ }
+
+ if (++ri->ri_nextin == ri->ri_last)
+ ri->ri_nextin = ri->ri_first;
+
+ ri->ri_free++;
+ descs++;
+ sc->lmc_if.if_flags &= ~IFF_OACTIVE;
+ }
+ /*
+ * If nothing left to transmit, disable the timer.
+ * Else if progress, reset the timer back to 2 ticks.
+ */
+ sc->lmc_if.if_opackets += xmits;
+
+ return descs;
+}
+
+static void
+lmc_print_abnormal_interrupt (lmc_softc_t * const sc, u_int32_t csr)
+{
+ printf(LMC_PRINTF_FMT ": Abnormal interrupt\n", LMC_PRINTF_ARGS);
+}
+
+static void
+lmc_intr_handler(lmc_softc_t * const sc, int *progress_p)
+{
+ u_int32_t csr;
+
+ while ((csr = LMC_CSR_READ(sc, csr_status)) & sc->lmc_intrmask) {
+
+#if defined(__NetBSD__)
+#if NRND > 0
+ rnd_add_uint32(&sc->lmc_rndsource, csr);
+#endif
+#endif
+
+ *progress_p = 1;
+ LMC_CSR_WRITE(sc, csr_status, csr);
+
+ if (csr & TULIP_STS_SYSERROR) {
+ sc->lmc_last_system_error = (csr & TULIP_STS_ERRORMASK) >> TULIP_STS_ERR_SHIFT;
+ if (sc->lmc_flags & LMC_NOMESSAGES) {
+ sc->lmc_flags |= LMC_SYSTEMERROR;
+ } else {
+ printf(LMC_PRINTF_FMT ": system error: %s\n",
+ LMC_PRINTF_ARGS,
+ lmc_system_errors[sc->lmc_last_system_error]);
+ }
+ sc->lmc_flags |= LMC_NEEDRESET;
+ sc->lmc_system_errors++;
+ break;
+ }
+ if (csr & (TULIP_STS_RXINTR | TULIP_STS_RXNOBUF)) {
+ u_int32_t misses = LMC_CSR_READ(sc, csr_missed_frames);
+ if (csr & TULIP_STS_RXNOBUF)
+ sc->lmc_dot3stats.dot3StatsMissedFrames += misses & 0xFFFF;
+ /*
+ * Pass 2.[012] of the 21140A-A[CDE] may hang and/or corrupt data
+ * on receive overflows.
+ */
+ if ((misses & 0x0FFE0000) && (sc->lmc_features & LMC_HAVE_RXBADOVRFLW)) {
+ sc->lmc_dot3stats.dot3StatsInternalMacReceiveErrors++;
+ /*
+ * Stop the receiver process and spin until it's stopped.
+ * Tell rx_intr to drop the packets it dequeues.
+ */
+ LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode & ~TULIP_CMD_RXRUN);
+ while ((LMC_CSR_READ(sc, csr_status) & TULIP_STS_RXSTOPPED) == 0)
+ ;
+ LMC_CSR_WRITE(sc, csr_status, TULIP_STS_RXSTOPPED);
+ sc->lmc_flags |= LMC_RXIGNORE;
+ }
+ lmc_rx_intr(sc);
+ if (sc->lmc_flags & LMC_RXIGNORE) {
+ /*
+ * Restart the receiver.
+ */
+ sc->lmc_flags &= ~LMC_RXIGNORE;
+ LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode);
+ }
+ }
+ if (csr & TULIP_STS_ABNRMLINTR) {
+ u_int32_t tmp = csr & sc->lmc_intrmask
+ & ~(TULIP_STS_NORMALINTR|TULIP_STS_ABNRMLINTR);
+ if (csr & TULIP_STS_TXUNDERFLOW) {
+ if ((sc->lmc_cmdmode & TULIP_CMD_THRESHOLDCTL) != TULIP_CMD_THRSHLD160) {
+ sc->lmc_cmdmode += TULIP_CMD_THRSHLD96;
+ sc->lmc_flags |= LMC_NEWTXTHRESH;
+ } else if (sc->lmc_features & LMC_HAVE_STOREFWD) {
+ sc->lmc_cmdmode |= TULIP_CMD_STOREFWD;
+ sc->lmc_flags |= LMC_NEWTXTHRESH;
+ }
+ }
+ if (sc->lmc_flags & LMC_NOMESSAGES) {
+ sc->lmc_statusbits |= tmp;
+ } else {
+ lmc_print_abnormal_interrupt(sc, tmp);
+ sc->lmc_flags |= LMC_NOMESSAGES;
+ }
+ LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode);
+ }
+
+ if (csr & TULIP_STS_TXINTR)
+ lmc_tx_intr(sc);
+
+ if (sc->lmc_flags & LMC_WANTTXSTART)
+ lmc_ifstart(&sc->lmc_if);
+ }
+}
+
+lmc_intrfunc_t
+lmc_intr_normal(void *arg)
+{
+ lmc_softc_t * sc = (lmc_softc_t *) arg;
+ int progress = 0;
+
+ lmc_intr_handler(sc, &progress);
+
+#if !defined(LMC_VOID_INTRFUNC)
+ return progress;
+#endif
+}
+
+static struct mbuf *
+lmc_mbuf_compress(struct mbuf *m)
+{
+ struct mbuf *m0;
+#if MCLBYTES >= LMC_MTU + PPP_HEADER_LEN && !defined(BIG_PACKET)
+ MGETHDR(m0, M_DONTWAIT, MT_DATA);
+ if (m0 != NULL) {
+ if (m->m_pkthdr.len > MHLEN) {
+ MCLGET(m0, M_DONTWAIT);
+ if ((m0->m_flags & M_EXT) == 0) {
+ m_freem(m);
+ m_freem(m0);
+ return NULL;
+ }
+ }
+ m_copydata(m, 0, m->m_pkthdr.len, mtod(m0, caddr_t));
+ m0->m_pkthdr.len = m0->m_len = m->m_pkthdr.len;
+ }
+#else
+ int mlen = MHLEN;
+ int len = m->m_pkthdr.len;
+ struct mbuf **mp = &m0;
+
+ while (len > 0) {
+ if (mlen == MHLEN) {
+ MGETHDR(*mp, M_DONTWAIT, MT_DATA);
+ } else {
+ MGET(*mp, M_DONTWAIT, MT_DATA);
+ }
+ if (*mp == NULL) {
+ m_freem(m0);
+ m0 = NULL;
+ break;
+ }
+ if (len > MLEN) {
+ MCLGET(*mp, M_DONTWAIT);
+ if (((*mp)->m_flags & M_EXT) == 0) {
+ m_freem(m0);
+ m0 = NULL;
+ break;
+ }
+ (*mp)->m_len = (len <= MCLBYTES ? len : MCLBYTES);
+ } else {
+ (*mp)->m_len = (len <= mlen ? len : mlen);
+ }
+ m_copydata(m, m->m_pkthdr.len - len,
+ (*mp)->m_len, mtod((*mp), caddr_t));
+ len -= (*mp)->m_len;
+ mp = &(*mp)->m_next;
+ mlen = MLEN;
+ }
+#endif
+ m_freem(m);
+ return m0;
+}
+
+/*
+ * queue the mbuf handed to us for the interface. If we cannot
+ * queue it, return the mbuf. Return NULL if the mbuf was queued.
+ */
+static struct mbuf *
+lmc_txput(lmc_softc_t * const sc, struct mbuf *m)
+{
+ lmc_ringinfo_t * const ri = &sc->lmc_txinfo;
+ tulip_desc_t *eop, *nextout;
+ int segcnt, free;
+ u_int32_t d_status;
+ struct mbuf *m0;
+
+#if defined(LMC_DEBUG)
+ if ((sc->lmc_cmdmode & TULIP_CMD_TXRUN) == 0) {
+ printf(LMC_PRINTF_FMT ": txput: tx not running\n",
+ LMC_PRINTF_ARGS);
+ sc->lmc_flags |= LMC_WANTTXSTART;
+ goto finish;
+ }
+#endif
+
+ /*
+ * Now we try to fill in our transmit descriptors. This is
+ * a bit reminiscent of going on the Ark two by two
+ * since each descriptor for the TULIP can describe
+ * two buffers. So we advance through packet filling
+ * each of the two entries at a time to fill each
+ * descriptor. Clear the first and last segment bits
+ * in each descriptor (actually just clear everything
+ * but the end-of-ring or chain bits) to make sure
+ * we don't get messed up by previously sent packets.
+ *
+ * We may fail to put the entire packet on the ring if
+ * there is either not enough ring entries free or if the
+ * packet has more than MAX_TXSEG segments. In the former
+ * case we will just wait for the ring to empty. In the
+ * latter case we have to recopy.
+ */
+ again:
+ d_status = 0;
+ eop = nextout = ri->ri_nextout;
+ m0 = m;
+ segcnt = 0;
+ free = ri->ri_free;
+ do {
+ int len = m0->m_len;
+ caddr_t addr = mtod(m0, caddr_t);
+ unsigned clsize = CLBYTES - (((u_long) addr) & (CLBYTES-1));
+
+ while (len > 0) {
+ unsigned slen = min(len, clsize);
+#ifdef BIG_PACKET
+ int partial = 0;
+ if (slen >= 2048)
+ slen = 2040, partial = 1;
+#endif
+ segcnt++;
+ if (segcnt > LMC_MAX_TXSEG) {
+ /*
+ * The packet exceeds the number of transmit
+ * buffer entries that we can use for one
+ * packet, so we have recopy it into one mbuf
+ * and then try again.
+ */
+ m = lmc_mbuf_compress(m);
+ if (m == NULL)
+ goto finish;
+ goto again;
+ }
+ if (segcnt & 1) {
+ if (--free == 0) {
+ /*
+ * See if there's any unclaimed space
+ * in the transmit ring.
+ */
+ if ((free += lmc_tx_intr(sc)) == 0) {
+ /*
+ * There's no more room but
+ * since nothing has been
+ * committed at this point,
+ * just show output is active,
+ * put back the mbuf and
+ * return.
+ */
+ sc->lmc_flags |= LMC_WANTTXSTART;
+ goto finish;
+ }
+ }
+ eop = nextout;
+ if (++nextout == ri->ri_last)
+ nextout = ri->ri_first;
+ eop->d_flag &= TULIP_DFLAG_ENDRING;
+ eop->d_flag |= TULIP_DFLAG_TxNOPADDING;
+ if (sc->ictl.crc_length == 16)
+ eop->d_flag |= TULIP_DFLAG_TxHASCRC;
+ eop->d_status = d_status;
+ eop->d_addr1 = LMC_KVATOPHYS(sc, addr);
+ eop->d_length1 = slen;
+ } else {
+ /*
+ * Fill in second half of descriptor
+ */
+ eop->d_addr2 = LMC_KVATOPHYS(sc, addr);
+ eop->d_length2 = slen;
+ }
+ d_status = TULIP_DSTS_OWNER;
+ len -= slen;
+ addr += slen;
+#ifdef BIG_PACKET
+ if (partial)
+ continue;
+#endif
+ clsize = CLBYTES;
+ }
+ } while ((m0 = m0->m_next) != NULL);
+
+
+ /*
+ * The descriptors have been filled in. Now get ready
+ * to transmit.
+ */
+ IF_ENQUEUE(&sc->lmc_txq, m);
+ m = NULL;
+
+ /*
+ * Make sure the next descriptor after this packet is owned
+ * by us since it may have been set up above if we ran out
+ * of room in the ring.
+ */
+ nextout->d_status = 0;
+
+ /*
+ * If we only used the first segment of the last descriptor,
+ * make sure the second segment will not be used.
+ */
+ if (segcnt & 1) {
+ eop->d_addr2 = 0;
+ eop->d_length2 = 0;
+ }
+
+ /*
+ * Mark the last and first segments, indicate we want a transmit
+ * complete interrupt, and tell it to transmit!
+ */
+ eop->d_flag |= TULIP_DFLAG_TxLASTSEG | TULIP_DFLAG_TxWANTINTR;
+
+ /*
+ * Note that ri->ri_nextout is still the start of the packet
+ * and until we set the OWNER bit, we can still back out of
+ * everything we have done.
+ */
+ ri->ri_nextout->d_flag |= TULIP_DFLAG_TxFIRSTSEG;
+ ri->ri_nextout->d_status = TULIP_DSTS_OWNER;
+
+ LMC_CSR_WRITE(sc, csr_txpoll, 1);
+
+ /*
+ * This advances the ring for us.
+ */
+ ri->ri_nextout = nextout;
+ ri->ri_free = free;
+
+ /*
+ * switch back to the single queueing ifstart.
+ */
+ sc->lmc_flags &= ~LMC_WANTTXSTART;
+ sc->lmc_if.if_start = lmc_ifstart_one;
+
+ /*
+ * If we want a txstart, there must be not enough space in the
+ * transmit ring. So we want to enable transmit done interrupts
+ * so we can immediately reclaim some space. When the transmit
+ * interrupt is posted, the interrupt handler will call tx_intr
+ * to reclaim space and then txstart (since WANTTXSTART is set).
+ * txstart will move the packet into the transmit ring and clear
+ * WANTTXSTART thereby causing TXINTR to be cleared.
+ */
+ finish:
+ if (sc->lmc_flags & LMC_WANTTXSTART) {
+ sc->lmc_if.if_flags |= IFF_OACTIVE;
+ sc->lmc_if.if_start = lmc_ifstart;
+ }
+
+ return m;
+}
+
+
+/*
+ * This routine is entered at splnet() (splsoftnet() on NetBSD)
+ */
+static int
+lmc_ifioctl(struct ifnet * ifp, ioctl_cmd_t cmd, caddr_t data)
+{
+ lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp);
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ lmc_spl_t s;
+#endif
+ int error = 0;
+ struct ifreq *ifr = (struct ifreq *)data;
+ u_int32_t new_state;
+ u_int32_t old_state;
+ lmc_ctl_t ctl;
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ s = LMC_RAISESPL();
+#endif
+
+ switch (cmd) {
+ case LMCIOCGINFO:
+ error = copyout(&sc->ictl, ifr->ifr_data, sizeof(lmc_ctl_t));
+
+ goto out;
+ break;
+
+ case LMCIOCSINFO:
+#if 0 /* XXX */
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error)
+ goto out;
+#endif
+
+ error = copyin(ifr->ifr_data, &ctl, sizeof(lmc_ctl_t));
+ if (error != 0)
+ goto out;
+
+ sc->lmc_media->set_status(sc, &ctl);
+
+ goto out;
+ break;
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+#if !defined(ifr_mtu)
+#define ifr_mtu ifr_metric
+#endif
+ case SIOCSIFMTU:
+ /*
+ * Don't allow the MTU to get larger than we can handle
+ */
+ if (ifr->ifr_mtu > LMC_MTU) {
+ error = EINVAL;
+ goto out;
+ }
+#endif
+ }
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ /*
+ * call the sppp ioctl layer
+ */
+ error = sppp_ioctl(ifp, cmd, data);
+ if (error != 0)
+ goto out;
+#endif
+
+#if defined(__bsdi__)
+ error = p2p_ioctl(ifp, cmd, data);
+#endif
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ /*
+ * If we are transitioning from up to down or down to up, call
+ * our init routine.
+ */
+ new_state = ifp->if_flags & IFF_UP;
+ old_state = sc->lmc_flags & LMC_IFUP;
+
+ if (new_state && !old_state)
+ lmc_ifup(sc);
+ else if (!new_state && old_state)
+ lmc_ifdown(sc);
+#endif
+
+ out:
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ LMC_RESTORESPL(s);
+#endif
+
+ return error;
+}
+
+/*
+ * These routines gets called at device spl (from sppp_output).
+ */
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+static ifnet_ret_t
+lmc_ifstart(struct ifnet * const ifp)
+{
+ lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp);
+ struct mbuf *m;
+
+ if (sc->lmc_flags & LMC_IFUP) {
+ while (sppp_isempty(ifp) == 0) {
+ m = sppp_dequeue(ifp);
+ if ((m = lmc_txput(sc, m)) != NULL) {
+ IF_PREPEND(&((struct sppp *)ifp)->pp_fastq, m);
+ break;
+ }
+ }
+ LMC_CSR_WRITE(sc, csr_txpoll, 1);
+ }
+}
+
+static ifnet_ret_t
+lmc_ifstart_one(struct ifnet * const ifp)
+{
+ lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp);
+ struct mbuf *m;
+
+ if ((sc->lmc_flags & LMC_IFUP) && (sppp_isempty(ifp) == 0)) {
+ m = sppp_dequeue(ifp);
+ if ((m = lmc_txput(sc, m)) != NULL) {
+ IF_PREPEND(&((struct sppp *)ifp)->pp_fastq, m);
+ }
+ LMC_CSR_WRITE(sc, csr_txpoll, 1);
+ }
+}
+#endif
+
+#if defined(__bsdi__)
+static ifnet_ret_t
+lmc_ifstart(struct ifnet * const ifp)
+{
+ lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp);
+ struct mbuf *m;
+ struct ifqueue *ifq;
+
+ if ((sc->lmc_flags & LMC_IFUP) == 0)
+ return;
+
+ for (;;) {
+ ifq = &sc->lmc_p2pcom.p2p_isnd;
+
+ m = ifq->ifq_head;
+ if (m == NULL) {
+ ifq = &sc->lmc_if.if_snd;
+ m = ifq->ifq_head;
+ }
+ if (m == NULL)
+ break;
+ IF_DEQUEUE(ifq, m);
+
+ m = lmc_txput(sc, m);
+ if (m != NULL) {
+ IF_PREPEND(ifq, m);
+ break;
+ }
+ }
+
+ LMC_CSR_WRITE(sc, csr_txpoll, 1);
+}
+
+static ifnet_ret_t
+lmc_ifstart_one(struct ifnet * const ifp)
+{
+ lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp);
+ struct mbuf *m;
+ struct ifqueue *ifq;
+
+ if ((sc->lmc_flags & LMC_IFUP) == 0)
+ return;
+
+ ifq = &sc->lmc_p2pcom.p2p_isnd;
+
+ m = ifq->ifq_head;
+ if (m == NULL) {
+ ifq = &sc->lmc_if.if_snd;
+ m = ifq->ifq_head;
+ }
+ if (m == NULL)
+ return 0;
+ IF_DEQUEUE(ifq, m);
+
+ m = lmc_txput(sc, m);
+ if (m != NULL)
+ IF_PREPEND(ifq, m);
+
+ LMC_CSR_WRITE(sc, csr_txpoll, 1);
+}
+#endif
+
+#if defined(__bsdi__)
+int
+lmc_getmdm(struct p2pcom *pp, caddr_t b)
+{
+ lmc_softc_t *sc = LMC_UNIT_TO_SOFTC(pp->p2p_if.if_unit);
+
+ if (sc->lmc_media->get_link_status(sc)) {
+ *(int *)b = TIOCM_CAR;
+ } else {
+ *(int *)b = 0;
+ }
+
+ return (0);
+}
+
+int
+lmc_mdmctl(struct p2pcom *pp, int flag)
+{
+ lmc_softc_t *sc = LMC_UNIT_TO_SOFTC(pp->p2p_if.if_unit);
+
+ sc->lmc_media->set_link_status(sc, flag);
+
+ if (flag)
+ if ((sc->lmc_flags & LMC_IFUP) == 0)
+ lmc_ifup(sc);
+ else
+ if ((sc->lmc_flags & LMC_IFUP) == LMC_IFUP)
+ lmc_ifdown(sc);
+
+ return (0);
+}
+#endif
+
+/*
+ * Set up the OS interface magic and attach to the operating system
+ * network services.
+ */
+void
+lmc_attach(lmc_softc_t * const sc)
+{
+ struct ifnet * const ifp = &sc->lmc_if;
+
+ ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
+ ifp->if_ioctl = lmc_ifioctl;
+ ifp->if_start = lmc_ifstart;
+ ifp->if_watchdog = lmc_watchdog;
+ ifp->if_timer = 1;
+ ifp->if_mtu = LMC_MTU;
+
+#if defined(__bsdi__)
+ ifp->if_type = IFT_NONE;
+ ifp->if_unit = (sc->lmc_dev.dv_unit);
+#endif
+
+ if_attach(ifp);
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ sppp_attach((struct ifnet *)&sc->lmc_sppp);
+ sc->lmc_sppp.pp_flags = PP_CISCO | PP_KEEPALIVE;
+#endif
+#if defined(__bsdi__)
+ sc->lmc_p2pcom.p2p_mdmctl = lmc_mdmctl;
+ sc->lmc_p2pcom.p2p_getmdm = lmc_getmdm;
+ p2p_attach(&sc->lmc_p2pcom);
+#endif
+
+#if NBPFILTER > 0
+ LMC_BPF_ATTACH(sc);
+#endif
+
+#if defined(__NetBSD__) && NRND > 0
+ rnd_attach_source(&sc->lmc_rndsource, sc->lmc_dev.dv_xname,
+ RND_TYPE_NET, 0);
+#endif
+
+ /*
+ * turn off those LEDs...
+ */
+ sc->lmc_miireg16 |= LMC_MII16_LED_ALL;
+ lmc_led_on(sc, LMC_MII16_LED0);
+}
+
+void
+lmc_initring(lmc_softc_t * const sc, lmc_ringinfo_t * const ri,
+ tulip_desc_t *descs, int ndescs)
+{
+ ri->ri_max = ndescs;
+ ri->ri_first = descs;
+ ri->ri_last = ri->ri_first + ri->ri_max;
+ bzero((caddr_t) ri->ri_first, sizeof(ri->ri_first[0]) * ri->ri_max);
+ ri->ri_last[-1].d_flag = TULIP_DFLAG_ENDRING;
+}
diff --git a/sys/dev/pci/if_lmc_common.c b/sys/dev/pci/if_lmc_common.c
new file mode 100644
index 00000000000..a005df8830c
--- /dev/null
+++ b/sys/dev/pci/if_lmc_common.c
@@ -0,0 +1,408 @@
+/* $NetBSD: if_lmc_common.c,v 1.1 1999/03/25 03:32:43 explorer Exp $ */
+
+/*-
+ * Copyright (c) 1997-1999 LAN Media Corporation (LMC)
+ * All rights reserved. www.lanmedia.com
+ *
+ * This code is written by Michael Graff <graff@vix.com> for LMC.
+ * The code is derived from permitted modifications to software created
+ * by Matt Thomas (matt@3am-software.com).
+ *
+ * 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 marketing or advertising materials mentioning features or
+ * use of this software must display the following acknowledgement:
+ * This product includes software developed by LAN Media Corporation
+ * and its contributors.
+ * 4. Neither the name of LAN Media Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION 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 FOUNDATION 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.
+ */
+
+/*-
+ * Copyright (c) 1994-1997 Matt Thomas (matt@3am-software.com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/proc.h> /* only for declaration of wakeup() used by vm.h */
+#if defined(__FreeBSD__)
+#include <machine/clock.h>
+#elif defined(__bsdi__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#endif
+
+#if defined(__NetBSD__)
+#include <dev/pci/pcidevs.h>
+#include "rnd.h"
+#if NRND > 0
+#include <sys/rnd.h>
+#endif
+#endif
+
+#if defined(__OpenBSD__)
+#include <dev/pci/pcidevs.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/netisr.h>
+
+#include "bpfilter.h"
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <net/if_sppp.h>
+#endif
+
+#if defined(__bsdi__)
+#if INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net/netisr.h>
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/if_types.h>
+#include <net/if_p2p.h>
+#include <net/if_c_hdlc.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <vm/pmap.h>
+#include <pci.h>
+#if NPCI > 0
+#include <pci/pcivar.h>
+#include <pci/dc21040reg.h>
+#define INCLUDE_PATH_PREFIX "pci/"
+#endif
+#endif /* __FreeBSD__ */
+
+#if defined(__bsdi__)
+#include <i386/pci/ic/dc21040.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/dma.h>
+#include <i386/isa/isavar.h>
+#include <i386/pci/pci.h>
+
+#define INCLUDE_PATH_PREFIX "i386/pci/"
+#endif /* __bsdi__ */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <machine/bus.h>
+#if defined(__alpha__) && defined(__NetBSD__)
+#include <machine/intr.h>
+#endif
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/ic/dc21040reg.h>
+#define INCLUDE_PATH_PREFIX "dev/pci/"
+#endif /* __NetBSD__ */
+
+#if defined(__OpenBSD__)
+#define d_length1 u.bd_length1
+#define d_length2 u.bd_length2
+#define d_flag u.bd_flag
+#endif
+
+/*
+ * Sigh. Every OS puts these in different places. NetBSD and FreeBSD use
+ * a C preprocessor that allows this hack, but BSDI does not.
+ */
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+#include INCLUDE_PATH_PREFIX "if_lmc_types.h"
+#include INCLUDE_PATH_PREFIX "if_lmcioctl.h"
+#include INCLUDE_PATH_PREFIX "if_lmcvar.h"
+#else /* BSDI */
+#include "i386/pci/if_lmc_types.h"
+#include "i386/pci/if_lmcioctl.h"
+#include "i386/pci/if_lmcvar.h"
+#endif
+
+void
+lmc_gpio_mkinput(lmc_softc_t * const sc, u_int32_t bits)
+{
+ sc->lmc_gpio_io &= ~bits;
+ LMC_CSR_WRITE(sc, csr_gp, TULIP_GP_PINSET | (sc->lmc_gpio_io));
+}
+
+void
+lmc_gpio_mkoutput(lmc_softc_t * const sc, u_int32_t bits)
+{
+ sc->lmc_gpio_io |= bits;
+ LMC_CSR_WRITE(sc, csr_gp, TULIP_GP_PINSET | (sc->lmc_gpio_io));
+}
+
+void
+lmc_led_on(lmc_softc_t * const sc, u_int32_t led)
+{
+ sc->lmc_miireg16 &= ~led;
+ lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
+}
+
+void
+lmc_led_off(lmc_softc_t * const sc, u_int32_t led)
+{
+ sc->lmc_miireg16 |= led;
+ lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
+}
+
+void
+lmc_reset(lmc_softc_t * const sc)
+{
+ sc->lmc_miireg16 |= LMC_MII16_FIFO_RESET;
+ lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
+
+ sc->lmc_miireg16 &= ~LMC_MII16_FIFO_RESET;
+ lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
+
+ /*
+ * make some of the GPIO pins be outputs
+ */
+ lmc_gpio_mkoutput(sc, LMC_GEP_DP | LMC_GEP_RESET);
+
+ /*
+ * drive DP and RESET low to force configuration. This also forces
+ * the transmitter clock to be internal, but we expect to reset
+ * that later anyway.
+ */
+ sc->lmc_gpio &= ~(LMC_GEP_DP | LMC_GEP_RESET);
+ LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
+
+ /*
+ * hold for more than 10 microseconds
+ */
+ DELAY(50);
+
+ /*
+ * stop driving Xilinx-related signals
+ */
+ lmc_gpio_mkinput(sc, LMC_GEP_DP | LMC_GEP_RESET);
+
+ /*
+ * busy wait for the chip to reset
+ */
+ while ((LMC_CSR_READ(sc, csr_gp) & LMC_GEP_DP) == 0)
+ ;
+
+ /*
+ * Call media specific init routine
+ */
+ sc->lmc_media->init(sc);
+}
+
+void
+lmc_dec_reset(lmc_softc_t * const sc)
+{
+#ifndef __linux__
+ lmc_ringinfo_t *ri;
+ tulip_desc_t *di;
+#endif
+ u_int32_t val;
+
+ /*
+ * disable all interrupts
+ */
+ sc->lmc_intrmask = 0;
+ LMC_CSR_WRITE(sc, csr_intr, sc->lmc_intrmask);
+
+ /*
+ * we are, obviously, down.
+ */
+#ifndef __linux__
+ sc->lmc_flags &= ~(LMC_IFUP | LMC_MODEMOK);
+
+ DP(("lmc_dec_reset\n"));
+#endif
+
+ /*
+ * Reset the chip with a software reset command.
+ * Wait 10 microseconds (actually 50 PCI cycles but at
+ * 33MHz that comes to two microseconds but wait a
+ * bit longer anyways)
+ */
+ LMC_CSR_WRITE(sc, csr_busmode, TULIP_BUSMODE_SWRESET);
+ DELAY(10);
+ sc->lmc_cmdmode = LMC_CSR_READ(sc, csr_command);
+
+ /*
+ * We want:
+ * no ethernet address in frames we write
+ * disable padding (txdesc, padding disable)
+ * ignore runt frames (rdes0 bit 15)
+ * no receiver watchdog or transmitter jabber timer
+ * (csr15 bit 0,14 == 1)
+ * if using 16-bit CRC, turn off CRC (trans desc, crc disable)
+ */
+
+#ifndef TULIP_CMD_RECEIVEALL
+#define TULIP_CMD_RECEIVEALL 0x40000000L
+#endif
+
+ sc->lmc_cmdmode |= ( TULIP_CMD_PROMISCUOUS
+ | TULIP_CMD_FULLDUPLEX
+ | TULIP_CMD_PASSBADPKT
+ | TULIP_CMD_NOHEARTBEAT
+ | TULIP_CMD_PORTSELECT
+ | TULIP_CMD_RECEIVEALL
+ | TULIP_CMD_MUSTBEONE
+ );
+ sc->lmc_cmdmode &= ~( TULIP_CMD_OPERMODE
+ | TULIP_CMD_THRESHOLDCTL
+ | TULIP_CMD_STOREFWD
+ | TULIP_CMD_TXTHRSHLDCTL
+ );
+
+ LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode);
+
+ /*
+ * disable receiver watchdog and transmit jabber
+ */
+ val = LMC_CSR_READ(sc, csr_sia_general);
+ val |= (TULIP_WATCHDOG_TXDISABLE | TULIP_WATCHDOG_RXDISABLE);
+ LMC_CSR_WRITE(sc, csr_sia_general, val);
+
+ /*
+ * turn off those LEDs...
+ */
+ sc->lmc_miireg16 |= LMC_MII16_LED_ALL;
+ lmc_led_on(sc, LMC_MII16_LED0);
+
+#ifndef __linux__
+ /*
+ * reprogram the tx desc, rx desc, and PCI bus options
+ */
+ LMC_CSR_WRITE(sc, csr_txlist,
+ LMC_KVATOPHYS(sc, &sc->lmc_txinfo.ri_first[0]));
+ LMC_CSR_WRITE(sc, csr_rxlist,
+ LMC_KVATOPHYS(sc, &sc->lmc_rxinfo.ri_first[0]));
+ LMC_CSR_WRITE(sc, csr_busmode,
+ (1 << (LMC_BURSTSIZE(sc->lmc_unit) + 8))
+ |TULIP_BUSMODE_CACHE_ALIGN8
+ |TULIP_BUSMODE_READMULTIPLE
+ |(BYTE_ORDER != LITTLE_ENDIAN ? TULIP_BUSMODE_BIGENDIAN : 0));
+
+ sc->lmc_txq.ifq_maxlen = LMC_TXDESCS;
+
+ /*
+ * Free all the mbufs that were on the transmit ring.
+ */
+ for (;;) {
+ struct mbuf *m;
+
+ IF_DEQUEUE(&sc->lmc_txq, m);
+ if (m == NULL)
+ break;
+ m_freem(m);
+ }
+
+ /*
+ * reset descriptor state and reclaim all descriptors.
+ */
+ ri = &sc->lmc_txinfo;
+ ri->ri_nextin = ri->ri_nextout = ri->ri_first;
+ ri->ri_free = ri->ri_max;
+ for (di = ri->ri_first; di < ri->ri_last; di++)
+ di->d_status = 0;
+
+ /*
+ * We need to collect all the mbufs were on the
+ * receive ring before we reinit it either to put
+ * them back on or to know if we have to allocate
+ * more.
+ */
+ ri = &sc->lmc_rxinfo;
+ ri->ri_nextin = ri->ri_nextout = ri->ri_first;
+ ri->ri_free = ri->ri_max;
+ for (di = ri->ri_first; di < ri->ri_last; di++) {
+ di->d_status = 0;
+ di->d_length1 = 0; di->d_addr1 = 0;
+ di->d_length2 = 0; di->d_addr2 = 0;
+ }
+ for (;;) {
+ struct mbuf *m;
+ IF_DEQUEUE(&sc->lmc_rxq, m);
+ if (m == NULL)
+ break;
+ m_freem(m);
+ }
+#endif
+}
+
+void
+lmc_initcsrs(lmc_softc_t * const sc, lmc_csrptr_t csr_base,
+ size_t csr_size)
+{
+ sc->lmc_csrs.csr_busmode = csr_base + 0 * csr_size;
+ sc->lmc_csrs.csr_txpoll = csr_base + 1 * csr_size;
+ sc->lmc_csrs.csr_rxpoll = csr_base + 2 * csr_size;
+ sc->lmc_csrs.csr_rxlist = csr_base + 3 * csr_size;
+ sc->lmc_csrs.csr_txlist = csr_base + 4 * csr_size;
+ sc->lmc_csrs.csr_status = csr_base + 5 * csr_size;
+ sc->lmc_csrs.csr_command = csr_base + 6 * csr_size;
+ sc->lmc_csrs.csr_intr = csr_base + 7 * csr_size;
+ sc->lmc_csrs.csr_missed_frames = csr_base + 8 * csr_size;
+ sc->lmc_csrs.csr_9 = csr_base + 9 * csr_size;
+ sc->lmc_csrs.csr_10 = csr_base + 10 * csr_size;
+ sc->lmc_csrs.csr_11 = csr_base + 11 * csr_size;
+ sc->lmc_csrs.csr_12 = csr_base + 12 * csr_size;
+ sc->lmc_csrs.csr_13 = csr_base + 13 * csr_size;
+ sc->lmc_csrs.csr_14 = csr_base + 14 * csr_size;
+ sc->lmc_csrs.csr_15 = csr_base + 15 * csr_size;
+}
diff --git a/sys/dev/pci/if_lmc_media.c b/sys/dev/pci/if_lmc_media.c
new file mode 100644
index 00000000000..7bcfa3bff59
--- /dev/null
+++ b/sys/dev/pci/if_lmc_media.c
@@ -0,0 +1,797 @@
+/* $NetBSD: if_lmc_media.c,v 1.1 1999/03/25 03:32:43 explorer Exp $ */
+
+/*-
+ * Copyright (c) 1997-1999 LAN Media Corporation (LMC)
+ * All rights reserved. www.lanmedia.com
+ *
+ * This code is written by Michael Graff <graff@vix.com> for LMC.
+ *
+ * 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 marketing or advertising materials mentioning features or
+ * use of this software must display the following acknowledgement:
+ * This product includes software developed by LAN Media Corporation
+ * and its contributors.
+ * 4. Neither the name of LAN Media Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION 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 FOUNDATION 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/proc.h> /* only for declaration of wakeup() used by vm.h */
+#if defined(__FreeBSD__)
+#include <machine/clock.h>
+#elif defined(__bsdi__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#endif
+
+#if defined(__NetBSD__)
+#include <dev/pci/pcidevs.h>
+#include "rnd.h"
+#if NRND > 0
+#include <sys/rnd.h>
+#endif
+#endif
+
+#if defined(__OpenBSD__)
+#include <dev/pci/pcidevs.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/netisr.h>
+
+#include "bpfilter.h"
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <net/if_sppp.h>
+#endif
+
+#if defined(__bsdi__)
+#if INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net/netisr.h>
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/if_types.h>
+#include <net/if_p2p.h>
+#include <net/if_c_hdlc.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <vm/pmap.h>
+#include <pci.h>
+#if NPCI > 0
+#include <pci/pcivar.h>
+#include <pci/dc21040reg.h>
+#define INCLUDE_PATH_PREFIX "pci/"
+#endif
+#endif /* __FreeBSD__ */
+
+#if defined(__bsdi__)
+#include <i386/pci/ic/dc21040.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/dma.h>
+#include <i386/isa/isavar.h>
+#include <i386/pci/pci.h>
+
+#define INCLUDE_PATH_PREFIX "i386/pci/"
+#endif /* __bsdi__ */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <machine/bus.h>
+#if defined(__alpha__) && defined(__NetBSD__)
+#include <machine/intr.h>
+#endif
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/ic/dc21040reg.h>
+#define INCLUDE_PATH_PREFIX "dev/pci/"
+#endif /* __NetBSD__ */
+
+/*
+ * Sigh. Every OS puts these in different places. NetBSD and FreeBSD use
+ * a C preprocessor that allows this hack, but BSDI does not.
+ */
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+#include INCLUDE_PATH_PREFIX "if_lmc_types.h"
+#include INCLUDE_PATH_PREFIX "if_lmcioctl.h"
+#include INCLUDE_PATH_PREFIX "if_lmcvar.h"
+#else /* BSDI */
+#include "i386/pci/if_lmc_types.h"
+#include "i386/pci/if_lmcioctl.h"
+#include "i386/pci/if_lmcvar.h"
+#endif
+
+/*
+ * For lack of a better place, put the T1 cable stuff here.
+ */
+char *lmc_t1_cables[] = {
+ "V.10/RS423", "EIA530A", "reserved", "X.21", "V.35",
+ "EIA449/EIA530/V.36", "V.28/EIA232", "none", NULL
+};
+
+/*
+ * protocol independent method.
+ */
+static void lmc_set_protocol(lmc_softc_t * const, lmc_ctl_t *);
+
+/*
+ * media independent methods to check on media status, link, light LEDs,
+ * etc.
+ */
+static void lmc_ds3_init(lmc_softc_t * const);
+static void lmc_ds3_default(lmc_softc_t * const);
+static void lmc_ds3_set_status(lmc_softc_t * const, lmc_ctl_t *);
+static void lmc_ds3_set_100ft(lmc_softc_t * const, int);
+static int lmc_ds3_get_link_status(lmc_softc_t * const);
+static void lmc_ds3_set_crc_length(lmc_softc_t * const, int);
+static void lmc_ds3_set_scram(lmc_softc_t * const, int);
+
+static void lmc_hssi_init(lmc_softc_t * const);
+static void lmc_hssi_default(lmc_softc_t * const);
+static void lmc_hssi_set_status(lmc_softc_t * const, lmc_ctl_t *);
+static void lmc_hssi_set_clock(lmc_softc_t * const, int);
+static int lmc_hssi_get_link_status(lmc_softc_t * const);
+static void lmc_hssi_set_link_status(lmc_softc_t * const, int);
+static void lmc_hssi_set_crc_length(lmc_softc_t * const, int);
+
+static void lmc_t1_init(lmc_softc_t * const);
+static void lmc_t1_default(lmc_softc_t * const);
+static void lmc_t1_set_status(lmc_softc_t * const, lmc_ctl_t *);
+static void lmc_t1_set_clock(lmc_softc_t * const, int);
+static void lmc_t1_set_speed(lmc_softc_t * const, lmc_ctl_t *);
+static int lmc_t1_get_link_status(lmc_softc_t * const);
+static void lmc_t1_set_link_status(lmc_softc_t * const, int);
+static void lmc_t1_set_crc_length(lmc_softc_t * const, int);
+
+static void lmc_dummy_set_1(lmc_softc_t * const, int);
+static void lmc_dummy_set2_1(lmc_softc_t * const, lmc_ctl_t *);
+
+static inline void write_av9110_bit(lmc_softc_t *, int);
+static void write_av9110(lmc_softc_t *, u_int32_t, u_int32_t, u_int32_t,
+ u_int32_t, u_int32_t);
+
+lmc_media_t lmc_ds3_media = {
+ lmc_ds3_init, /* special media init stuff */
+ lmc_ds3_default, /* reset to default state */
+ lmc_ds3_set_status, /* reset status to state provided */
+ lmc_dummy_set_1, /* set clock source */
+ lmc_dummy_set2_1, /* set line speed */
+ lmc_ds3_set_100ft, /* set cable length */
+ lmc_ds3_set_scram, /* set scrambler */
+ lmc_ds3_get_link_status, /* get link status */
+ lmc_dummy_set_1, /* set link status */
+ lmc_ds3_set_crc_length, /* set CRC length */
+};
+
+lmc_media_t lmc_hssi_media = {
+ lmc_hssi_init, /* special media init stuff */
+ lmc_hssi_default, /* reset to default state */
+ lmc_hssi_set_status, /* reset status to state provided */
+ lmc_hssi_set_clock, /* set clock source */
+ lmc_dummy_set2_1, /* set line speed */
+ lmc_dummy_set_1, /* set cable length */
+ lmc_dummy_set_1, /* set scrambler */
+ lmc_hssi_get_link_status, /* get link status */
+ lmc_hssi_set_link_status, /* set link status */
+ lmc_hssi_set_crc_length, /* set CRC length */
+};
+
+lmc_media_t lmc_t1_media = {
+ lmc_t1_init, /* special media init stuff */
+ lmc_t1_default, /* reset to default state */
+ lmc_t1_set_status, /* reset status to state provided */
+ lmc_t1_set_clock, /* set clock source */
+ lmc_t1_set_speed, /* set line speed */
+ lmc_dummy_set_1, /* set cable length */
+ lmc_dummy_set_1, /* set scrambler */
+ lmc_t1_get_link_status, /* get link status */
+ lmc_t1_set_link_status, /* set link status */
+ lmc_t1_set_crc_length, /* set CRC length */
+};
+
+static void
+lmc_dummy_set_1(lmc_softc_t * const sc, int a)
+{
+}
+
+static void
+lmc_dummy_set2_1(lmc_softc_t * const sc, lmc_ctl_t *a)
+{
+}
+
+/*
+ * HSSI methods
+ */
+
+static void
+lmc_hssi_init(lmc_softc_t * const sc)
+{
+ sc->ictl.cardtype = LMC_CTL_CARDTYPE_LMC5200;
+
+ lmc_gpio_mkoutput(sc, LMC_GEP_HSSI_CLOCK);
+}
+
+static void
+lmc_hssi_default(lmc_softc_t * const sc)
+{
+ sc->lmc_miireg16 = LMC_MII16_LED_ALL;
+
+ sc->lmc_media->set_link_status(sc, 0);
+ sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_EXT);
+ sc->lmc_media->set_crc_length(sc, LMC_CTL_CRC_LENGTH_16);
+}
+
+/*
+ * Given a user provided state, set ourselves up to match it. This will
+ * always reset the card if needed.
+ */
+static void
+lmc_hssi_set_status(lmc_softc_t * const sc, lmc_ctl_t *ctl)
+{
+ if (ctl == NULL) {
+ sc->lmc_media->set_clock_source(sc, sc->ictl.clock_source);
+ lmc_set_protocol(sc, NULL);
+
+ return;
+ }
+
+ /*
+ * check for change in clock source
+ */
+ if (ctl->clock_source && !sc->ictl.clock_source)
+ sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_INT);
+ else if (!ctl->clock_source && sc->ictl.clock_source)
+ sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_EXT);
+
+ lmc_set_protocol(sc, ctl);
+}
+
+/*
+ * 1 == internal, 0 == external
+ */
+static void
+lmc_hssi_set_clock(lmc_softc_t * const sc, int ie)
+{
+ if (ie == LMC_CTL_CLOCK_SOURCE_EXT) {
+ sc->lmc_gpio |= LMC_GEP_HSSI_CLOCK;
+ LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
+ sc->ictl.clock_source = LMC_CTL_CLOCK_SOURCE_EXT;
+ printf(LMC_PRINTF_FMT ": clock external\n",
+ LMC_PRINTF_ARGS);
+ } else {
+ sc->lmc_gpio &= ~(LMC_GEP_HSSI_CLOCK);
+ LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
+ sc->ictl.clock_source = LMC_CTL_CLOCK_SOURCE_INT;
+ printf(LMC_PRINTF_FMT ": clock internal\n",
+ LMC_PRINTF_ARGS);
+ }
+}
+
+/*
+ * return hardware link status.
+ * 0 == link is down, 1 == link is up.
+ */
+static int
+lmc_hssi_get_link_status(lmc_softc_t * const sc)
+{
+ u_int16_t link_status;
+
+ link_status = lmc_mii_readreg(sc, 0, 16);
+
+ if ((link_status & LMC_MII16_HSSI_CA) == LMC_MII16_HSSI_CA)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+lmc_hssi_set_link_status(lmc_softc_t * const sc, int state)
+{
+ if (state)
+ sc->lmc_miireg16 |= LMC_MII16_HSSI_TA;
+ else
+ sc->lmc_miireg16 &= ~LMC_MII16_HSSI_TA;
+
+ lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
+}
+
+/*
+ * 0 == 16bit, 1 == 32bit
+ */
+static void
+lmc_hssi_set_crc_length(lmc_softc_t * const sc, int state)
+{
+ if (state == LMC_CTL_CRC_LENGTH_32) {
+ /* 32 bit */
+ sc->lmc_miireg16 |= LMC_MII16_HSSI_CRC;
+ sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_32;
+ } else {
+ /* 16 bit */
+ sc->lmc_miireg16 &= ~LMC_MII16_HSSI_CRC;
+ sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_16;
+ }
+
+ lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
+}
+
+
+/*
+ * DS3 methods
+ */
+
+/*
+ * Set cable length
+ */
+static void
+lmc_ds3_set_100ft(lmc_softc_t * const sc, int ie)
+{
+ if (ie == LMC_CTL_CABLE_LENGTH_GT_100FT) {
+ sc->lmc_miireg16 &= ~LMC_MII16_DS3_ZERO;
+ sc->ictl.cable_length = LMC_CTL_CABLE_LENGTH_GT_100FT;
+ } else if (ie == LMC_CTL_CABLE_LENGTH_LT_100FT) {
+ sc->lmc_miireg16 |= LMC_MII16_DS3_ZERO;
+ sc->ictl.cable_length = LMC_CTL_CABLE_LENGTH_LT_100FT;
+ }
+ lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
+}
+
+static void
+lmc_ds3_default(lmc_softc_t * const sc)
+{
+ sc->lmc_miireg16 = LMC_MII16_LED_ALL;
+
+ sc->lmc_media->set_link_status(sc, 0);
+ sc->lmc_media->set_cable_length(sc, LMC_CTL_CABLE_LENGTH_LT_100FT);
+ sc->lmc_media->set_scrambler(sc, LMC_CTL_OFF);
+ sc->lmc_media->set_crc_length(sc, LMC_CTL_CRC_LENGTH_16);
+}
+
+/*
+ * Given a user provided state, set ourselves up to match it. This will
+ * always reset the card if needed.
+ */
+static void
+lmc_ds3_set_status(lmc_softc_t * const sc, lmc_ctl_t *ctl)
+{
+ if (ctl == NULL) {
+ sc->lmc_media->set_cable_length(sc, sc->ictl.cable_length);
+ sc->lmc_media->set_scrambler(sc, sc->ictl.scrambler_onoff);
+ lmc_set_protocol(sc, NULL);
+
+ return;
+ }
+
+ /*
+ * check for change in cable length setting
+ */
+ if (ctl->cable_length && !sc->ictl.cable_length)
+ lmc_ds3_set_100ft(sc, LMC_CTL_CABLE_LENGTH_GT_100FT);
+ else if (!ctl->cable_length && sc->ictl.cable_length)
+ lmc_ds3_set_100ft(sc, LMC_CTL_CABLE_LENGTH_LT_100FT);
+
+ /*
+ * Check for change in scrambler setting (requires reset)
+ */
+ if (ctl->scrambler_onoff && !sc->ictl.scrambler_onoff)
+ lmc_ds3_set_scram(sc, LMC_CTL_ON);
+ else if (!ctl->scrambler_onoff && sc->ictl.scrambler_onoff)
+ lmc_ds3_set_scram(sc, LMC_CTL_OFF);
+
+ lmc_set_protocol(sc, ctl);
+}
+
+static void
+lmc_ds3_init(lmc_softc_t * const sc)
+{
+ int i;
+
+ sc->ictl.cardtype = LMC_CTL_CARDTYPE_LMC5245;
+
+ /* writes zeros everywhere */
+ for (i = 0 ; i < 21 ; i++) {
+ lmc_mii_writereg(sc, 0, 17, i);
+ lmc_mii_writereg(sc, 0, 18, 0);
+ }
+
+ /* set some essential bits */
+ lmc_mii_writereg(sc, 0, 17, 1);
+ lmc_mii_writereg(sc, 0, 18, 0x05); /* ser, xtx */
+
+ lmc_mii_writereg(sc, 0, 17, 5);
+ lmc_mii_writereg(sc, 0, 18, 0x80); /* emode */
+
+ lmc_mii_writereg(sc, 0, 17, 14);
+ lmc_mii_writereg(sc, 0, 18, 0x30); /* rcgen, tcgen */
+
+ /* clear counters and latched bits */
+ for (i = 0 ; i < 21 ; i++) {
+ lmc_mii_writereg(sc, 0, 17, i);
+ lmc_mii_readreg(sc, 0, 18);
+ }
+}
+
+/*
+ * 1 == DS3 payload scrambled, 0 == not scrambled
+ */
+static void
+lmc_ds3_set_scram(lmc_softc_t * const sc, int ie)
+{
+ if (ie == LMC_CTL_ON) {
+ sc->lmc_miireg16 |= LMC_MII16_DS3_SCRAM;
+ sc->ictl.scrambler_onoff = LMC_CTL_ON;
+ } else {
+ sc->lmc_miireg16 &= ~LMC_MII16_DS3_SCRAM;
+ sc->ictl.scrambler_onoff = LMC_CTL_OFF;
+ }
+ lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
+}
+
+/*
+ * return hardware link status.
+ * 0 == link is down, 1 == link is up.
+ */
+static int
+lmc_ds3_get_link_status(lmc_softc_t * const sc)
+{
+ u_int16_t link_status;
+
+ lmc_mii_writereg(sc, 0, 17, 7);
+ link_status = lmc_mii_readreg(sc, 0, 18);
+
+ if ((link_status & LMC_FRAMER_REG0_DLOS) == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * 0 == 16bit, 1 == 32bit
+ */
+static void
+lmc_ds3_set_crc_length(lmc_softc_t * const sc, int state)
+{
+ if (state == LMC_CTL_CRC_LENGTH_32) {
+ /* 32 bit */
+ sc->lmc_miireg16 |= LMC_MII16_DS3_CRC;
+ sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_32;
+ } else {
+ /* 16 bit */
+ sc->lmc_miireg16 &= ~LMC_MII16_DS3_CRC;
+ sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_16;
+ }
+
+ lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
+}
+
+
+/*
+ * T1 methods
+ */
+
+static void
+lmc_t1_init(lmc_softc_t * const sc)
+{
+ u_int16_t mii17;
+ int cable;
+
+ sc->ictl.cardtype = LMC_CTL_CARDTYPE_LMC1000;
+
+ mii17 = lmc_mii_readreg(sc, 0, 17);
+
+ cable = (mii17 & LMC_MII17_T1_CABLE_MASK) >> LMC_MII17_T1_CABLE_SHIFT;
+ sc->ictl.cable_type = cable;
+
+ lmc_gpio_mkoutput(sc, LMC_GEP_T1_TXCLOCK);
+}
+
+static void
+lmc_t1_default(lmc_softc_t * const sc)
+{
+ sc->lmc_miireg16 = LMC_MII16_LED_ALL;
+
+ /*
+ * make TXCLOCK always be an output
+ */
+ lmc_gpio_mkoutput(sc, LMC_GEP_T1_TXCLOCK);
+
+ sc->lmc_media->set_link_status(sc, 0);
+ sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_EXT);
+ sc->lmc_media->set_speed(sc, NULL);
+ sc->lmc_media->set_crc_length(sc, LMC_CTL_CRC_LENGTH_16);
+}
+
+/*
+ * Given a user provided state, set ourselves up to match it. This will
+ * always reset the card if needed.
+ */
+static void
+lmc_t1_set_status(lmc_softc_t * const sc, lmc_ctl_t *ctl)
+{
+ if (ctl == NULL) {
+ sc->lmc_media->set_clock_source(sc, sc->ictl.clock_source);
+ sc->lmc_media->set_speed(sc, &sc->ictl);
+ lmc_set_protocol(sc, NULL);
+
+ return;
+ }
+
+ /*
+ * check for change in clock source
+ */
+ if (ctl->clock_source == LMC_CTL_CLOCK_SOURCE_INT
+ && sc->ictl.clock_source == LMC_CTL_CLOCK_SOURCE_EXT)
+ sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_INT);
+ else if (ctl->clock_source == LMC_CTL_CLOCK_SOURCE_EXT
+ && sc->ictl.clock_source == LMC_CTL_CLOCK_SOURCE_INT)
+ sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_EXT);
+
+ if (ctl->clock_rate != sc->ictl.clock_rate)
+ sc->lmc_media->set_speed(sc, ctl);
+
+ lmc_set_protocol(sc, ctl);
+}
+
+/*
+ * 1 == internal, 0 == external
+ */
+static void
+lmc_t1_set_clock(lmc_softc_t * const sc, int ie)
+{
+ if (ie == LMC_CTL_CLOCK_SOURCE_EXT) {
+ sc->lmc_gpio &= ~(LMC_GEP_T1_TXCLOCK);
+ LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
+ sc->ictl.clock_source = LMC_CTL_CLOCK_SOURCE_EXT;
+ printf(LMC_PRINTF_FMT ": clock external\n",
+ LMC_PRINTF_ARGS);
+ } else {
+ sc->lmc_gpio |= LMC_GEP_T1_TXCLOCK;
+ LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
+ sc->ictl.clock_source = LMC_CTL_CLOCK_SOURCE_INT;
+ printf(LMC_PRINTF_FMT ": clock internal\n",
+ LMC_PRINTF_ARGS);
+ }
+}
+
+static void
+lmc_t1_set_speed(lmc_softc_t * const sc, lmc_ctl_t *ctl)
+{
+ lmc_ctl_t *ictl = &sc->ictl;
+ lmc_av9110_t *av;
+
+ if (ctl == NULL) {
+ av = &ictl->cardspec.t1;
+ ictl->clock_rate = 100000;
+ av->f = ictl->clock_rate;
+ av->n = 8;
+ av->m = 25;
+ av->v = 0;
+ av->x = 0;
+ av->r = 2;
+
+ write_av9110(sc, av->n, av->m, av->v, av->x, av->r);
+ return;
+ }
+
+ av = &ctl->cardspec.t1;
+
+ if (av->f == 0)
+ return;
+
+ ictl->clock_rate = av->f; /* really, this is the rate we are */
+ ictl->cardspec.t1 = *av;
+
+ write_av9110(sc, av->n, av->m, av->v, av->x, av->r);
+}
+
+/*
+ * return hardware link status.
+ * 0 == link is down, 1 == link is up.
+ */
+static int
+lmc_t1_get_link_status(lmc_softc_t * const sc)
+{
+ u_int16_t link_status;
+
+ /*
+ * missing CTS? Hmm. If we require CTS on, we may never get the
+ * link to come up, so omit it in this test.
+ *
+ * Also, it seems that with a loopback cable, DCD isn't asserted,
+ * so just check for things like this:
+ * DSR _must_ be asserted.
+ * One of DCD or CTS must be asserted.
+ */
+ link_status = lmc_mii_readreg(sc, 0, 16);
+
+ if ((link_status & LMC_MII16_T1_DSR) == 0)
+ return (0);
+
+ if ((link_status & (LMC_MII16_T1_CTS | LMC_MII16_T1_DCD)) == 0)
+ return (0);
+
+ return (1);
+}
+
+static void
+lmc_t1_set_link_status(lmc_softc_t * const sc, int state)
+{
+ if (state) {
+ sc->lmc_miireg16 |= (LMC_MII16_T1_DTR | LMC_MII16_T1_RTS);
+ printf(LMC_PRINTF_FMT ": asserting DTR and RTS\n",
+ LMC_PRINTF_ARGS);
+ } else {
+ sc->lmc_miireg16 &= ~(LMC_MII16_T1_DTR | LMC_MII16_T1_RTS);
+ printf(LMC_PRINTF_FMT ": deasserting DTR and RTS\n",
+ LMC_PRINTF_ARGS);
+ }
+
+ lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
+}
+
+/*
+ * 0 == 16bit, 1 == 32bit
+ */
+static void
+lmc_t1_set_crc_length(lmc_softc_t * const sc, int state)
+{
+ if (state == LMC_CTL_CRC_LENGTH_32) {
+ /* 32 bit */
+ sc->lmc_miireg16 |= LMC_MII16_T1_CRC;
+ sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_32;
+ } else {
+ /* 16 bit */
+ sc->lmc_miireg16 &= ~LMC_MII16_T1_CRC;
+ sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_16;
+ }
+
+ lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
+}
+
+/*
+ * These are bits to program the T1 frequency generator
+ */
+static inline void
+write_av9110_bit(lmc_softc_t *sc, int c)
+{
+ /*
+ * set the data bit as we need it.
+ */
+ sc->lmc_gpio &= ~(LMC_GEP_SERIALCLK);
+ if (c & 0x01)
+ sc->lmc_gpio |= LMC_GEP_SERIAL;
+ else
+ sc->lmc_gpio &= ~(LMC_GEP_SERIAL);
+ LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
+
+ /*
+ * set the clock to high
+ */
+ sc->lmc_gpio |= LMC_GEP_SERIALCLK;
+ LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
+
+ /*
+ * set the clock to low again.
+ */
+ sc->lmc_gpio &= ~(LMC_GEP_SERIALCLK);
+ LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
+}
+
+static void
+write_av9110(lmc_softc_t *sc, u_int32_t n, u_int32_t m, u_int32_t v,
+ u_int32_t x, u_int32_t r)
+{
+ int i;
+
+#if 0
+ printf(LMC_PRINTF_FMT ": speed %u, %d %d %d %d %d\n",
+ LMC_PRINTF_ARGS, sc->ictl.clock_rate,
+ n, m, v, x, r);
+#endif
+
+ sc->lmc_gpio |= LMC_GEP_T1_GENERATOR;
+ sc->lmc_gpio &= ~(LMC_GEP_SERIAL | LMC_GEP_SERIALCLK);
+ LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
+
+ /*
+ * Set the TXCLOCK, GENERATOR, SERIAL, and SERIALCLK
+ * as outputs.
+ */
+ lmc_gpio_mkoutput(sc, (LMC_GEP_SERIAL | LMC_GEP_SERIALCLK
+ | LMC_GEP_T1_GENERATOR));
+
+ sc->lmc_gpio &= ~(LMC_GEP_T1_GENERATOR);
+ LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
+
+ /*
+ * a shifting we will go...
+ */
+ for (i = 0 ; i < 7 ; i++)
+ write_av9110_bit(sc, n >> i);
+ for (i = 0 ; i < 7 ; i++)
+ write_av9110_bit(sc, m >> i);
+ for (i = 0 ; i < 1 ; i++)
+ write_av9110_bit(sc, v >> i);
+ for (i = 0 ; i < 2 ; i++)
+ write_av9110_bit(sc, x >> i);
+ for (i = 0 ; i < 2 ; i++)
+ write_av9110_bit(sc, r >> i);
+ for (i = 0 ; i < 5 ; i++)
+ write_av9110_bit(sc, 0x17 >> i);
+
+ /*
+ * stop driving serial-related signals
+ */
+ lmc_gpio_mkinput(sc,
+ (LMC_GEP_SERIAL | LMC_GEP_SERIALCLK
+ | LMC_GEP_T1_GENERATOR));
+}
+
+static void
+lmc_set_protocol(lmc_softc_t * const sc, lmc_ctl_t *ctl)
+{
+ if (ctl == 0) {
+ sc->ictl.keepalive_onoff = LMC_CTL_ON;
+
+ return;
+ }
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ if (ctl->keepalive_onoff != sc->ictl.keepalive_onoff) {
+ switch (ctl->keepalive_onoff) {
+ case LMC_CTL_ON:
+ printf(LMC_PRINTF_FMT ": enabling keepalive\n",
+ LMC_PRINTF_ARGS);
+ sc->ictl.keepalive_onoff = LMC_CTL_ON;
+ sc->lmc_sppp.pp_flags = PP_CISCO | PP_KEEPALIVE;
+ break;
+ case LMC_CTL_OFF:
+ printf(LMC_PRINTF_FMT ": disabling keepalive\n",
+ LMC_PRINTF_ARGS);
+ sc->ictl.keepalive_onoff = LMC_CTL_OFF;
+ sc->lmc_sppp.pp_flags = PP_CISCO;
+ }
+ }
+#endif
+}
diff --git a/sys/dev/pci/if_lmc_obsd.c b/sys/dev/pci/if_lmc_obsd.c
new file mode 100644
index 00000000000..534616400d2
--- /dev/null
+++ b/sys/dev/pci/if_lmc_obsd.c
@@ -0,0 +1,427 @@
+/* $NetBSD: if_lmc_nbsd.c,v 1.1 1999/03/25 03:32:43 explorer Exp $ */
+
+/*-
+ * Copyright (c) 1997-1999 LAN Media Corporation (LMC)
+ * All rights reserved. www.lanmedia.com
+ *
+ * This code is written by Michael Graff <graff@vix.com> for LMC.
+ * The code is derived from permitted modifications to software created
+ * by Matt Thomas (matt@3am-software.com).
+ *
+ * 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 marketing or advertising materials mentioning features or
+ * use of this software must display the following acknowledgement:
+ * This product includes software developed by LAN Media Corporation
+ * and its contributors.
+ * 4. Neither the name of LAN Media Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION 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 FOUNDATION 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.
+ */
+
+/*-
+ * Copyright (c) 1994-1997 Matt Thomas (matt@3am-software.com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/proc.h> /* only for declaration of wakeup() used by vm.h */
+#if defined(__FreeBSD__)
+#include <machine/clock.h>
+#elif defined(__bsdi__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#endif
+
+#if defined(__NetBSD__)
+#include <dev/pci/pcidevs.h>
+#include "rnd.h"
+#if NRND > 0
+#include <sys/rnd.h>
+#endif
+#endif
+
+#if defined(__OpenBSD__)
+#include <dev/pci/pcidevs.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/netisr.h>
+
+#include "bpfilter.h"
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <net/if_sppp.h>
+#endif
+
+#if defined(__bsdi__)
+#if INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net/netisr.h>
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/if_types.h>
+#include <net/if_p2p.h>
+#include <net/if_c_hdlc.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <vm/pmap.h>
+#include <pci.h>
+#if NPCI > 0
+#include <pci/pcivar.h>
+#include <pci/dc21040reg.h>
+#define INCLUDE_PATH_PREFIX "pci/"
+#endif
+#endif /* __FreeBSD__ */
+
+#if defined(__bsdi__)
+#include <i386/pci/ic/dc21040.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/dma.h>
+#include <i386/isa/isavar.h>
+#include <i386/pci/pci.h>
+
+#define INCLUDE_PATH_PREFIX "i386/pci/"
+#endif /* __bsdi__ */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <machine/bus.h>
+#if defined(__alpha__) && defined(__NetBSD__)
+#include <machine/intr.h>
+#endif
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/ic/dc21040reg.h>
+#define INCLUDE_PATH_PREFIX "dev/pci/"
+#endif /* __NetBSD__ */
+
+/*
+ * Sigh. Every OS puts these in different places. NetBSD and FreeBSD use
+ * a C preprocessor that allows this hack, but BSDI does not. Grr.
+ */
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+#include INCLUDE_PATH_PREFIX "if_lmc_types.h"
+#include INCLUDE_PATH_PREFIX "if_lmcioctl.h"
+#include INCLUDE_PATH_PREFIX "if_lmcvar.h"
+#else /* BSDI */
+#include "i386/pci/if_lmctypes.h"
+#include "i386/pci/if_lmcioctl.h"
+#include "i386/pci/if_lmcvar.h"
+#endif
+
+/*
+ * This file is INCLUDED (gross, I know, but...)
+ */
+
+static void lmc_shutdown(void *arg);
+
+static int
+lmc_pci_probe(struct device *parent,
+#if defined (__BROKEN_INDIRECT_CONFIG) || defined(__OpenBSD__)
+ void *match,
+#else
+ struct cfdata *match,
+#endif
+ void *aux)
+{
+ struct pci_attach_args *pa = (struct pci_attach_args *)aux;
+ u_int32_t id;
+
+ /*
+ * check first for the DEC chip we expect to find. We expect
+ * 21140A, pass 2.2 or higher.
+ */
+ if (PCI_VENDORID(pa->pa_id) != PCI_VENDOR_DEC)
+ return 0;
+ if (PCI_CHIPID(pa->pa_id) != PCI_PRODUCT_DEC_21140)
+ return 0;
+ id = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CFRV) & 0xff;
+ if (id < 0x22)
+ return 0;
+
+ /*
+ * Next, check the subsystem ID and see if it matches what we
+ * expect.
+ */
+ id = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SSID);
+ if (PCI_VENDORID(id) != PCI_VENDOR_LMC)
+ return 0;
+ if ((PCI_CHIPID(id) != PCI_PRODUCT_LMC_HSSI)
+ && (PCI_CHIPID(id) != PCI_PRODUCT_LMC_DS3)
+ && (PCI_CHIPID(id) != PCI_PRODUCT_LMC_SSI))
+ return 0;
+
+ return 10; /* must be > than any other tulip driver */
+}
+
+static void lmc_pci_attach(struct device * const parent,
+ struct device * const self, void * const aux);
+
+struct cfattach lmc_ca = {
+ sizeof(lmc_softc_t), lmc_pci_probe, lmc_pci_attach
+};
+
+struct cfdriver lmc_cd = {
+ 0, "lmc", DV_IFNET
+};
+
+static void
+lmc_pci_attach(struct device * const parent,
+ struct device * const self, void * const aux)
+{
+ u_int32_t revinfo, cfdainfo, id, ssid;
+ pci_intr_handle_t intrhandle;
+ const char *intrstr;
+#if !defined(LMC_IOMAPPED)
+ vm_offset_t pa_csrs;
+#endif
+ unsigned csroffset = LMC_PCI_CSROFFSET;
+ unsigned csrsize = LMC_PCI_CSRSIZE;
+ lmc_csrptr_t csr_base;
+ lmc_spl_t s;
+ lmc_intrfunc_t (*intr_rtn)(void *) = lmc_intr_normal;
+ lmc_softc_t * const sc = (lmc_softc_t *) self;
+ struct pci_attach_args * const pa = (struct pci_attach_args *) aux;
+ extern lmc_media_t lmc_hssi_media;
+ extern lmc_media_t lmc_ds3_media;
+ extern lmc_media_t lmc_t1_media;
+
+ revinfo = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CFRV) & 0xFF;
+ id = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CFID);
+ cfdainfo = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CFDA);
+ ssid = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SSID);
+
+ switch (PCI_CHIPID(ssid)) {
+ case PCI_PRODUCT_LMC_HSSI:
+ printf(": Lan Media Corporation HSSI\n");
+ sc->lmc_media = &lmc_hssi_media;
+ break;
+ case PCI_PRODUCT_LMC_DS3:
+ printf(": Lan Media Corporation DS3\n");
+ sc->lmc_media = &lmc_ds3_media;
+ break;
+ case PCI_PRODUCT_LMC_SSI:
+ printf(": Lan Media Corporation SSI\n");
+ sc->lmc_media = &lmc_t1_media;
+ break;
+ }
+
+ sc->lmc_pci_busno = parent;
+ sc->lmc_pci_devno = pa->pa_device;
+
+ sc->lmc_chipid = LMC_21140A;
+ sc->lmc_features |= LMC_HAVE_STOREFWD;
+ if (sc->lmc_chipid == LMC_21140A && revinfo <= 0x22)
+ sc->lmc_features |= LMC_HAVE_RXBADOVRFLW;
+
+ if (cfdainfo & (TULIP_CFDA_SLEEP | TULIP_CFDA_SNOOZE)) {
+ cfdainfo &= ~(TULIP_CFDA_SLEEP | TULIP_CFDA_SNOOZE);
+ pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_CFDA, cfdainfo);
+ DELAY(11 * 1000);
+ }
+
+ bcopy(self->dv_xname, sc->lmc_if.if_xname, IFNAMSIZ);
+ sc->lmc_if.if_softc = sc;
+ sc->lmc_pc = pa->pa_pc;
+
+ sc->lmc_revinfo = revinfo;
+ sc->lmc_if.if_softc = sc;
+
+ csr_base = 0;
+ {
+ bus_space_tag_t iot, memt;
+ bus_space_handle_t ioh, memh;
+ int ioh_valid, memh_valid;
+
+#if defined(__OpenBSD__)
+ pci_chipset_tag_t pc = pa->pa_pc;
+ bus_addr_t lmcbase;
+ bus_size_t lmcsize;
+ int retval;
+#endif
+
+#if defined(__NetBSD__)
+
+ ioh_valid = (pci_mapreg_map(pa, PCI_CBIO, PCI_MAPREG_TYPE_IO,
+ 0, &iot, &ioh, NULL, NULL) == 0);
+ memh_valid = (pci_mapreg_map(pa, PCI_CBMA,
+ PCI_MAPREG_TYPE_MEM |
+ PCI_MAPREG_MEM_TYPE_32BIT,
+ 0, &memt, &memh, NULL,
+ NULL) == 0);
+#elif defined(__OpenBSD__)
+ ioh_valid = 0;
+ memh_valid = 0;
+ csr_base = 0;
+
+ iot = pa->pa_iot;
+ retval = pci_io_find(pc, pa->pa_tag, PCI_CBIO, &lmcbase, &lmcsize);
+
+ if(!retval)
+ retval = bus_space_map(pa->pa_iot, lmcbase, lmcsize, 0, &ioh);
+ ioh_valid = (retval == 0);
+
+ if(!ioh_valid) { /* Can this if just be removed? */
+ memt = pa->pa_memt;
+ retval = pci_mem_find(pc, pa->pa_tag, PCI_CBMA, &lmcbase, &lmcsize, NULL);
+ if(!retval)
+ retval = bus_space_map(pa->pa_memt, lmcbase, lmcsize, 0, &memh);
+
+ memh_valid = (retval == 0);
+ }
+#endif
+
+
+ if (memh_valid) {
+ sc->lmc_bustag = memt;
+ sc->lmc_bushandle = memh;
+ } else if (ioh_valid) {
+ sc->lmc_bustag = iot;
+ sc->lmc_bushandle = ioh;
+ } else {
+ printf("%s: unable to map device registers\n",
+ sc->lmc_dev.dv_xname);
+ return;
+ }
+ }
+
+ lmc_initcsrs(sc, csr_base + csroffset, csrsize);
+ lmc_initring(sc, &sc->lmc_rxinfo, sc->lmc_rxdescs,
+ LMC_RXDESCS);
+ lmc_initring(sc, &sc->lmc_txinfo, sc->lmc_txdescs,
+ LMC_TXDESCS);
+
+ lmc_gpio_mkinput(sc, 0xff);
+ sc->lmc_gpio = 0; /* drive no signals yet */
+
+ sc->lmc_media->defaults(sc);
+
+ sc->lmc_media->set_link_status(sc, 0); /* down */
+
+ /*
+ * Make sure there won't be any interrupts or such...
+ */
+ LMC_CSR_WRITE(sc, csr_busmode, TULIP_BUSMODE_SWRESET);
+
+ /*
+ * Wait 10 microseconds (actually 50 PCI cycles but at
+ * 33MHz that comes to two microseconds but wait a
+ * bit longer anyways)
+ */
+ DELAY(100);
+
+ lmc_read_macaddr(sc);
+ printf("%s: pass %d.%d, serial " LMC_EADDR_FMT "\n",
+ sc->lmc_dev.dv_xname,
+ (sc->lmc_revinfo & 0xF0) >> 4, sc->lmc_revinfo & 0x0F,
+ LMC_EADDR_ARGS(sc->lmc_enaddr));
+
+ if (pci_intr_map(pa->pa_pc, pa->pa_intrtag, pa->pa_intrpin,
+ pa->pa_intrline, &intrhandle)) {
+ printf("%s: couldn't map interrupt\n",
+ sc->lmc_dev.dv_xname);
+ return;
+ }
+ intrstr = pci_intr_string(pa->pa_pc, intrhandle);
+
+#if defined(__OpenBSD__)
+ sc->lmc_ih = pci_intr_establish(pa->pa_pc, intrhandle, IPL_NET,
+ intr_rtn, sc, self->dv_xname);
+#else
+ sc->lmc_ih = pci_intr_establish(pa->pa_pc, intrhandle, IPL_NET,
+ intr_rtn, sc);
+#endif
+
+ if (sc->lmc_ih == NULL) {
+ printf("%s: couldn't establish interrupt",
+ sc->lmc_dev.dv_xname);
+ if (intrstr != NULL)
+ printf(" at %s", intrstr);
+ printf("\n");
+ return;
+ }
+ printf("%s: interrupting at %s\n", sc->lmc_dev.dv_xname,
+ intrstr);
+
+ sc->lmc_ats = shutdownhook_establish(lmc_shutdown, sc);
+ if (sc->lmc_ats == NULL)
+ printf("%s: warning: couldn't establish shutdown hook\n",
+ sc->lmc_xname);
+
+ s = LMC_RAISESPL();
+ lmc_dec_reset(sc);
+ lmc_reset(sc);
+ lmc_attach(sc);
+ LMC_RESTORESPL(s);
+}
+
+static void
+lmc_shutdown(void *arg)
+{
+ lmc_softc_t * const sc = arg;
+ LMC_CSR_WRITE(sc, csr_busmode, TULIP_BUSMODE_SWRESET);
+ DELAY(10);
+
+ sc->lmc_miireg16 = 0; /* deassert ready, and all others too */
+ lmc_led_on(sc, LMC_MII16_LED_ALL);
+}
diff --git a/sys/dev/pci/if_lmc_types.h b/sys/dev/pci/if_lmc_types.h
new file mode 100644
index 00000000000..8858f1c9b05
--- /dev/null
+++ b/sys/dev/pci/if_lmc_types.h
@@ -0,0 +1,88 @@
+/* $NetBSD: if_lmc_types.h,v 1.2 1999/03/25 04:09:33 explorer Exp $ */
+
+/*-
+ * Copyright (c) 1997-1999 LAN Media Corporation (LMC)
+ * All rights reserved. www.lanmedia.com
+ *
+ * This code is written by Michael Graff <graff@vix.com> for LMC.
+ * The code is derived from permitted modifications to software created
+ * by Matt Thomas (matt@3am-software.com).
+ *
+ * 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 marketing or advertising materials mentioning features or
+ * use of this software must display the following acknowledgement:
+ * This product includes software developed by LAN Media Corporation
+ * and its contributors.
+ * 4. Neither the name of LAN Media Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION 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 FOUNDATION 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.
+ */
+
+#ifndef LMC_TYPES_H
+#define LMC_TYPES_H
+
+/*
+ * NetBSD uses _KERNEL, FreeBSD uses KERNEL.
+ */
+#if defined(_KERNEL) && (defined(__NetBSD__) || defined(__OpenBSD__))
+#define LMC_IS_KERNEL
+#endif
+#if defined(KERNEL) && defined(__FreeBSD__)
+#define LMC_IS_KERNEL
+#endif
+#if defined(__KERNEL__) && defined(linux)
+#define LMC_IS_KERNEL
+#endif
+
+#if defined(LMC_IS_KERNEL)
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+typedef bus_addr_t lmc_csrptr_t;
+#else
+#ifdef LMC_IOMAPPED
+typedef u_int16_t lmc_csrptr_t;
+#else
+typedef volatile u_int32_t *lmc_csrptr_t;
+#endif
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#define lmc_intrfunc_t int
+#endif /* __NetBSD__ */
+
+#if defined(__FreeBSD__)
+#define lmc_intrfunc_t void
+#endif /* __FreeBSD__ */
+
+#if defined(__bsdi__)
+#define lmc_intrfunc_t int
+#endif /* __bsdi__ */
+
+typedef struct lmc___softc lmc_softc_t;
+typedef struct lmc___media lmc_media_t;
+typedef struct lmc_ringinfo lmc_ringinfo_t;
+
+#endif /* LMC_IS_KERNEL */
+
+typedef struct lmc___ctl lmc_ctl_t;
+
+#endif /* LMC_TYPES_H */
diff --git a/sys/dev/pci/if_lmcioctl.h b/sys/dev/pci/if_lmcioctl.h
new file mode 100644
index 00000000000..f1921d1f502
--- /dev/null
+++ b/sys/dev/pci/if_lmcioctl.h
@@ -0,0 +1,243 @@
+/* $NetBSD: if_lmcioctl.h,v 1.2 1999/03/25 04:09:34 explorer Exp $ */
+
+/*-
+ * Copyright (c) 1997-1999 LAN Media Corporation (LMC)
+ * All rights reserved. www.lanmedia.com
+ *
+ * This code is written by Michael Graff <graff@vix.com> for LMC.
+ * The code is derived from permitted modifications to software created
+ * by Matt Thomas (matt@3am-software.com).
+ *
+ * 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 marketing or advertising materials mentioning features or
+ * use of this software must display the following acknowledgement:
+ * This product includes software developed by LAN Media Corporation
+ * and its contributors.
+ * 4. Neither the name of LAN Media Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION 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 FOUNDATION 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 defined(linux)
+/*
+ * IOCTLs that we use for linux. The structures passed in really should
+ * go into an OS inspecific file, since BSD will use these as well.
+ *
+ * Under linux, the 16 reserved-for-device IOCTLs are numbered 0x89f0
+ * through 0x89ff.
+ */
+#define LMCIOCGINFO 0x89f0 /* get current state */
+#define LMCIOCSINFO 0x89f1 /* set state to user provided values */
+
+#else
+/*
+ * IOCTLs for the sane world.
+ */
+#define LMCIOCGINFO _IOW('i', 240, struct ifreq)
+#define LMCIOCSINFO _IOWR('i', 241, struct ifreq)
+
+#endif
+
+typedef struct {
+ u_int32_t n;
+ u_int32_t m;
+ u_int32_t v;
+ u_int32_t x;
+ u_int32_t r;
+ u_int32_t f;
+ u_int32_t exact;
+} lmc_av9110_t;
+
+/*
+ * Common structure passed to the ioctl code.
+ */
+struct lmc___ctl {
+ u_int32_t cardtype;
+ u_int32_t clock_source; /* HSSI, T1 */
+ u_int32_t clock_rate; /* T1 */
+ u_int32_t crc_length;
+ u_int32_t cable_length; /* DS3 */
+ u_int32_t scrambler_onoff; /* DS3 */
+ u_int32_t cable_type; /* T1 */
+ u_int32_t keepalive_onoff; /* protocol */
+ u_int32_t ticks; /* ticks/sec */
+ union {
+ lmc_av9110_t t1;
+ } cardspec;
+};
+
+#define LMC_CTL_CARDTYPE_LMC5200 0 /* HSSI */
+#define LMC_CTL_CARDTYPE_LMC5245 1 /* DS3 */
+#define LMC_CTL_CARDTYPE_LMC1000 2 /* T1, E1, etc */
+
+#define LMC_CTL_OFF 0 /* generic OFF value */
+#define LMC_CTL_ON 1 /* generic ON value */
+
+#define LMC_CTL_CLOCK_SOURCE_EXT 0 /* clock off line */
+#define LMC_CTL_CLOCK_SOURCE_INT 1 /* internal clock */
+
+#define LMC_CTL_CRC_LENGTH_16 16
+#define LMC_CTL_CRC_LENGTH_32 32
+
+#define LMC_CTL_CABLE_LENGTH_LT_100FT 0 /* DS3 cable < 100 feet */
+#define LMC_CTL_CABLE_LENGTH_GT_100FT 1 /* DS3 cable >= 100 feet */
+
+/*
+ * These are not in the least IOCTL related, but I want them common.
+ */
+/*
+ * assignments for the GPIO register on the DEC chip (common)
+ */
+#define LMC_GEP_INIT 0x01 /* 0: */
+#define LMC_GEP_RESET 0x02 /* 1: */
+#define LMC_GEP_LOAD 0x10 /* 4: */
+#define LMC_GEP_DP 0x20 /* 5: */
+#define LMC_GEP_SERIAL 0x40 /* 6: serial out */
+#define LMC_GEP_SERIALCLK 0x80 /* 7: serial clock */
+
+/*
+ * HSSI GPIO assignments
+ */
+#define LMC_GEP_HSSI_ST 0x04 /* 2: receive timing sense (deprecated) */
+#define LMC_GEP_HSSI_CLOCK 0x08 /* 3: clock source */
+
+/*
+ * T1 GPIO assignments
+ */
+#define LMC_GEP_T1_GENERATOR 0x04 /* 2: enable prog freq gen serial i/f */
+#define LMC_GEP_T1_TXCLOCK 0x08 /* 3: provide clock on TXCLOCK output */
+
+/*
+ * Common MII16 bits
+ */
+#define LMC_MII16_LED0 0x0080
+#define LMC_MII16_LED1 0x0100
+#define LMC_MII16_LED2 0x0200
+#define LMC_MII16_LED3 0x0400 /* Error, and the red one */
+#define LMC_MII16_LED_ALL 0x0780 /* LED bit mask */
+#define LMC_MII16_FIFO_RESET 0x0800
+
+/*
+ * definitions for HSSI
+ */
+#define LMC_MII16_HSSI_TA 0x0001
+#define LMC_MII16_HSSI_CA 0x0002
+#define LMC_MII16_HSSI_LA 0x0004
+#define LMC_MII16_HSSI_LB 0x0008
+#define LMC_MII16_HSSI_LC 0x0010
+#define LMC_MII16_HSSI_TM 0x0020
+#define LMC_MII16_HSSI_CRC 0x0040
+
+/*
+ * assignments for the MII register 16 (DS3)
+ */
+#define LMC_MII16_DS3_ZERO 0x0001
+#define LMC_MII16_DS3_TRLBK 0x0002
+#define LMC_MII16_DS3_LNLBK 0x0004
+#define LMC_MII16_DS3_RAIS 0x0008
+#define LMC_MII16_DS3_TAIS 0x0010
+#define LMC_MII16_DS3_BIST 0x0020
+#define LMC_MII16_DS3_DLOS 0x0040
+#define LMC_MII16_DS3_CRC 0x1000
+#define LMC_MII16_DS3_SCRAM 0x2000
+
+/*
+ * And T1
+ */
+#define LMC_MII16_T1_DTR 0x0001 /* DTR output RW */
+#define LMC_MII16_T1_DSR 0x0002 /* DSR input RO */
+#define LMC_MII16_T1_RTS 0x0004 /* RTS output RW */
+#define LMC_MII16_T1_CTS 0x0008 /* CTS input RO */
+#define LMC_MII16_T1_DCD 0x0010 /* DCD input RO */
+#define LMC_MII16_T1_RI 0x0020 /* RI input RO */
+#define LMC_MII16_T1_CRC 0x0040 /* CRC select */
+
+/*
+ * bits 0x0080 through 0x0800 are generic, and described
+ * above with LMC_MII16_LED[0123] _LED_ALL, and _FIFO_RESET
+ */
+#define LMC_MII16_T1_LL 0x1000 /* LL output RW */
+#define LMC_MII16_T1_RL 0x2000 /* RL output RW */
+#define LMC_MII16_T1_TM 0x4000 /* TM input RO */
+#define LMC_MII16_T1_LOOP 0x8000 /* loopback enable RW */
+
+/*
+ * Some of the MII16 bits are mirrored in the MII17 register as well,
+ * but let's keep thing seperate for now, and get only the cable from
+ * the MII17.
+ */
+#define LMC_MII17_T1_CABLE_MASK 0x0038 /* mask to extract the cable type */
+#define LMC_MII17_T1_CABLE_SHIFT 3 /* shift to extract the cable type */
+
+/*
+ * framer register 0 and 7 (7 is latched and reset on read)
+ */
+#define LMC_FRAMER_REG0_DLOS 0x80 /* digital loss of service */
+#define LMC_FRAMER_REG0_OOFS 0x40 /* out of frame sync */
+#define LMC_FRAMER_REG0_AIS 0x20 /* alarm indication signal */
+#define LMC_FRAMER_REG0_CIS 0x10 /* channel idle */
+#define LMC_FRAMER_REG0_LOC 0x08 /* loss of clock */
+
+#define LMC_CARDTYPE_UNKNOWN -1
+#define LMC_CARDTYPE_HSSI 1 /* probed card is a HSSI card */
+#define LMC_CARDTYPE_DS3 2 /* probed card is a DS3 card */
+#define LMC_CARDTYPE_T1 3 /* probed card is a T1 card */
+
+#if defined(LMC_IS_KERNEL) /* defined in if_lmc_types.h */
+
+/*
+ * media independent methods to check on media status, link, light LEDs,
+ * etc.
+ */
+struct lmc___media {
+ void (* init)(lmc_softc_t * const);
+ void (* defaults)(lmc_softc_t * const);
+ void (* set_status)(lmc_softc_t * const, lmc_ctl_t *);
+ void (* set_clock_source)(lmc_softc_t * const, int);
+ void (* set_speed)(lmc_softc_t * const, lmc_ctl_t *);
+ void (* set_cable_length)(lmc_softc_t * const, int);
+ void (* set_scrambler)(lmc_softc_t * const, int);
+ int (* get_link_status)(lmc_softc_t * const);
+ void (* set_link_status)(lmc_softc_t * const, int);
+ void (* set_crc_length)(lmc_softc_t * const, int);
+};
+
+u_int32_t lmc_mii_readreg(lmc_softc_t * const sc, u_int32_t devaddr,
+ u_int32_t regno);
+void lmc_mii_writereg(lmc_softc_t * const sc, u_int32_t devaddr,
+ u_int32_t regno, u_int32_t data);
+void lmc_initcsrs(lmc_softc_t * const sc, lmc_csrptr_t csr_base,
+ size_t csr_size);
+void lmc_dec_reset(lmc_softc_t * const sc);
+void lmc_reset(lmc_softc_t * const sc);
+void lmc_led_on(lmc_softc_t * const sc, u_int32_t led);
+void lmc_led_off(lmc_softc_t * const sc, u_int32_t led);
+void lmc_gpio_mkinput(lmc_softc_t * const sc, u_int32_t bits);
+void lmc_gpio_mkoutput(lmc_softc_t * const sc, u_int32_t bits);
+lmc_intrfunc_t lmc_intr_normal(void *);
+int lmc_read_macaddr(lmc_softc_t * const sc);
+void lmc_attach(lmc_softc_t * const sc);
+void lmc_initring(lmc_softc_t * const sc, lmc_ringinfo_t * const ri,
+ tulip_desc_t *descs, int ndescs);
+
+#endif /* LMC_IS_KERNEL */
diff --git a/sys/dev/pci/if_lmcvar.h b/sys/dev/pci/if_lmcvar.h
new file mode 100644
index 00000000000..f1693079fcf
--- /dev/null
+++ b/sys/dev/pci/if_lmcvar.h
@@ -0,0 +1,609 @@
+/* $NetBSD: if_lmcvar.h,v 1.1 1999/03/25 03:32:43 explorer Exp $ */
+
+/*-
+ * Copyright (c) 1997-1999 LAN Media Corporation (LMC)
+ * All rights reserved. www.lanmedia.com
+ *
+ * This code is written by Michael Graff <graff@vix.com> for LMC.
+ * The code is derived from permitted modifications to software created
+ * by Matt Thomas (matt@3am-software.com).
+ *
+ * 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 marketing or advertising materials mentioning features or
+ * use of this software must display the following acknowledgement:
+ * This product includes software developed by LAN Media Corporation
+ * and its contributors.
+ * 4. Neither the name of LAN Media Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION 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 FOUNDATION 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.
+ */
+
+/*-
+ * Copyright (c) 1994-1997 Matt Thomas (matt@3am-software.com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if !defined(_DEVAR_H)
+#define _DEVAR_H
+
+#define LMC_MTU 1500
+#define PPP_HEADER_LEN 4
+#define BIG_PACKET
+
+/*
+ * Intel CPUs should use I/O mapped access. XXXMLG Is this true on NetBSD
+ * too?
+ */
+#if defined(__i386__)
+#define LMC_IOMAPPED
+#endif
+
+/*
+ * This turns on all sort of debugging stuff and make the
+ * driver much larger.
+ */
+#if 0
+#define LMC_DEBUG
+#define DP(x) printf x
+#else
+#define DP(x)
+#endif
+
+/*
+ * the dec chip has its own idea of what a receive error is, but we don't
+ * want to use it, as it will get the crc error wrong when 16-bit
+ * crcs are used. So, we only care about certain conditions.
+ */
+#ifndef TULIP_DSTS_RxMIIERR
+#define TULIP_DSTS_RxMIIERR 0x00000008
+#endif
+#define LMC_DSTS_ERRSUM (TULIP_DSTS_RxMIIERR)
+
+/*
+ * This is the PCI configuration support.
+ */
+#define PCI_CFID 0x00 /* Configuration ID */
+#define PCI_CFCS 0x04 /* Configurtion Command/Status */
+#define PCI_CFRV 0x08 /* Configuration Revision */
+#define PCI_CFLT 0x0c /* Configuration Latency Timer */
+#define PCI_CBIO 0x10 /* Configuration Base IO Address */
+#define PCI_CBMA 0x14 /* Configuration Base Memory Address */
+#define PCI_SSID 0x2c /* subsystem config register */
+#define PCI_CFIT 0x3c /* Configuration Interrupt */
+#define PCI_CFDA 0x40 /* Configuration Driver Area */
+
+#define LMC_HZ 10
+
+#ifndef TULIP_GP_PINSET
+#define TULIP_GP_PINSET 0x00000100L
+#endif
+#ifndef TULIP_BUSMODE_READMULTIPLE
+#define TULIP_BUSMODE_READMULTIPLE 0x00200000L
+#endif
+
+#if defined(__NetBSD__)
+
+#include "rnd.h"
+#if NRND > 0
+#include <sys/rnd.h>
+#endif
+
+#endif /* NetBSD */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#define LMC_CSR_READ(sc, csr) \
+ bus_space_read_4((sc)->lmc_bustag, (sc)->lmc_bushandle, (sc)->lmc_csrs.csr)
+#define LMC_CSR_WRITE(sc, csr, val) \
+ bus_space_write_4((sc)->lmc_bustag, (sc)->lmc_bushandle, (sc)->lmc_csrs.csr, (val))
+
+#define LMC_CSR_READBYTE(sc, csr) \
+ bus_space_read_1((sc)->lmc_bustag, (sc)->lmc_bushandle, (sc)->lmc_csrs.csr)
+#define LMC_CSR_WRITEBYTE(sc, csr, val) \
+ bus_space_write_1((sc)->lmc_bustag, (sc)->lmc_bushandle, (sc)->lmc_csrs.csr, (val))
+#endif /* __NetBSD__ */
+
+#ifdef LMC_IOMAPPED
+#define LMC_EISA_CSRSIZE 16
+#define LMC_EISA_CSROFFSET 0
+#define LMC_PCI_CSRSIZE 8
+#define LMC_PCI_CSROFFSET 0
+
+#if !defined(__NetBSD__) && !defined(__OpenBSD__)
+#define LMC_CSR_READ(sc, csr) (inl((sc)->lmc_csrs.csr))
+#define LMC_CSR_WRITE(sc, csr, val) outl((sc)->lmc_csrs.csr, val)
+
+#define LMC_CSR_READBYTE(sc, csr) (inb((sc)->lmc_csrs.csr))
+#define LMC_CSR_WRITEBYTE(sc, csr, val) outb((sc)->lmc_csrs.csr, val)
+#endif /* __NetBSD__ */
+
+#else /* LMC_IOMAPPED */
+
+#define LMC_PCI_CSRSIZE 8
+#define LMC_PCI_CSROFFSET 0
+
+#if !defined(__NetBSD__) && !defined(__OpenBSD__)
+/*
+ * macros to read and write CSRs. Note that the "0 +" in
+ * READ_CSR is to prevent the macro from being an lvalue
+ * and WRITE_CSR shouldn't be assigned from.
+ */
+#define LMC_CSR_READ(sc, csr) (0 + *(sc)->lmc_csrs.csr)
+#define LMC_CSR_WRITE(sc, csr, val) ((void)(*(sc)->lmc_csrs.csr = (val)))
+#endif /* __NetBSD__ */
+
+#endif /* LMC_IOMAPPED */
+
+/*
+ * This structure contains "pointers" for the registers on
+ * the various 21x4x chips. CSR0 through CSR8 are common
+ * to all chips. After that, it gets messy...
+ */
+typedef struct {
+ lmc_csrptr_t csr_busmode; /* CSR0 */
+ lmc_csrptr_t csr_txpoll; /* CSR1 */
+ lmc_csrptr_t csr_rxpoll; /* CSR2 */
+ lmc_csrptr_t csr_rxlist; /* CSR3 */
+ lmc_csrptr_t csr_txlist; /* CSR4 */
+ lmc_csrptr_t csr_status; /* CSR5 */
+ lmc_csrptr_t csr_command; /* CSR6 */
+ lmc_csrptr_t csr_intr; /* CSR7 */
+ lmc_csrptr_t csr_missed_frames; /* CSR8 */
+ lmc_csrptr_t csr_9; /* CSR9 */
+ lmc_csrptr_t csr_10; /* CSR10 */
+ lmc_csrptr_t csr_11; /* CSR11 */
+ lmc_csrptr_t csr_12; /* CSR12 */
+ lmc_csrptr_t csr_13; /* CSR13 */
+ lmc_csrptr_t csr_14; /* CSR14 */
+ lmc_csrptr_t csr_15; /* CSR15 */
+} lmc_regfile_t;
+
+#define csr_enetrom csr_9 /* 21040 */
+#define csr_reserved csr_10 /* 21040 */
+#define csr_full_duplex csr_11 /* 21040 */
+#define csr_bootrom csr_10 /* 21041/21140A/?? */
+#define csr_gp csr_12 /* 21140* */
+#define csr_watchdog csr_15 /* 21140* */
+#define csr_gp_timer csr_11 /* 21041/21140* */
+#define csr_srom_mii csr_9 /* 21041/21140* */
+#define csr_sia_status csr_12 /* 2104x */
+#define csr_sia_connectivity csr_13 /* 2104x */
+#define csr_sia_tx_rx csr_14 /* 2104x */
+#define csr_sia_general csr_15 /* 2104x */
+
+/*
+ * While 21x4x allows chaining of its descriptors, this driver
+ * doesn't take advantage of it. We keep the descriptors in a
+ * traditional FIFO ring.
+ */
+struct lmc_ringinfo {
+ tulip_desc_t *ri_first; /* first entry in ring */
+ tulip_desc_t *ri_last; /* one after last entry */
+ tulip_desc_t *ri_nextin; /* next to processed by host */
+ tulip_desc_t *ri_nextout; /* next to processed by adapter */
+ int ri_max;
+ int ri_free;
+};
+
+/*
+ * The 21040 has a stupid restriction in that the receive
+ * buffers must be longword aligned. But since Ethernet
+ * headers are not a multiple of longwords in size this forces
+ * the data to non-longword aligned. Since IP requires the
+ * data to be longword aligned, we need to copy it after it has
+ * been DMA'ed in our memory.
+ *
+ * Since we have to copy it anyways, we might as well as allocate
+ * dedicated receive space for the input. This allows to use a
+ * small receive buffer size and more ring entries to be able to
+ * better keep with a flood of tiny Ethernet packets.
+ *
+ * The receive space MUST ALWAYS be a multiple of the page size.
+ * And the number of receive descriptors multiplied by the size
+ * of the receive buffers must equal the recevive space. This
+ * is so that we can manipulate the page tables so that even if a
+ * packet wraps around the end of the receive space, we can
+ * treat it as virtually contiguous.
+ *
+ * The above used to be true (the stupid restriction is still true)
+ * but we gone to directly DMA'ing into MBUFs (unless it's on an
+ * architecture which can't handle unaligned accesses) because with
+ * 100Mb/s cards the copying is just too much of a hit.
+ */
+
+#define LMC_RXDESCS 48
+#define LMC_TXDESCS 128
+#define LMC_RXQ_TARGET 32
+#if LMC_RXQ_TARGET >= LMC_RXDESCS
+#error LMC_RXQ_TARGET must be less than LMC_RXDESCS
+#endif
+
+#define LMC_RX_BUFLEN ((MCLBYTES < 2048 ? MCLBYTES : 2048) - 16)
+
+/*
+ * The various controllers support. Technically the DE425 is just
+ * a 21040 on EISA. But since it remarkably difference from normal
+ * 21040s, we give it its own chip id.
+ */
+
+typedef enum {
+ LMC_21140, LMC_21140A,
+ LMC_CHIPID_UNKNOWN
+} lmc_chipid_t;
+
+#define LMC_BIT(b) (1L << ((int)(b)))
+
+typedef struct {
+ /*
+ * Transmit Statistics
+ */
+ u_int32_t dot3StatsSingleCollisionFrames;
+ u_int32_t dot3StatsMultipleCollisionFrames;
+ u_int32_t dot3StatsSQETestErrors;
+ u_int32_t dot3StatsDeferredTransmissions;
+ u_int32_t dot3StatsLateCollisions;
+ u_int32_t dot3StatsExcessiveCollisions;
+ u_int32_t dot3StatsCarrierSenseErrors;
+ u_int32_t dot3StatsInternalMacTransmitErrors;
+ u_int32_t dot3StatsInternalTransmitUnderflows; /* not in rfc1650! */
+ u_int32_t dot3StatsInternalTransmitBabbles; /* not in rfc1650! */
+ /*
+ * Receive Statistics
+ */
+ u_int32_t dot3StatsMissedFrames; /* not in rfc1650! */
+ u_int32_t dot3StatsAlignmentErrors;
+ u_int32_t dot3StatsFCSErrors;
+ u_int32_t dot3StatsFrameTooLongs;
+ u_int32_t dot3StatsInternalMacReceiveErrors;
+} lmc_dot3_stats_t;
+
+/*
+ * Now to important stuff. This is softc structure (where does softc
+ * come from??? No idea) for the tulip device.
+ *
+ */
+struct lmc___softc {
+#if defined(__bsdi__)
+ struct device lmc_dev; /* base device */
+ struct isadev lmc_id; /* ISA device */
+ struct intrhand lmc_ih; /* intrrupt vectoring */
+ struct atshutdown lmc_ats; /* shutdown hook */
+ struct p2pcom lmc_p2pcom; /* point-to-point common stuff */
+
+#define lmc_if lmc_p2pcom.p2p_if /* network-visible interface */
+#endif /* __bsdi__ */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ struct device lmc_dev; /* base device */
+ void *lmc_ih; /* intrrupt vectoring */
+ void *lmc_ats; /* shutdown hook */
+ bus_space_tag_t lmc_bustag;
+ bus_space_handle_t lmc_bushandle; /* CSR region handle */
+ pci_chipset_tag_t lmc_pc;
+#endif
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ struct sppp lmc_sppp;
+#define lmc_if lmc_sppp.pp_if
+#endif
+
+ u_int8_t lmc_enaddr[6]; /* yes, a small hack... */
+ lmc_regfile_t lmc_csrs;
+ volatile u_int32_t lmc_txtick;
+ volatile u_int32_t lmc_rxtick;
+ u_int32_t lmc_flags;
+
+ u_int32_t lmc_features; /* static bits indicating features of chip */
+ u_int32_t lmc_intrmask; /* our copy of csr_intr */
+ u_int32_t lmc_cmdmode; /* our copy of csr_cmdmode */
+ u_int32_t lmc_last_system_error : 3; /* last system error (only value is LMC_SYSTEMERROR is also set) */
+ u_int32_t lmc_system_errors; /* number of system errors encountered */
+ u_int32_t lmc_statusbits; /* status bits from CSR5 that may need to be printed */
+
+ u_int8_t lmc_revinfo; /* revision of chip */
+ u_int8_t lmc_cardtype; /* LMC_CARDTYPE_HSSI or ..._DS3 */
+ u_int32_t lmc_gpio_io; /* state of in/out settings */
+ u_int32_t lmc_gpio; /* state of outputs */
+ u_int8_t lmc_gp;
+
+ lmc_chipid_t lmc_chipid; /* type of chip we are using */
+ u_int32_t lmc_miireg16;
+ struct ifqueue lmc_txq;
+ struct ifqueue lmc_rxq;
+ lmc_dot3_stats_t lmc_dot3stats;
+ lmc_ringinfo_t lmc_rxinfo;
+ lmc_ringinfo_t lmc_txinfo;
+ u_int8_t lmc_rombuf[128];
+ lmc_media_t *lmc_media;
+ lmc_ctl_t ictl;
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ struct device *lmc_pci_busno; /* needed for multiport boards */
+#else
+ u_int8_t lmc_pci_busno; /* needed for multiport boards */
+#endif
+ u_int8_t lmc_pci_devno; /* needed for multiport boards */
+#if defined(__FreeBSD__)
+ tulip_desc_t *lmc_rxdescs;
+ tulip_desc_t *lmc_txdescs;
+#else
+ tulip_desc_t lmc_rxdescs[LMC_RXDESCS];
+ tulip_desc_t lmc_txdescs[LMC_TXDESCS];
+#endif
+#if defined(__NetBSD__) && NRND > 0
+ rndsource_element_t lmc_rndsource;
+#endif
+};
+
+/*
+ * lmc_flags
+ */
+#define LMC_IFUP 0x00000001
+#define LMC_00000002 0x00000002
+#define LMC_00000004 0x00000004
+#define LMC_00000008 0x00000008
+#define LMC_00000010 0x00000010
+#define LMC_MODEMOK 0x00000020
+#define LMC_00000040 0x00000040
+#define LMC_00000080 0x00000080
+#define LMC_RXACT 0x00000100
+#define LMC_INRESET 0x00000200
+#define LMC_NEEDRESET 0x00000400
+#define LMC_00000800 0x00000800
+#define LMC_00001000 0x00001000
+#define LMC_00002000 0x00002000
+#define LMC_WANTTXSTART 0x00004000
+#define LMC_NEWTXTHRESH 0x00008000
+#define LMC_NOAUTOSENSE 0x00010000
+#define LMC_PRINTLINKUP 0x00020000
+#define LMC_LINKUP 0x00040000
+#define LMC_RXBUFSLOW 0x00080000
+#define LMC_NOMESSAGES 0x00100000
+#define LMC_SYSTEMERROR 0x00200000
+#define LMC_TIMEOUTPENDING 0x00400000
+#define LMC_00800000 0x00800000
+#define LMC_01000000 0x01000000
+#define LMC_02000000 0x02000000
+#define LMC_RXIGNORE 0x04000000
+#define LMC_08000000 0x08000000
+#define LMC_10000000 0x10000000
+#define LMC_20000000 0x20000000
+#define LMC_40000000 0x40000000
+#define LMC_80000000 0x80000000
+
+/*
+ * lmc_features
+ */
+#define LMC_HAVE_GPR 0x00000001 /* have gp register (140[A]) */
+#define LMC_HAVE_RXBADOVRFLW 0x00000002 /* RX corrupts on overflow */
+#define LMC_HAVE_POWERMGMT 0x00000004 /* Snooze/sleep modes */
+#define LMC_HAVE_MII 0x00000008 /* Some medium on MII */
+#define LMC_HAVE_SIANWAY 0x00000010 /* SIA does NWAY */
+#define LMC_HAVE_DUALSENSE 0x00000020 /* SIA senses both AUI & TP */
+#define LMC_HAVE_SIAGP 0x00000040 /* SIA has a GP port */
+#define LMC_HAVE_BROKEN_HASH 0x00000080 /* Broken Multicast Hash */
+#define LMC_HAVE_ISVSROM 0x00000100 /* uses ISV SROM Format */
+#define LMC_HAVE_BASEROM 0x00000200 /* Board ROM can be cloned */
+#define LMC_HAVE_SLAVEDROM 0x00000400 /* Board ROM cloned */
+#define LMC_HAVE_SLAVEDINTR 0x00000800 /* Board slaved interrupt */
+#define LMC_HAVE_SHAREDINTR 0x00001000 /* Board shares interrupts */
+#define LMC_HAVE_OKROM 0x00002000 /* ROM was recognized */
+#define LMC_HAVE_NOMEDIA 0x00004000 /* did not detect any media */
+#define LMC_HAVE_STOREFWD 0x00008000 /* have CMD_STOREFWD */
+#define LMC_HAVE_SIA100 0x00010000 /* has LS100 in SIA status */
+
+static const char * const lmc_system_errors[] = {
+ "parity error",
+ "master abort",
+ "target abort",
+ "reserved #3",
+ "reserved #4",
+ "reserved #5",
+ "reserved #6",
+ "reserved #7",
+};
+
+static const char * const lmc_status_bits[] = {
+ NULL,
+ "transmit process stopped",
+ NULL,
+ "transmit jabber timeout",
+
+ NULL,
+ "transmit underflow",
+ NULL,
+ "receive underflow",
+
+ "receive process stopped",
+ "receive watchdog timeout",
+ NULL,
+ NULL,
+
+ "link failure",
+ NULL,
+ NULL,
+};
+
+/*
+ * This driver supports a maximum of 32 tulip boards.
+ * This should be enough for the forseeable future.
+ */
+#define LMC_MAX_DEVICES 32
+
+#if defined(__FreeBSD__)
+typedef void ifnet_ret_t;
+typedef int ioctl_cmd_t;
+static lmc_softc_t *tulips[LMC_MAX_DEVICES];
+#if BSD >= 199506
+#define LMC_IFP_TO_SOFTC(ifp) ((lmc_softc_t *)((ifp)->if_softc))
+#if NBPFILTER > 0
+#define LMC_BPF_MTAP(sc, m) bpf_mtap(&(sc)->lmc_sppp.pp_if, m)
+#define LMC_BPF_TAP(sc, p, l) bpf_tap(&(sc)->lmc_sppp.pp_if, p, l)
+#define LMC_BPF_ATTACH(sc) bpfattach(&(sc)->lmc_sppp.pp_if, DLT_PPP, PPP_HEADER_LEN)
+#endif
+#define LMC_VOID_INTRFUNC
+#define IFF_NOTRAILERS 0
+#define CLBYTES PAGE_SIZE
+#define LMC_EADDR_FMT "%6D"
+#define LMC_EADDR_ARGS(addr) addr, ":"
+#else
+extern int bootverbose;
+#define LMC_IFP_TO_SOFTC(ifp) (LMC_UNIT_TO_SOFTC((ifp)->if_unit))
+#include <sys/devconf.h>
+#define LMC_DEVCONF
+#endif
+#define LMC_UNIT_TO_SOFTC(unit) (tulips[unit])
+#define LMC_BURSTSIZE(unit) pci_max_burst_len
+#define loudprintf if (bootverbose) printf
+#endif
+
+#if defined(__bsdi__)
+typedef int ifnet_ret_t;
+typedef u_long ioctl_cmd_t;
+extern struct cfdriver lmccd;
+#define LMC_UNIT_TO_SOFTC(unit) ((lmc_softc_t *)lmccd.cd_devs[unit])
+#define LMC_IFP_TO_SOFTC(ifp) (LMC_UNIT_TO_SOFTC((ifp)->if_unit))
+#define loudprintf aprint_verbose
+#define MCNT(x) (sizeof(x) / sizeof(struct ifmedia_entry))
+#define lmc_unit lmc_dev.dv_unit
+#define lmc_name lmc_p2pcom.p2p_if.if_name
+#define LMC_BPF_MTAP(sc, m)
+#define LMC_BPF_TAP(sc, p, l)
+#define LMC_BPF_ATTACH(sc)
+#endif /* __bsdi__ */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+typedef void ifnet_ret_t;
+typedef u_long ioctl_cmd_t;
+extern struct cfattach lmc_ca;
+extern struct cfdriver lmc_cd;
+#define LMC_UNIT_TO_SOFTC(unit) ((lmc_softc_t *) lmc_cd.cd_devs[unit])
+#define LMC_IFP_TO_SOFTC(ifp) ((lmc_softc_t *)((ifp)->if_softc))
+#define lmc_unit lmc_dev.dv_unit
+#define lmc_xname lmc_if.if_xname
+#define LMC_RAISESPL() splnet()
+#define LMC_RAISESOFTSPL() splsoftnet()
+#define LMC_RESTORESPL(s) splx(s)
+/* #define lmc_enaddr lmc_enaddr */
+#define loudprintf printf
+#define LMC_PRINTF_FMT "%s"
+#define LMC_PRINTF_ARGS sc->lmc_xname
+#if defined(__alpha__)
+/* XXX XXX NEED REAL DMA MAPPING SUPPORT XXX XXX */
+#define LMC_KVATOPHYS(sc, va) alpha_XXX_dmamap((vm_offset_t)(va))
+#endif
+#endif /* __NetBSD__ */
+
+#ifndef LMC_PRINTF_FMT
+#define LMC_PRINTF_FMT "%s%d"
+#endif
+#ifndef LMC_PRINTF_ARGS
+#define LMC_PRINTF_ARGS sc->lmc_name, sc->lmc_unit
+#endif
+
+#ifndef LMC_BURSTSIZE
+#define LMC_BURSTSIZE(unit) 3
+#endif
+
+#ifndef lmc_unit
+#define lmc_unit lmc_sppp.pp_if.if_unit
+#endif
+
+#ifndef lmc_name
+#define lmc_name lmc_sppp.pp_if.if_name
+#endif
+
+#if !defined(lmc_bpf)
+#if defined(__NetBSD__) || defined(__FreeBSD__) | defined(__OpenBSD__)
+#define lmc_bpf lmc_sppp.pp_if.if_bpf
+#endif
+#if defined(__bsdi__)
+#define lmc_bpf lmc_if.if_bpf
+#endif
+#endif
+
+#if !defined(LMC_KVATOPHYS)
+#define LMC_KVATOPHYS(sc, va) vtophys(va)
+#endif
+
+#ifndef LMC_RAISESPL
+#define LMC_RAISESPL() splimp()
+#endif
+#ifndef LMC_RAISESOFTSPL
+#define LMC_RAISESOFTSPL() splnet()
+#endif
+#ifndef TULUP_RESTORESPL
+#define LMC_RESTORESPL(s) splx(s)
+#endif
+
+/*
+ * While I think FreeBSD's 2.2 change to the bpf is a nice simplification,
+ * it does add yet more conditional code to this driver. Sigh.
+ */
+#if !defined(LMC_BPF_MTAP) && NBPFILTER > 0
+#define LMC_BPF_MTAP(sc, m) bpf_mtap((sc)->lmc_bpf, m)
+#define LMC_BPF_TAP(sc, p, l) bpf_tap((sc)->lmc_bpf, p, l)
+#define LMC_BPF_ATTACH(sc) bpfattach(&(sc)->lmc_bpf, &(sc)->lmc_sppp.pp_if, DLT_PPP, PPP_HEADER_LEN)
+#endif
+
+/*
+ * However, this change to FreeBSD I am much less enamored with.
+ */
+#if !defined(LMC_EADDR_FMT)
+#define LMC_EADDR_FMT "%s"
+#define LMC_EADDR_ARGS(addr) ether_sprintf(addr)
+#endif
+
+#define LMC_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- Little Endian */
+#define LMC_MAX_TXSEG 30
+
+#define LMC_ADDREQUAL(a1, a2) \
+ (((u_int16_t *)a1)[0] == ((u_int16_t *)a2)[0] \
+ && ((u_int16_t *)a1)[1] == ((u_int16_t *)a2)[1] \
+ && ((u_int16_t *)a1)[2] == ((u_int16_t *)a2)[2])
+#define LMC_ADDRBRDCST(a1) \
+ (((u_int16_t *)a1)[0] == 0xFFFFU \
+ && ((u_int16_t *)a1)[1] == 0xFFFFU \
+ && ((u_int16_t *)a1)[2] == 0xFFFFU)
+
+typedef int lmc_spl_t;
+
+#endif /* !defined(_DEVAR_H) */