summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorNiklas Hallqvist <niklas@cvs.openbsd.org>2001-07-13 17:26:45 +0000
committerNiklas Hallqvist <niklas@cvs.openbsd.org>2001-07-13 17:26:45 +0000
commitd003de9876c8509ab915bb56ba3788080d8bba5a (patch)
tree7015c8fb01c9f9717d0f940cd715099681352bbd /sys/dev
parent725ca777df50676e72aa1fe1a1273c55761680dc (diff)
On my 10th wedding anniversary I am celebrating by
committing this driver for technology from back when I got married :-) It is for DEC EtherWorks cards, and comes from NetBSD. I have done severe KNF, and fixed the driver to work with PIO-only cards. Enjoy!
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ic/lemac.c1111
-rw-r--r--sys/dev/ic/lemacreg.h187
-rw-r--r--sys/dev/ic/lemacvar.h160
-rw-r--r--sys/dev/isa/files.isa6
-rw-r--r--sys/dev/isa/if_lc_isa.c215
5 files changed, 1678 insertions, 1 deletions
diff --git a/sys/dev/ic/lemac.c b/sys/dev/ic/lemac.c
new file mode 100644
index 00000000000..e31b3476adb
--- /dev/null
+++ b/sys/dev/ic/lemac.c
@@ -0,0 +1,1111 @@
+/* $OpenBSD: lemac.c,v 1.1 2001/07/13 17:26:44 niklas Exp $ */
+/* $NetBSD: lemac.c,v 1.20 2001/06/13 10:46:02 wiz Exp $ */
+
+/*-
+ * Copyright (c) 1994, 1995, 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 without 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.
+ */
+
+/*
+ * DEC EtherWORKS 3 Ethernet Controllers
+ *
+ * Written by Matt Thomas
+ * BPF support code stolen directly from if_ec.c
+ *
+ * This driver supports the LEMAC DE203/204/205 cards.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <net/if_media.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#include <machine/bus.h>
+
+#include <dev/ic/lemacreg.h>
+#include <dev/ic/lemacvar.h>
+
+#if 0
+#include <uvm/uvm_extern.h>
+#endif
+
+#include "bpfilter.h"
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+int lemac_ifioctl(struct ifnet *, u_long, caddr_t);
+int lemac_ifmedia_change(struct ifnet *const);
+void lemac_ifmedia_status(struct ifnet *const, struct ifmediareq *);
+void lemac_ifstart(struct ifnet *);
+void lemac_init(struct lemac_softc *);
+void lemac_init_adapmem(struct lemac_softc *);
+void lemac_input(struct lemac_softc *, bus_addr_t, size_t);
+void lemac_multicast_filter(struct lemac_softc *);
+void lemac_multicast_op(u_int16_t *, const u_char *, int);
+int lemac_read_eeprom(struct lemac_softc *);
+int lemac_read_macaddr(unsigned char *, const bus_space_tag_t,
+ const bus_space_handle_t, const bus_addr_t, int);
+void lemac_reset(struct lemac_softc *);
+void lemac_rne_intr(struct lemac_softc *);
+void lemac_rxd_intr(struct lemac_softc *, unsigned);
+void lemac_tne_intr(struct lemac_softc *);
+void lemac_txd_intr(struct lemac_softc *, unsigned);
+
+struct cfdriver lc_cd = {
+ NULL, "lc", DV_IFNET
+};
+
+static const u_int16_t lemac_allmulti_mctbl[16] = {
+ 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU,
+ 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU,
+ 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU,
+ 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU,
+};
+
+/*
+ * Some tuning/monitoring variables.
+ */
+unsigned lemac_txmax = 16;
+
+void
+lemac_rxd_intr(struct lemac_softc *sc, unsigned cs_value)
+{
+ /*
+ * Handle CS_RXD (Receiver disabled) here.
+ *
+ * Check Free Memory Queue Count. If not equal to zero
+ * then just turn Receiver back on. If it is equal to
+ * zero then check to see if transmitter is disabled.
+ * Process transmit TXD loop once more. If all else
+ * fails then do software init (0xC0 to EEPROM Init)
+ * and rebuild Free Memory Queue.
+ */
+
+ sc->sc_cntrs.cntr_rxd_intrs++;
+
+ /*
+ * Re-enable Receiver.
+ */
+
+ cs_value &= ~LEMAC_CS_RXD;
+ LEMAC_OUTB(sc, LEMAC_REG_CS, cs_value);
+
+ if (LEMAC_INB(sc, LEMAC_REG_FMC) > 0)
+ return;
+
+ if (cs_value & LEMAC_CS_TXD)
+ lemac_txd_intr(sc, cs_value);
+
+ if ((LEMAC_INB(sc, LEMAC_REG_CS) & LEMAC_CS_RXD) == 0)
+ return;
+
+ printf("%s: fatal RXD error, attempting recovery\n",
+ sc->sc_if.if_xname);
+
+ lemac_reset(sc);
+ if (sc->sc_if.if_flags & IFF_UP) {
+ lemac_init(sc);
+ return;
+ }
+
+ /*
+ * Error during initializion. Mark card as disabled.
+ */
+ printf("%s: recovery failed -- board disabled\n", sc->sc_if.if_xname);
+}
+
+void
+lemac_tne_intr(struct lemac_softc *sc)
+{
+ unsigned txcount = LEMAC_INB(sc, LEMAC_REG_TDC);
+
+ sc->sc_cntrs.cntr_tne_intrs++;
+ while (txcount-- > 0) {
+ unsigned txsts = LEMAC_INB(sc, LEMAC_REG_TDQ);
+ sc->sc_if.if_opackets++; /* another one done */
+ if ((txsts & (LEMAC_TDQ_LCL|LEMAC_TDQ_NCL))
+ || (txsts & LEMAC_TDQ_COL) == LEMAC_TDQ_EXCCOL) {
+ if (txsts & LEMAC_TDQ_NCL)
+ sc->sc_flags &= ~LEMAC_LINKUP;
+ sc->sc_if.if_oerrors++;
+ } else {
+ sc->sc_flags |= LEMAC_LINKUP;
+ if ((txsts & LEMAC_TDQ_COL) != LEMAC_TDQ_NOCOL)
+ sc->sc_if.if_collisions++;
+ }
+ }
+ sc->sc_if.if_flags &= ~IFF_OACTIVE;
+ lemac_ifstart(&sc->sc_if);
+}
+
+void
+lemac_txd_intr(struct lemac_softc *sc, unsigned cs_value)
+{
+ /*
+ * Read transmit status, remove transmit buffer from
+ * transmit queue and place on free memory queue,
+ * then reset transmitter.
+ * Increment appropriate counters.
+ */
+
+ sc->sc_cntrs.cntr_txd_intrs++;
+ if (sc->sc_txctl & LEMAC_TX_STP) {
+ sc->sc_if.if_oerrors++;
+ /* return page to free queue */
+ LEMAC_OUTB(sc, LEMAC_REG_FMQ, LEMAC_INB(sc, LEMAC_REG_TDQ));
+ }
+
+ /* Turn back on transmitter if disabled */
+ LEMAC_OUTB(sc, LEMAC_REG_CS, cs_value & ~LEMAC_CS_TXD);
+ sc->sc_if.if_flags &= ~IFF_OACTIVE;
+}
+
+int
+lemac_read_eeprom(struct lemac_softc *sc)
+{
+ int word_off, cksum;
+
+ u_char *ep;
+
+ cksum = 0;
+ ep = sc->sc_eeprom;
+ for (word_off = 0; word_off < LEMAC_EEP_SIZE / 2; word_off++) {
+ LEMAC_OUTB(sc, LEMAC_REG_PI1, word_off);
+ LEMAC_OUTB(sc, LEMAC_REG_IOP, LEMAC_IOP_EEREAD);
+
+ DELAY(LEMAC_EEP_DELAY);
+
+ *ep = LEMAC_INB(sc, LEMAC_REG_EE1);
+ cksum += *ep++;
+ *ep = LEMAC_INB(sc, LEMAC_REG_EE2);
+ cksum += *ep++;
+ }
+
+ /*
+ * Set up Transmit Control Byte for use later during transmit.
+ */
+
+ sc->sc_txctl |= LEMAC_TX_FLAGS;
+
+ if ((sc->sc_eeprom[LEMAC_EEP_SWFLAGS] & LEMAC_EEP_SW_SQE) == 0)
+ sc->sc_txctl &= ~LEMAC_TX_SQE;
+
+ if (sc->sc_eeprom[LEMAC_EEP_SWFLAGS] & LEMAC_EEP_SW_LAB)
+ sc->sc_txctl |= LEMAC_TX_LAB;
+
+ bcopy(&sc->sc_eeprom[LEMAC_EEP_PRDNM], sc->sc_prodname,
+ LEMAC_EEP_PRDNMSZ);
+ sc->sc_prodname[LEMAC_EEP_PRDNMSZ] = '\0';
+
+ return (cksum % 256);
+}
+
+void
+lemac_init_adapmem(struct lemac_softc *sc)
+{
+ int pg, conf;
+
+ conf = LEMAC_INB(sc, LEMAC_REG_CNF);
+
+ if ((sc->sc_eeprom[LEMAC_EEP_SETUP] & LEMAC_EEP_ST_DRAM) == 0) {
+ sc->sc_lastpage = 63;
+ conf &= ~LEMAC_CNF_DRAM;
+ } else {
+ sc->sc_lastpage = 127;
+ conf |= LEMAC_CNF_DRAM;
+ }
+
+ LEMAC_OUTB(sc, LEMAC_REG_CNF, conf);
+
+ for (pg = 1; pg <= sc->sc_lastpage; pg++)
+ LEMAC_OUTB(sc, LEMAC_REG_FMQ, pg);
+}
+
+void
+lemac_input(struct lemac_softc *sc, bus_addr_t offset, size_t length)
+{
+ struct ether_header eh;
+ struct mbuf *m;
+
+ if (length - sizeof(eh) > ETHERMTU ||
+ length - sizeof(eh) < ETHERMIN) {
+ sc->sc_if.if_ierrors++;
+ return;
+ }
+ if (LEMAC_USE_PIO_MODE(sc)) {
+ LEMAC_INSB(sc, LEMAC_REG_DAT, sizeof(eh), (void *)&eh);
+ } else {
+ LEMAC_GETBUF16(sc, offset, sizeof(eh) / 2, (void *)&eh);
+ }
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ sc->sc_if.if_ierrors++;
+ return;
+ }
+ if (length + 2 > MHLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ sc->sc_if.if_ierrors++;
+ return;
+ }
+ }
+ m->m_data += 2;
+ bcopy((caddr_t)&eh, m->m_data, sizeof(eh));
+ if (LEMAC_USE_PIO_MODE(sc)) {
+ LEMAC_INSB(sc, LEMAC_REG_DAT, length - sizeof(eh),
+ mtod(m, caddr_t) + sizeof(eh));
+ } else {
+ LEMAC_GETBUF16(sc, offset + sizeof(eh),
+ (length - sizeof(eh)) / 2,
+ (void *)(mtod(m, caddr_t) + sizeof(eh)));
+ if (length & 1)
+ m->m_data[length - 1] = LEMAC_GET8(sc,
+ offset + length - 1);
+ }
+#if NBPFILTER > 0
+ if (sc->sc_if.if_bpf != NULL) {
+ m->m_pkthdr.len = m->m_len = length;
+ bpf_mtap(sc->sc_if.if_bpf, m);
+ }
+
+ /*
+ * If this is single cast but not to us
+ * drop it!
+ */
+ if ((eh.ether_dhost[0] & 1) == 0 &&
+ !LEMAC_ADDREQUAL(eh.ether_dhost, sc->sc_arpcom.ac_enaddr)) {
+ m_freem(m);
+ return;
+ }
+#endif
+ m->m_pkthdr.len = m->m_len = length;
+ m->m_pkthdr.rcvif = &sc->sc_if;
+ ether_input_mbuf(&sc->sc_if, m);
+}
+
+void
+lemac_rne_intr(struct lemac_softc *sc)
+{
+ int rxcount;
+
+ sc->sc_cntrs.cntr_rne_intrs++;
+ rxcount = LEMAC_INB(sc, LEMAC_REG_RQC);
+ while (rxcount--) {
+ unsigned rxpg = LEMAC_INB(sc, LEMAC_REG_RQ);
+ u_int32_t rxlen;
+
+ sc->sc_if.if_ipackets++;
+ if (LEMAC_USE_PIO_MODE(sc)) {
+ LEMAC_OUTB(sc, LEMAC_REG_IOP, rxpg);
+ LEMAC_OUTB(sc, LEMAC_REG_PI1, 0);
+ LEMAC_OUTB(sc, LEMAC_REG_PI2, 0);
+ LEMAC_INSB(sc, LEMAC_REG_DAT, sizeof(rxlen),
+ (void *)&rxlen);
+ } else {
+ LEMAC_OUTB(sc, LEMAC_REG_MPN, rxpg);
+ rxlen = LEMAC_GET32(sc, 0);
+ }
+ if (rxlen & LEMAC_RX_OK) {
+ sc->sc_flags |= LEMAC_LINKUP;
+ /*
+ * Get receive length - subtract out checksum.
+ */
+ rxlen = ((rxlen >> 8) & 0x7FF) - 4;
+ lemac_input(sc, sizeof(rxlen), rxlen);
+ } else {
+ sc->sc_if.if_ierrors++;
+ }
+ /* Return this page to Free Memory Queue */
+ LEMAC_OUTB(sc, LEMAC_REG_FMQ, rxpg);
+ } /* end while (recv_count--) */
+
+ return;
+}
+
+/*
+ * This is the standard method of reading the DEC Address ROMS.
+ * I don't understand it but it does work.
+ */
+int
+lemac_read_macaddr(unsigned char *hwaddr, const bus_space_tag_t iot,
+ const bus_space_handle_t ioh, const bus_addr_t ioreg, int skippat)
+{
+ int cksum, rom_cksum;
+ unsigned char addrbuf[6];
+
+ if (!skippat) {
+ int idx, idx2, found, octet;
+ static u_char testpat[] = {
+ 0xFF, 0, 0x55, 0xAA, 0xFF, 0, 0x55, 0xAA
+ };
+ idx2 = found = 0;
+
+ for (idx = 0; idx < 32; idx++) {
+ octet = bus_space_read_1(iot, ioh, ioreg);
+
+ if (octet == testpat[idx2]) {
+ if (++idx2 == sizeof(testpat)) {
+ ++found;
+ break;
+ }
+ } else {
+ idx2 = 0;
+ }
+ }
+
+ if (!found)
+ return (-1);
+ }
+
+ if (hwaddr == NULL)
+ hwaddr = addrbuf;
+
+ cksum = 0;
+ hwaddr[0] = bus_space_read_1(iot, ioh, ioreg);
+ hwaddr[1] = bus_space_read_1(iot, ioh, ioreg);
+
+ /* hardware adddress can't be multicast */
+ if (hwaddr[0] & 1)
+ return (-1);
+
+ cksum = *(u_short *)&hwaddr[0];
+
+ hwaddr[2] = bus_space_read_1(iot, ioh, ioreg);
+ hwaddr[3] = bus_space_read_1(iot, ioh, ioreg);
+ cksum *= 2;
+ if (cksum > 65535)
+ cksum -= 65535;
+ cksum += *(u_short *)&hwaddr[2];
+ if (cksum > 65535)
+ cksum -= 65535;
+
+ hwaddr[4] = bus_space_read_1(iot, ioh, ioreg);
+ hwaddr[5] = bus_space_read_1(iot, ioh, ioreg);
+ cksum *= 2;
+ if (cksum > 65535)
+ cksum -= 65535;
+ cksum += *(u_short *)&hwaddr[4];
+ if (cksum >= 65535)
+ cksum -= 65535;
+
+ /* 00-00-00 is an illegal OUI */
+ if (hwaddr[0] == 0 && hwaddr[1] == 0 && hwaddr[2] == 0)
+ return (-1);
+
+ rom_cksum = bus_space_read_1(iot, ioh, ioreg);
+ rom_cksum |= bus_space_read_1(iot, ioh, ioreg) << 8;
+
+ if (cksum != rom_cksum)
+ return (-1);
+ return (0);
+}
+
+#if 0
+void
+lemac_multicast_op(u_int16_t *mctbl, const u_char *mca, int enable)
+{
+ u_int idx, bit, crc;
+
+ crc = ether_crc32_le(mca, ETHER_ADDR_LEN);
+
+ /*
+ * The following two lines convert the N bit index into a
+ * longword index and a longword mask.
+ */
+#if LEMAC_MCTBL_BITS < 0
+ crc >>= (32 + LEMAC_MCTBL_BITS);
+ crc &= (1 << -LEMAC_MCTBL_BITS) - 1;
+#else
+ crc &= (1 << LEMAC_MCTBL_BITS) - 1;
+#endif
+ bit = 1 << (crc & 0x0F);
+ idx = crc >> 4;
+
+ /*
+ * Set or clear hash filter bit in our table.
+ */
+ if (enable) {
+ mctbl[idx] |= bit; /* Set Bit */
+ } else {
+ mctbl[idx] &= ~bit; /* Clear Bit */
+ }
+}
+#endif
+
+void
+lemac_multicast_filter(struct lemac_softc *sc)
+{
+#if 0
+ struct ether_multistep step;
+ struct ether_multi *enm;
+#endif
+
+ bzero(sc->sc_mctbl, LEMAC_MCTBL_BITS / 8);
+
+#if 0
+ lemac_multicast_op(sc->sc_mctbl, etherbroadcastaddr, 1);
+
+ ETHER_FIRST_MULTI(step, &sc->sc_ec, enm);
+ while (enm != NULL) {
+ if (!LEMAC_ADDREQUAL(enm->enm_addrlo, enm->enm_addrhi)) {
+ sc->sc_flags |= LEMAC_ALLMULTI;
+ sc->sc_if.if_flags |= IFF_ALLMULTI;
+ return;
+ }
+ lemac_multicast_op(sc->sc_mctbl, enm->enm_addrlo, TRUE);
+ ETHER_NEXT_MULTI(step, enm);
+ }
+#endif
+ sc->sc_flags &= ~LEMAC_ALLMULTI;
+ sc->sc_if.if_flags &= ~IFF_ALLMULTI;
+}
+
+/*
+ * Do a hard reset of the board;
+ */
+void
+lemac_reset(struct lemac_softc *const sc)
+{
+ unsigned data;
+
+ /*
+ * Initialize board..
+ */
+ sc->sc_flags &= ~LEMAC_LINKUP;
+ sc->sc_if.if_flags &= ~IFF_OACTIVE;
+ LEMAC_INTR_DISABLE(sc);
+
+ LEMAC_OUTB(sc, LEMAC_REG_IOP, LEMAC_IOP_EEINIT);
+ DELAY(LEMAC_EEP_DELAY);
+
+ /*
+ * Read EEPROM information. NOTE - the placement of this function
+ * is important because functions hereafter may rely on information
+ * read from the EEPROM.
+ */
+ if ((data = lemac_read_eeprom(sc)) != LEMAC_EEP_CKSUM) {
+ printf("%s: reset: EEPROM checksum failed (0x%x)\n",
+ sc->sc_if.if_xname, data);
+ return;
+ }
+
+ /*
+ * Update the control register to reflect the media choice
+ */
+ data = LEMAC_INB(sc, LEMAC_REG_CTL);
+ if ((data & (LEMAC_CTL_APD|LEMAC_CTL_PSL)) != sc->sc_ctlmode) {
+ data &= ~(LEMAC_CTL_APD|LEMAC_CTL_PSL);
+ data |= sc->sc_ctlmode;
+ LEMAC_OUTB(sc, LEMAC_REG_CTL, data);
+ }
+
+ /*
+ * Force to 2K mode if not already configured.
+ */
+
+ data = LEMAC_INB(sc, LEMAC_REG_MBR);
+ if (LEMAC_IS_2K_MODE(data)) {
+ sc->sc_flags |= LEMAC_2K_MODE;
+ } else if (LEMAC_IS_64K_MODE(data)) {
+ data = (((data * 2) & 0xF) << 4);
+ sc->sc_flags |= LEMAC_WAS_64K_MODE;
+ LEMAC_OUTB(sc, LEMAC_REG_MBR, data);
+ } else if (LEMAC_IS_32K_MODE(data)) {
+ data = ((data & 0xF) << 4);
+ sc->sc_flags |= LEMAC_WAS_32K_MODE;
+ LEMAC_OUTB(sc, LEMAC_REG_MBR, data);
+ } else {
+ sc->sc_flags |= LEMAC_PIO_MODE;
+ /* PIO mode */
+ }
+
+ /*
+ * Initialize Free Memory Queue, Init mcast table with broadcast.
+ */
+
+ lemac_init_adapmem(sc);
+ sc->sc_flags |= LEMAC_ALIVE;
+}
+
+void
+lemac_init(struct lemac_softc *const sc)
+{
+ if ((sc->sc_flags & LEMAC_ALIVE) == 0)
+ return;
+
+ /*
+ * If the interface has the up flag
+ */
+ if (sc->sc_if.if_flags & IFF_UP) {
+ int saved_cs = LEMAC_INB(sc, LEMAC_REG_CS);
+ LEMAC_OUTB(sc, LEMAC_REG_CS,
+ saved_cs | (LEMAC_CS_TXD | LEMAC_CS_RXD));
+ LEMAC_OUTB(sc, LEMAC_REG_PA0, sc->sc_arpcom.ac_enaddr[0]);
+ LEMAC_OUTB(sc, LEMAC_REG_PA1, sc->sc_arpcom.ac_enaddr[1]);
+ LEMAC_OUTB(sc, LEMAC_REG_PA2, sc->sc_arpcom.ac_enaddr[2]);
+ LEMAC_OUTB(sc, LEMAC_REG_PA3, sc->sc_arpcom.ac_enaddr[3]);
+ LEMAC_OUTB(sc, LEMAC_REG_PA4, sc->sc_arpcom.ac_enaddr[4]);
+ LEMAC_OUTB(sc, LEMAC_REG_PA5, sc->sc_arpcom.ac_enaddr[5]);
+
+ LEMAC_OUTB(sc, LEMAC_REG_IC,
+ LEMAC_INB(sc, LEMAC_REG_IC) | LEMAC_IC_IE);
+
+ if (sc->sc_if.if_flags & IFF_PROMISC) {
+ LEMAC_OUTB(sc, LEMAC_REG_CS,
+ LEMAC_CS_MCE | LEMAC_CS_PME);
+ } else {
+ LEMAC_INTR_DISABLE(sc);
+ lemac_multicast_filter(sc);
+ if (sc->sc_flags & LEMAC_ALLMULTI)
+ bcopy(lemac_allmulti_mctbl, sc->sc_mctbl,
+ sizeof(sc->sc_mctbl));
+ if (LEMAC_USE_PIO_MODE(sc)) {
+ LEMAC_OUTB(sc, LEMAC_REG_IOP, 0);
+ LEMAC_OUTB(sc, LEMAC_REG_PI1,
+ LEMAC_MCTBL_OFF & 0xFF);
+ LEMAC_OUTB(sc, LEMAC_REG_PI2,
+ LEMAC_MCTBL_OFF >> 8);
+ LEMAC_OUTSB(sc, LEMAC_REG_DAT,
+ sizeof(sc->sc_mctbl),
+ (void *)sc->sc_mctbl);
+ } else {
+ LEMAC_OUTB(sc, LEMAC_REG_MPN, 0);
+ LEMAC_PUTBUF8(sc, LEMAC_MCTBL_OFF,
+ sizeof(sc->sc_mctbl),
+ (void *)sc->sc_mctbl);
+ }
+
+ LEMAC_OUTB(sc, LEMAC_REG_CS, LEMAC_CS_MCE);
+ }
+
+ LEMAC_OUTB(sc, LEMAC_REG_CTL,
+ LEMAC_INB(sc, LEMAC_REG_CTL) ^ LEMAC_CTL_LED);
+
+ LEMAC_INTR_ENABLE(sc);
+ sc->sc_if.if_flags |= IFF_RUNNING;
+ lemac_ifstart(&sc->sc_if);
+ } else {
+ LEMAC_OUTB(sc, LEMAC_REG_CS, LEMAC_CS_RXD|LEMAC_CS_TXD);
+
+ LEMAC_INTR_DISABLE(sc);
+ sc->sc_if.if_flags &= ~IFF_RUNNING;
+ }
+}
+
+void
+lemac_ifstart(struct ifnet *ifp)
+{
+ struct lemac_softc *const sc = LEMAC_IFP_TO_SOFTC(ifp);
+
+ if ((ifp->if_flags & IFF_RUNNING) == 0)
+ return;
+
+ LEMAC_INTR_DISABLE(sc);
+
+ for (;;) {
+ struct mbuf *m;
+ struct mbuf *m0;
+ int tx_pg;
+
+ IFQ_POLL(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+
+ if ((sc->sc_csr.csr_tqc = LEMAC_INB(sc, LEMAC_REG_TQC)) >=
+ lemac_txmax) {
+ sc->sc_cntrs.cntr_txfull++;
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+
+ /*
+ * get free memory page
+ */
+ tx_pg = sc->sc_csr.csr_fmq = LEMAC_INB(sc, LEMAC_REG_FMQ);
+
+ /*
+ * Check for good transmit page.
+ */
+ if (tx_pg == 0 || tx_pg > sc->sc_lastpage) {
+ sc->sc_cntrs.cntr_txnospc++;
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+
+ /*
+ * The first four bytes of each transmit buffer are for
+ * control information. The first byte is the control
+ * byte, then the length (why not word aligned?), then
+ * the offset to the buffer.
+ */
+
+ if (LEMAC_USE_PIO_MODE(sc)) {
+ /* Shift 2K window. */
+ LEMAC_OUTB(sc, LEMAC_REG_IOP, tx_pg);
+ LEMAC_OUTB(sc, LEMAC_REG_PI1, 0);
+ LEMAC_OUTB(sc, LEMAC_REG_PI2, 0);
+ LEMAC_OUTB(sc, LEMAC_REG_DAT, sc->sc_txctl);
+ LEMAC_OUTB(sc, LEMAC_REG_DAT,
+ (m->m_pkthdr.len >> 0) & 0xFF);
+ LEMAC_OUTB(sc, LEMAC_REG_DAT,
+ (m->m_pkthdr.len >> 8) & 0xFF);
+ LEMAC_OUTB(sc, LEMAC_REG_DAT, LEMAC_TX_HDRSZ);
+ for (m0 = m; m0 != NULL; m0 = m0->m_next)
+ LEMAC_OUTSB(sc, LEMAC_REG_DAT,
+ m0->m_len, m0->m_data);
+ } else {
+ bus_size_t txoff = /* (mtod(m, u_int32_t) &
+ (sizeof(u_int32_t) - 1)) + */ LEMAC_TX_HDRSZ;
+ /* Shift 2K window. */
+ LEMAC_OUTB(sc, LEMAC_REG_MPN, tx_pg);
+ LEMAC_PUT8(sc, 0, sc->sc_txctl);
+ LEMAC_PUT8(sc, 1, (m->m_pkthdr.len >> 0) & 0xFF);
+ LEMAC_PUT8(sc, 2, (m->m_pkthdr.len >> 8) & 0xFF);
+ LEMAC_PUT8(sc, 3, txoff);
+
+ /*
+ * Copy the packet to the board
+ */
+ for (m0 = m; m0 != NULL; m0 = m0->m_next) {
+#if 0
+ LEMAC_PUTBUF8(sc, txoff, m0->m_len,
+ m0->m_data);
+ txoff += m0->m_len;
+#else
+ const u_int8_t *cp = m0->m_data;
+ int len = m0->m_len;
+#if 0
+ if ((txoff & 3) == (((long)cp) & 3) &&
+ len >= 4) {
+ if (txoff & 3) {
+ int alen = (~txoff & 3);
+ LEMAC_PUTBUF8(sc, txoff, alen,
+ cp);
+ cp += alen;
+ txoff += alen;
+ len -= alen;
+ }
+ if (len >= 4) {
+ LEMAC_PUTBUF32(sc, txoff,
+ len / 4, cp);
+ cp += len & ~3;
+ txoff += len & ~3;
+ len &= 3;
+ }
+ }
+#endif
+ if ((txoff & 1) == (((long)cp) & 1) &&
+ len >= 2) {
+ if (txoff & 1) {
+ int alen = (~txoff & 1);
+ LEMAC_PUTBUF8(sc, txoff, alen,
+ cp);
+ cp += alen;
+ txoff += alen;
+ len -= alen;
+ }
+ if (len >= 2) {
+ LEMAC_PUTBUF16(sc, txoff,
+ len / 2, (void *)cp);
+ cp += len & ~1;
+ txoff += len & ~1;
+ len &= 1;
+ }
+ }
+ if (len > 0) {
+ LEMAC_PUTBUF8(sc, txoff, len, cp);
+ txoff += len;
+ }
+#endif
+ }
+ }
+
+ /* tell chip to transmit this packet */
+ LEMAC_OUTB(sc, LEMAC_REG_TQ, tx_pg);
+#if NBPFILTER > 0
+ if (sc->sc_if.if_bpf != NULL)
+ bpf_mtap(sc->sc_if.if_bpf, m);
+#endif
+ m_freem(m); /* free the mbuf */
+ }
+ LEMAC_INTR_ENABLE(sc);
+}
+
+int
+lemac_ifioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct lemac_softc *const sc = LEMAC_IFP_TO_SOFTC(ifp);
+ int s;
+ int error = 0;
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ifreq *ifr = (struct ifreq *)data;
+
+ s = splnet();
+
+ if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) {
+ splx(s);
+ return (error);
+ }
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ lemac_init(sc);
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ arp_ifinit(&sc->sc_arpcom, ifa);
+ break;
+#endif /* INET */
+
+#ifdef NS
+ /* This magic copied from if_is.c; I don't use XNS,
+ * so I have no way of telling if this actually
+ * works or not.
+ */
+ case AF_NS: {
+ struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+ if (ns_nullhost(*ina)) {
+ ina->x_host =
+ *(union ns_host *)sc->sc_arpcom.ac_enaddr;
+ } else {
+ bcopy((caddr_t)ina->x_host.c_host,
+ sc->sc_arpcom.ac_enaddr, ifp->if_addrlen);
+ }
+ break;
+ }
+#endif /* NS */
+
+ default:
+ break;
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ lemac_init(sc);
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ /*
+ * Update multicast listeners
+ */
+ error = (cmd == SIOCADDMULTI) ?
+ ether_addmulti(ifr, &sc->sc_arpcom) :
+ ether_delmulti(ifr, &sc->sc_arpcom);
+
+ if (error == ENETRESET) {
+ /* Reset multicast filtering. */
+ lemac_init(sc);
+ error = 0;
+ }
+ break;
+
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, (struct ifreq *)data,
+ &sc->sc_ifmedia, cmd);
+ break;
+
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu > ETHERMTU || ifr->ifr_mtu < ETHERMIN) {
+ error = EINVAL;
+ } else if (ifp->if_mtu != ifr->ifr_mtu) {
+ ifp->if_mtu = ifr->ifr_mtu;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ splx(s);
+ return (error);
+}
+
+int
+lemac_ifmedia_change(struct ifnet *const ifp)
+{
+ struct lemac_softc *const sc = LEMAC_IFP_TO_SOFTC(ifp);
+ unsigned new_ctl;
+
+ switch (IFM_SUBTYPE(sc->sc_ifmedia.ifm_media)) {
+ case IFM_10_T:
+ new_ctl = LEMAC_CTL_APD;
+ break;
+ case IFM_10_2:
+ case IFM_10_5:
+ new_ctl = LEMAC_CTL_APD|LEMAC_CTL_PSL;
+ break;
+ case IFM_AUTO:
+ new_ctl = 0;
+ break;
+ default:
+ return (EINVAL);
+ }
+ if (sc->sc_ctlmode != new_ctl) {
+ sc->sc_ctlmode = new_ctl;
+ lemac_reset(sc);
+ if (sc->sc_if.if_flags & IFF_UP)
+ lemac_init(sc);
+ }
+ return (0);
+}
+
+/*
+ * Media status callback
+ */
+void
+lemac_ifmedia_status(struct ifnet *const ifp, struct ifmediareq *req)
+{
+ struct lemac_softc *sc = LEMAC_IFP_TO_SOFTC(ifp);
+ unsigned data = LEMAC_INB(sc, LEMAC_REG_CNF);
+
+ req->ifm_status = IFM_AVALID;
+ if (sc->sc_flags & LEMAC_LINKUP)
+ req->ifm_status |= IFM_ACTIVE;
+
+ if (sc->sc_ctlmode & LEMAC_CTL_APD) {
+ if (sc->sc_ctlmode & LEMAC_CTL_PSL) {
+ req->ifm_active = IFM_10_5;
+ } else {
+ req->ifm_active = IFM_10_T;
+ }
+ } else {
+ /*
+ * The link bit of the configuration register reflects the
+ * current media choice when auto-port is enabled.
+ */
+ if (data & LEMAC_CNF_NOLINK) {
+ req->ifm_active = IFM_10_5;
+ } else {
+ req->ifm_active = IFM_10_T;
+ }
+ }
+
+ req->ifm_active |= IFM_ETHER;
+}
+
+int
+lemac_port_check(const bus_space_tag_t iot, const bus_space_handle_t ioh)
+{
+ unsigned char hwaddr[6];
+
+ if (lemac_read_macaddr(hwaddr, iot, ioh, LEMAC_REG_APD, 0) == 0)
+ return (1);
+ if (lemac_read_macaddr(hwaddr, iot, ioh, LEMAC_REG_APD, 1) == 0)
+ return (1);
+ return (0);
+}
+
+void
+lemac_info_get(const bus_space_tag_t iot, const bus_space_handle_t ioh,
+ bus_addr_t *maddr_p, bus_size_t *msize_p, int *irq_p)
+{
+ unsigned data;
+
+ *irq_p = LEMAC_DECODEIRQ(bus_space_read_1(iot, ioh, LEMAC_REG_IC) &
+ LEMAC_IC_IRQMSK);
+
+ data = bus_space_read_1(iot, ioh, LEMAC_REG_MBR);
+ if (LEMAC_IS_2K_MODE(data)) {
+ *maddr_p = data * (2 * 1024) + (512 * 1024);
+ *msize_p = 2 * 1024;
+ } else if (LEMAC_IS_64K_MODE(data)) {
+ *maddr_p = data * 64 * 1024;
+ *msize_p = 64 * 1024;
+ } else if (LEMAC_IS_32K_MODE(data)) {
+ *maddr_p = data * 32 * 1024;
+ *msize_p = 32* 1024;
+ } else {
+ *maddr_p = 0;
+ *msize_p = 0;
+ }
+}
+
+/*
+ * What to do upon receipt of an interrupt.
+ */
+int
+lemac_intr(void *arg)
+{
+ struct lemac_softc *const sc = arg;
+ int cs_value;
+
+ LEMAC_INTR_DISABLE(sc); /* Mask interrupts */
+
+ /*
+ * Determine cause of interrupt. Receive events take
+ * priority over Transmit.
+ */
+
+ cs_value = LEMAC_INB(sc, LEMAC_REG_CS);
+
+ /*
+ * Check for Receive Queue not being empty.
+ * Check for Transmit Done Queue not being empty.
+ */
+
+ if (cs_value & LEMAC_CS_RNE)
+ lemac_rne_intr(sc);
+ if (cs_value & LEMAC_CS_TNE)
+ lemac_tne_intr(sc);
+
+ /*
+ * Check for Transmitter Disabled.
+ * Check for Receiver Disabled.
+ */
+
+ if (cs_value & LEMAC_CS_TXD)
+ lemac_txd_intr(sc, cs_value);
+ if (cs_value & LEMAC_CS_RXD)
+ lemac_rxd_intr(sc, cs_value);
+
+ /*
+ * Toggle LED and unmask interrupts.
+ */
+
+ sc->sc_csr.csr_cs = LEMAC_INB(sc, LEMAC_REG_CS);
+
+ LEMAC_OUTB(sc, LEMAC_REG_CTL,
+ LEMAC_INB(sc, LEMAC_REG_CTL) ^ LEMAC_CTL_LED);
+ LEMAC_INTR_ENABLE(sc); /* Unmask interrupts */
+
+#if 0
+ if (cs_value)
+ rnd_add_uint32(&sc->rnd_source, cs_value);
+#endif
+
+ return (1);
+}
+
+void
+lemac_shutdown(void *arg)
+{
+ lemac_reset((struct lemac_softc *)arg);
+}
+
+const char *const lemac_modes[4] = {
+ "PIO mode (internal 2KB window)",
+ "2KB window",
+ "changed 32KB window to 2KB",
+ "changed 64KB window to 2KB",
+};
+
+void
+lemac_ifattach(struct lemac_softc *sc)
+{
+ struct ifnet *const ifp = &sc->sc_if;
+
+ bcopy(sc->sc_dv.dv_xname, ifp->if_xname, IFNAMSIZ);
+
+ lemac_reset(sc);
+
+ lemac_read_macaddr(sc->sc_arpcom.ac_enaddr, sc->sc_iot, sc->sc_ioh,
+ LEMAC_REG_APD, 0);
+
+ printf(": %s\n", sc->sc_prodname);
+
+ printf("%s: address %s, %dKB RAM, %s\n", ifp->if_xname,
+ ether_sprintf(sc->sc_arpcom.ac_enaddr), sc->sc_lastpage * 2 + 2,
+ lemac_modes[sc->sc_flags & LEMAC_MODE_MASK]);
+
+ ifp->if_softc = (void *)sc;
+ ifp->if_start = lemac_ifstart;
+ ifp->if_ioctl = lemac_ifioctl;
+
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX
+#ifdef IFF_NOTRAILERS
+ | IFF_NOTRAILERS
+#endif
+ | IFF_MULTICAST;
+
+ if (sc->sc_flags & LEMAC_ALIVE) {
+ int media;
+
+ IFQ_SET_READY(&ifp->if_snd);
+
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+#if 0
+ rnd_attach_source(&sc->rnd_source, sc->sc_dv.dv_xname,
+ RND_TYPE_NET, 0);
+#endif
+
+ ifmedia_init(&sc->sc_ifmedia, 0, lemac_ifmedia_change,
+ lemac_ifmedia_status);
+ if (sc->sc_prodname[4] == '5') /* DE205 is UTP/AUI */
+ ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0,
+ 0);
+ if (sc->sc_prodname[4] != '3') /* DE204 & 205 have UTP */
+ ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T, 0,
+ 0);
+ if (sc->sc_prodname[4] != '4') /* DE203 & 205 have BNC */
+ ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_5, 0,
+ 0);
+ switch (sc->sc_prodname[4]) {
+ case '3':
+ media = IFM_10_5;
+ break;
+ case '4':
+ media = IFM_10_T;
+ break;
+ default:
+ media = IFM_AUTO;
+ break;
+ }
+ ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | media);
+ } else {
+ printf("%s: disabled due to error\n", ifp->if_xname);
+ }
+}
diff --git a/sys/dev/ic/lemacreg.h b/sys/dev/ic/lemacreg.h
new file mode 100644
index 00000000000..045b8cb5878
--- /dev/null
+++ b/sys/dev/ic/lemacreg.h
@@ -0,0 +1,187 @@
+/* $OpenBSD: lemacreg.h,v 1.1 2001/07/13 17:26:44 niklas Exp $ */
+/* $NetBSD: lemacreg.h,v 1.2 2001/06/13 10:46:03 wiz Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995, 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 without 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.
+ */
+#ifndef _LEMAC_H_
+#define _LEMAC_H_
+
+#define LEMAC_IOBASE_LOW 0x200
+#define LEMAC_IOBASE_HIGH 0x400
+
+/*
+ * This is list of registers used on a DEC EtherWORKS III card.
+ * Each board occupies a 32 byte register space. This can be
+ * in either EISA or ISA space. Currently we only support ISA
+ * space.
+ */
+
+#define LEMAC_REG_CS 0x00 /* Control and Status */
+#define LEMAC_REG_CTL 0x01 /* Control */
+#define LEMAC_REG_IC 0x02 /* Interrupt Control */
+#define LEMAC_REG_TS 0x03 /* Transmit Status */
+#define LEMAC_REG_RSVD1 0x04 /* Reserved (not used) */
+#define LEMAC_REG_RSVD2 0x05 /* Reserved (not used) */
+#define LEMAC_REG_FMQ 0x06 /* Free Memory Queue */
+#define LEMAC_REG_FMC 0x07 /* Free Memory Queue Count */
+#define LEMAC_REG_RQ 0x08 /* Receive Queue */
+#define LEMAC_REG_RQC 0x09 /* Receive Queue Count */
+#define LEMAC_REG_TQ 0x0A /* Transmit Queue */
+#define LEMAC_REG_TQC 0x0B /* Transmit Queue Count */
+#define LEMAC_REG_TDQ 0x0C /* Transmit Done Queue */
+#define LEMAC_REG_TDC 0x0D /* Transmit Done Queue Count */
+#define LEMAC_REG_PI1 0x0E /* Page Index #1 */
+#define LEMAC_REG_PI2 0x0F /* Page Index #2 */
+#define LEMAC_REG_DAT 0x10 /* Data */
+#define LEMAC_REG_IOP 0x11 /* I/O Page */
+#define LEMAC_REG_IOB 0x12 /* I/O Base */
+#define LEMAC_REG_MPN 0x13 /* Memory Page */
+#define LEMAC_REG_MBR 0x14 /* Memory Base */
+#define LEMAC_REG_APD 0x15 /* Address PROM */
+#define LEMAC_REG_EE1 0x16 /* EEPROM Data #1 */
+#define LEMAC_REG_EE2 0x17 /* EEPROM Data #2 */
+#define LEMAC_REG_PA0 0x18 /* Physical Address (Byte 0) */
+#define LEMAC_REG_PA1 0x19 /* Physical Address (Byte 1) */
+#define LEMAC_REG_PA2 0x1A /* Physical Address (Byte 2) */
+#define LEMAC_REG_PA3 0x1B /* Physical Address (Byte 3) */
+#define LEMAC_REG_PA4 0x1C /* Physical Address (Byte 4) */
+#define LEMAC_REG_PA5 0x1D /* Physical Address (Byte 5) */
+#define LEMAC_REG_CNF 0x1E /* Configuration Management */
+#define LEMAC_IOSIZE 0x20 /* LEMAC uses 32 bytes of IOSPACE */
+
+
+#define LEMAC_REG_EID0 0x80 /* EISA Identification 0 */
+#define LEMAC_REG_EID1 0x81 /* EISA Identification 1 */
+#define LEMAC_REG_EID2 0x82 /* EISA Identification 2 */
+#define LEMAC_REG_EID3 0x83 /* EISA Identification 3 */
+#define LEMAC_REG_EIC 0x84 /* EISA Control */
+
+/* Control Page (Page 0) Definitions */
+
+#define LEMAC_MCTBL_BITS 9
+#define LEMAC_MCTBL_OFF 512
+#define LEMAC_MCTBL_SIZE (1 << (LEMAC_MCTBL_BITS - 3))
+#define LEMAC_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- Little Endian) */
+
+/* EEPROM Definitions */
+
+#define LEMAC_EEP_CKSUM 0 /* The valid checksum is 0 */
+#define LEMAC_EEP_SIZE 32 /* EEPROM is 32 bytes */
+#define LEMAC_EEP_DELAY 2000 /* 2ms = 2000us */
+#define LEMAC_EEP_PRDNM 8 /* Product Name Offset */
+#define LEMAC_EEP_PRDNMSZ 8 /* Product Name Size */
+#define LEMAC_EEP_SWFLAGS 16 /* Software Options Offset */
+#define LEMAC_EEP_SETUP 23 /* Setup Options Offset */
+
+#define LEMAC_EEP_SW_SQE 0x10 /* Enable TX_SQE on Transmits */
+#define LEMAC_EEP_SW_LAB 0x08 /* Enable TX_LAB on Transmits */
+#define LEMAC_EEP_ST_DRAM 0x02 /* Enable extra DRAM */
+
+#define LEMAC_ADP_ROMSZ 32 /* Size of Address PROM */
+
+/* Receive Status Definitions */
+
+#define LEMAC_RX_PLL 0x01 /* Phase Lock Lost */
+#define LEMAC_RX_CRC 0x02 /* CRC Error */
+#define LEMAC_RX_DBE 0x04 /* Dribble Bit Error */
+#define LEMAC_RX_MCM 0x08 /* Multicast Match */
+#define LEMAC_RX_IAM 0x10 /* Individual Address Match */
+#define LEMAC_RX_OK 0x80 /* No Errors */
+
+/* Transmit Status Definitions (not valid if TXD == 0) */
+
+#define LEMAC_TS_RTRYMSK 0x0F /* Retries of last TX PDU */
+#define LEMAC_TS_ECL 0x10 /* Excessive collision of ... */
+#define LEMAC_TS_LCL 0x20 /* Late collision of ... */
+#define LEMAC_TS_ID 0x40 /* Initially Deferred ... */
+#define LEMAC_TS_NCL 0x08 /* No carrier loopback ... */
+
+/* Transmit Control Definitions */
+
+#define LEMAC_TX_ISA 0x01 /* Insert Source Address (no) */
+#define LEMAC_TX_IFC 0x02 /* Insert Frame Check (yes) */
+#define LEMAC_TX_PAD 0x04 /* Zero PAD to mininum length (yes) */
+#define LEMAC_TX_LAB 0x08 /* Less Agressive Backoff (no) */
+#define LEMAC_TX_QMD 0x10 /* Q-Mode (yes) */
+#define LEMAC_TX_STP 0x20 /* Stop on Error (no) */
+#define LEMAC_TX_SQE 0x40 /* SQE Enable (yes) */
+
+#define LEMAC_TX_FLAGS (LEMAC_TX_IFC|LEMAC_TX_PAD|LEMAC_TX_QMD|\
+ LEMAC_TX_SQE)
+#define LEMAC_TX_HDRSZ 4 /* Size of TX header */
+
+/* Transmit Done Queue Status Definitions */
+
+#define LEMAC_TDQ_COL 0x03 /* Collision Mask */
+#define LEMAC_TDQ_NOCOL 0x00 /* No Collisions */
+#define LEMAC_TDQ_ONECOL 0x01 /* One Collision */
+#define LEMAC_TDQ_MULCOL 0x02 /* Multiple Collisions */
+#define LEMAC_TDQ_EXCCOL 0x03 /* Excesive Collisions */
+#define LEMAC_TDQ_ID 0x04 /* Initially Deferred */
+#define LEMAC_TDQ_LCL 0x08 /* Late Collision (will TX_STP) */
+#define LEMAC_TDQ_NCL 0x10 /* No carrier loopback */
+#define LEMAC_TDQ_SQE 0x20 /* SQE error */
+
+/* Control / Status Definitions */
+
+#define LEMAC_CS_RXD 0x01 /* Receiver Disabled */
+#define LEMAC_CS_TXD 0x02 /* Transmitter Disabled */
+#define LEMAC_CS_RNE 0x04 /* Receive Queue Not Empty */
+#define LEMAC_CS_TNE 0x08 /* Transmit Done Queue Not Empty */
+#define LEMAC_CS_MBZ4 0x10 /* MBZ */
+#define LEMAC_CS_MCE 0x20 /* Multicast Enable */
+#define LEMAC_CS_PME 0x40 /* Promiscuous Mode Enable */
+#define LEMAC_CS_RA 0x80 /* Runt Accept */
+
+/* Control Definitions */
+
+#define LEMAC_CTL_LED 0x02 /* LED state (inverted) */
+#define LEMAC_CTL_PSL 0x40 /* Port Select (1=AUI, 0=UTP) */
+#define LEMAC_CTL_APD 0x80 /* Auto Port Disable */
+
+/* Interrupt Control Definitions */
+
+#define LEMAC_IC_RXD 0x01 /* Enable RXD Interrupt */
+#define LEMAC_IC_TXD 0x02 /* Enable TXD Interrupt */
+#define LEMAC_IC_RNE 0x04 /* Enable RNE Interrupt */
+#define LEMAC_IC_TNE 0x08 /* Enable TNE Interrupt */
+#define LEMAC_IC_ALL 0x0F /* Enable RXD,TXD,RNE,TNE */
+#define LEMAC_IC_IRQMSK 0x60 /* Interrupt Select */
+#define LEMAC_IC_IRQ5 0x00 /* Select IRQ 5 */
+#define LEMAC_IC_IRQ10 0x20 /* Select IRQ 10 */
+#define LEMAC_IC_IRQ11 0x40 /* Select IRQ 11 */
+#define LEMAC_IC_IRQ15 0x60 /* Select IRQ 15 */
+#define LEMAC_IC_IE 0x80 /* Interrupt Enable */
+
+/* I/O Page Definitions */
+
+#define LEMAC_IOP_EEINIT 0xC0 /* Perform a board init/reset */
+#define LEMAC_IOP_EEREAD 0xE0 /* Start a read from EEPROM */
+
+/* Configuration / Management Definitions */
+
+#define LEMAC_CNF_DRAM 0x02 /* Extra on-board DRAM is available */
+#define LEMAC_CNF_NOLINK 0x20 /* UTP port is UP */
+
+#endif /* _LEMAC_H_ */
diff --git a/sys/dev/ic/lemacvar.h b/sys/dev/ic/lemacvar.h
new file mode 100644
index 00000000000..c3aac8cce9f
--- /dev/null
+++ b/sys/dev/ic/lemacvar.h
@@ -0,0 +1,160 @@
+/* $OpenBSD: lemacvar.h,v 1.1 2001/07/13 17:26:44 niklas Exp $ */
+/* $NetBSD: lemacvar.h,v 1.6 2001/06/13 10:46:03 wiz Exp $ */
+
+/*
+ * Copyright (c) 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 without 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.
+ */
+
+#ifndef _LEMAC_VAR_H
+#define _LEMAC_VAR_H
+
+/*
+ * Ethernet status, per interface.
+ */
+struct lemac_softc {
+ struct device sc_dv;
+ void *sc_ih;
+ void *sc_ats;
+ struct arpcom sc_arpcom; /* Ethernet common part */
+ struct ifmedia sc_ifmedia;
+ bus_space_tag_t sc_iot;
+ bus_space_tag_t sc_memt;
+ bus_space_handle_t sc_ioh;
+ bus_space_handle_t sc_memh;
+ unsigned sc_flags;
+#define LEMAC_PIO_MODE 0x0000U
+#define LEMAC_2K_MODE 0x0001U
+#define LEMAC_WAS_32K_MODE 0x0002U
+#define LEMAC_WAS_64K_MODE 0x0003U
+#define LEMAC_MODE_MASK 0x0003U
+#define LEMAC_ALLMULTI 0x0010U
+#define LEMAC_ALIVE 0x0020U
+#define LEMAC_LINKUP 0x0040U
+ unsigned sc_lastpage; /* last 2K page */
+ unsigned sc_txctl; /* Transmit Control Byte */
+ unsigned sc_ctlmode; /* media ctl bits */
+ struct {
+ u_int8_t csr_cs;
+ u_int8_t csr_tqc;
+ u_int8_t csr_fmq;
+ } sc_csr;
+ unsigned sc_laststatus; /* last read of LEMAC_REG_CS */
+ u_int16_t sc_mctbl[LEMAC_MCTBL_SIZE/sizeof(u_int16_t)];
+ /* local copy of multicast table */
+ struct {
+ unsigned cntr_txnospc; /* total # of no trnasmit memory */
+ unsigned cntr_txfull; /* total # of tranmitter full */
+ unsigned cntr_tne_intrs;/* total # of tranmit done intrs */
+ unsigned cntr_rne_intrs;/* total # of receive done intrs */
+ unsigned cntr_txd_intrs;/* total # of tranmit error intrs */
+ unsigned cntr_rxd_intrs;/* total # of receive error intrs */
+ } sc_cntrs;
+
+ /*
+ * We rely on sc_enaddr being aligned on (at least) a 16 bit boundary
+ */
+ unsigned char sc_enaddr[ETHER_ADDR_LEN];/* current Ethernet address */
+ char sc_prodname[LEMAC_EEP_PRDNMSZ+1]; /* product name DE20x-xx */
+ u_int8_t sc_eeprom[LEMAC_EEP_SIZE]; /* local copy eeprom */
+};
+
+#define sc_if sc_arpcom.ac_if
+
+#define LEMAC_IFP_TO_SOFTC(ifp) ((struct lemac_softc *)((ifp)->if_softc))
+#define LEMAC_USE_PIO_MODE(sc) \
+ (((sc->sc_flags & LEMAC_MODE_MASK) == LEMAC_PIO_MODE) || \
+ (sc->sc_if.if_flags & IFF_LINK0))
+
+#define LEMAC_OUTB(sc, o, v) \
+ bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (o), (v))
+#define LEMAC_OUTSB(sc, o, l, p) \
+ bus_space_write_multi_1((sc)->sc_iot, (sc)->sc_ioh, (o), (p), (l))
+#define LEMAC_INB(sc, o) \
+ bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (o))
+#define LEMAC_INSB(sc, o, l, p) \
+ bus_space_read_multi_1((sc)->sc_iot, (sc)->sc_ioh, (o), (p), (l))
+
+#define LEMAC_PUTBUF8(sc, o, l, p) \
+ bus_space_write_region_1((sc)->sc_memt, (sc)->sc_memh, (o), (p), (l))
+#define LEMAC_PUTBUF16(sc, o, l, p) \
+ bus_space_write_raw_region_2((sc)->sc_memt, (sc)->sc_memh, (o), (p), \
+ (l) << 1)
+#define LEMAC_PUTBUF32(sc, o, l, p) \
+ bus_space_write_raw_region_4((sc)->sc_memt, (sc)->sc_memh, (o), (p), \
+ (l) << 2)
+
+#define LEMAC_PUT8(sc, o, v) \
+ bus_space_write_1((sc)->sc_memt, (sc)->sc_memh, (o), (v))
+#define LEMAC_PUT16(sc, o, v) \
+ bus_space_write_2((sc)->sc_memt, (sc)->sc_memh, (o), htole16(v))
+#define LEMAC_PUT32(sc, o, v) \
+ bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (o), htole32(v))
+
+#define LEMAC_GETBUF8(sc, o, l, p) \
+ bus_space_read_region_1((sc)->sc_memt, (sc)->sc_memh, (o), (p), (l))
+#define LEMAC_GETBUF16(sc, o, l, p) \
+ bus_space_read_raw_region_2((sc)->sc_memt, (sc)->sc_memh, (o), (p), \
+ (l) << 1)
+#define LEMAC_GETBUF32(sc, o, l, p) \
+ bus_space_read_raw_region_4((sc)->sc_memt, (sc)->sc_memh, (o), (p), \
+ (l) << 2)
+
+#define LEMAC_GET8(sc, o) \
+ bus_space_read_1((sc)->sc_memt, (sc)->sc_memh, (o))
+#define LEMAC_GET16(sc, o) \
+ letoh16(bus_space_read_2((sc)->sc_memt, (sc)->sc_memh, (o)))
+#define LEMAC_GET32(sc, o) \
+ letoh32(bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (o)))
+
+#define LEMAC_INTR_ENABLE(sc) \
+ LEMAC_OUTB(sc, LEMAC_REG_IC, \
+ LEMAC_INB(sc, LEMAC_REG_IC) | LEMAC_IC_ALL)
+
+#define LEMAC_INTR_DISABLE(sc) \
+ LEMAC_OUTB(sc, LEMAC_REG_IC, \
+ LEMAC_INB(sc, LEMAC_REG_IC) & ~LEMAC_IC_ALL)
+
+#define LEMAC_IS_64K_MODE(mbase) (((mbase) >= 0x0A) && ((mbase) <= 0x0F))
+#define LEMAC_IS_32K_MODE(mbase) (((mbase) >= 0x14) && ((mbase) <= 0x1F))
+#define LEMAC_IS_2K_MODE(mbase) ((mbase) >= 0x40)
+
+#define LEMAC_DECODEIRQ(i) ((0xFBA5 >> ((i) >> 3)) & 0x0F)
+
+#define LEMAC_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 LEMAC_ADDRBRDCST(a1) \
+ (((u_int16_t *)a1)[0] == 0xFFFFU && \
+ ((u_int16_t *)a1)[1] == 0xFFFFU && \
+ ((u_int16_t *)a1)[2] == 0xFFFFU)
+
+void lemac_ifattach(struct lemac_softc *);
+void lemac_info_get(const bus_space_tag_t, const bus_space_handle_t,
+ bus_addr_t *, bus_size_t *, int *);
+int lemac_port_check(const bus_space_tag_t, const bus_space_handle_t);
+int lemac_intr(void *);
+void lemac_shutdown(void *);
+
+#endif /* _LEMACVAR_H */
diff --git a/sys/dev/isa/files.isa b/sys/dev/isa/files.isa
index 9c7e5b2b533..41aa7133719 100644
--- a/sys/dev/isa/files.isa
+++ b/sys/dev/isa/files.isa
@@ -1,4 +1,4 @@
-# $OpenBSD: files.isa,v 1.65 2001/01/30 00:00:33 aaron Exp $
+# $OpenBSD: files.isa,v 1.66 2001/07/13 17:26:44 niklas Exp $
# $NetBSD: files.isa,v 1.21 1996/05/16 03:45:55 mycroft Exp $
#
# Config file and device description for machine-independent ISA code.
@@ -149,6 +149,10 @@ file dev/isa/wt.c wt needs-flag
# ISA networking drivers
#
+# DEC EtherWORKS III (LEMAC-based: DE203, DE204, DE205) cards
+attach lc at isa with lc_isa
+file dev/isa/if_lc_isa.c lc_isa
+
# Novell NE1000, NE2000, and clones
# Must be before the WD/SMC probe which it is hurt by.
attach ne at isa with ne_isa: rtl80x9
diff --git a/sys/dev/isa/if_lc_isa.c b/sys/dev/isa/if_lc_isa.c
new file mode 100644
index 00000000000..c50940103b9
--- /dev/null
+++ b/sys/dev/isa/if_lc_isa.c
@@ -0,0 +1,215 @@
+/* $OpenBSD: if_lc_isa.c,v 1.1 2001/07/13 17:26:44 niklas Exp $ */
+/* $NetBSD: if_lc_isa.c,v 1.10 2001/06/13 10:46:03 wiz Exp $ */
+
+/*-
+ * Copyright (c) 1994, 1995, 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 without 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.
+ */
+
+/*
+ * DEC EtherWORKS 3 Ethernet Controllers
+ *
+ * Written by Matt Thomas
+ *
+ * This driver supports the LEMAC (DE203, DE204, and DE205) cards.
+ */
+
+#include "bpfilter.h"
+
+#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/syslog.h>
+#include <sys/select.h>
+#include <sys/device.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <machine/cpu.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ic/lemacreg.h>
+#include <dev/ic/lemacvar.h>
+
+#include <dev/isa/isavar.h>
+
+extern struct cfdriver lc_cd;
+
+int lemac_isa_find __P((struct lemac_softc *, struct isa_attach_args *,
+ int));
+int lemac_isa_probe __P((struct device *, void *, void *));
+void lemac_isa_attach __P((struct device *, struct device *, void *));
+
+struct cfattach lc_isa_ca = {
+ sizeof(struct lemac_softc), lemac_isa_probe, lemac_isa_attach
+};
+
+int
+lemac_isa_find(sc, ia, attach)
+ struct lemac_softc *sc;
+ struct isa_attach_args *ia;
+ int attach;
+{
+ bus_addr_t maddr;
+ bus_addr_t msize;
+ int rv = 0, irq;
+
+ /*
+ * Disallow wildcarded i/o addresses.
+ */
+ if (ia->ia_iobase == IOBASEUNK)
+ return 0;
+
+ /*
+ * Make sure this is a valid LEMAC address.
+ */
+ if (ia->ia_iobase & (LEMAC_IOSIZE - 1))
+ return 0;
+
+ sc->sc_iot = ia->ia_iot;
+
+ /*
+ * Map the LEMAC's port space for the probe sequence.
+ */
+ ia->ia_iosize = LEMAC_IOSIZE;
+
+ if (bus_space_map(sc->sc_iot, ia->ia_iobase, ia->ia_iosize, 0,
+ &sc->sc_ioh)) {
+ if (attach)
+ printf(": can't map i/o space\n");
+ return (0);
+ }
+
+ /*
+ * Read the Ethernet address from the EEPROM.
+ * It must start with one of the DEC OUIs and pass the
+ * DEC ethernet checksum test.
+ */
+ if (lemac_port_check(sc->sc_iot, sc->sc_ioh) == 0)
+ goto outio;
+
+ /*
+ * Get information about memory space and attempt to map it.
+ */
+ lemac_info_get(sc->sc_iot, sc->sc_ioh, &maddr, &msize, &irq);
+
+ if (ia->ia_maddr != maddr && ia->ia_maddr != MADDRUNK)
+ goto outio;
+
+ if (maddr != 0 && msize != 0) {
+ sc->sc_memt = ia->ia_memt;
+ if (bus_space_map(ia->ia_memt, maddr, msize, 0,
+ &sc->sc_memh)) {
+ if (attach)
+ printf(": can't map mem space\n");
+ goto outio;
+ }
+ }
+
+ /*
+ * Double-check IRQ configuration.
+ */
+ if (ia->ia_irq != irq && ia->ia_irq != IRQUNK)
+ printf("%s: overriding IRQ %d to %d\n", sc->sc_dv.dv_xname,
+ ia->ia_irq, irq);
+
+ if (attach) {
+ sc->sc_ats = shutdownhook_establish(lemac_shutdown, sc);
+ if (sc->sc_ats == NULL)
+ printf(
+ "\n%s: warning: can't establish shutdown hook\n",
+ sc->sc_dv.dv_xname);
+
+ lemac_ifattach(sc);
+
+ sc->sc_ih = isa_intr_establish(ia->ia_ic, irq, IST_EDGE,
+ IPL_NET, lemac_intr, sc, sc->sc_dv.dv_xname);
+ }
+
+ /*
+ * I guess we've found one.
+ */
+ rv = 1;
+
+ ia->ia_maddr = maddr;
+ ia->ia_msize = msize;
+ ia->ia_irq = irq;
+
+ if (maddr != 0 && msize != 0 && (rv == 0 || !attach))
+ bus_space_unmap(sc->sc_memt, sc->sc_memh, msize);
+outio:
+ if (rv == 0 || !attach)
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, LEMAC_IOSIZE);
+ return (rv);
+}
+
+int
+lemac_isa_probe(parent, match, aux)
+ struct device *parent;
+ void *match;
+ void *aux;
+{
+ struct isa_attach_args *ia = aux;
+ struct cfdata *cf = match;
+ struct lemac_softc sc;
+
+ sprintf(sc.sc_dv.dv_xname, "%s%d", lc_cd.cd_name, cf->cf_unit);
+
+ return (lemac_isa_find(&sc, ia, 0));
+}
+
+void
+lemac_isa_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct lemac_softc *sc = (void *)self;
+ struct isa_attach_args *ia = aux;
+
+ lemac_isa_find(sc, ia, 1);
+}