summaryrefslogtreecommitdiffstats
path: root/sys/dev/ex
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ex')
-rw-r--r--sys/dev/ex/if_ex.c1078
-rw-r--r--sys/dev/ex/if_ex_isa.c311
-rw-r--r--sys/dev/ex/if_ex_pccard.c146
-rw-r--r--sys/dev/ex/if_exreg.h209
-rw-r--r--sys/dev/ex/if_exvar.h90
5 files changed, 1834 insertions, 0 deletions
diff --git a/sys/dev/ex/if_ex.c b/sys/dev/ex/if_ex.c
new file mode 100644
index 0000000..7988d0f
--- /dev/null
+++ b/sys/dev/ex/if_ex.c
@@ -0,0 +1,1078 @@
+/*
+ * Copyright (c) 1996, Javier Martín Rueda (jmrueda@diatel.upm.es)
+ * 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 unmodified, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ *
+ * MAINTAINER: Matthew N. Dodd <winter@jurai.net>
+ * <mdodd@FreeBSD.org>
+ */
+
+/*
+ * Intel EtherExpress Pro/10, Pro/10+ Ethernet driver
+ *
+ * Revision history:
+ *
+ * dd-mmm-yyyy: Multicast support ported from NetBSD's if_iy driver.
+ * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/bpf.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+
+#include <isa/isavar.h>
+#include <isa/pnpvar.h>
+
+#include <dev/ex/if_exreg.h>
+#include <dev/ex/if_exvar.h>
+
+#ifdef EXDEBUG
+# define Start_End 1
+# define Rcvd_Pkts 2
+# define Sent_Pkts 4
+# define Status 8
+static int debug_mask = 0;
+static int exintr_count = 0;
+# define DODEBUG(level, action) if (level & debug_mask) action
+#else
+# define DODEBUG(level, action)
+#endif
+
+devclass_t ex_devclass;
+
+char irq2eemap[] =
+ { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 };
+u_char ee2irqmap[] =
+ { 9, 3, 5, 10, 11, 0, 0, 0 };
+
+char plus_irq2eemap[] =
+ { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 };
+u_char plus_ee2irqmap[] =
+ { 3, 4, 5, 7, 9, 10, 11, 12 };
+
+/* Network Interface Functions */
+static void ex_init (void *);
+static void ex_start (struct ifnet *);
+static int ex_ioctl (struct ifnet *, u_long, caddr_t);
+static void ex_watchdog (struct ifnet *);
+
+/* ifmedia Functions */
+static int ex_ifmedia_upd (struct ifnet *);
+static void ex_ifmedia_sts (struct ifnet *, struct ifmediareq *);
+
+static int ex_get_media (u_int32_t iobase);
+
+static void ex_reset (struct ex_softc *);
+static void ex_setmulti (struct ex_softc *);
+
+static void ex_tx_intr (struct ex_softc *);
+static void ex_rx_intr (struct ex_softc *);
+
+int
+look_for_card (u_int32_t iobase)
+{
+ int count1, count2;
+
+ /*
+ * Check for the i82595 signature, and check that the round robin
+ * counter actually advances.
+ */
+ if (((count1 = inb(iobase + ID_REG)) & Id_Mask) != Id_Sig)
+ return(0);
+ count2 = inb(iobase + ID_REG);
+ count2 = inb(iobase + ID_REG);
+ count2 = inb(iobase + ID_REG);
+
+ return((count2 & Counter_bits) == ((count1 + 0xc0) & Counter_bits));
+}
+
+void
+ex_get_address (u_int32_t iobase, u_char *enaddr)
+{
+ u_int16_t eaddr_tmp;
+
+ eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Lo);
+ enaddr[5] = eaddr_tmp & 0xff;
+ enaddr[4] = eaddr_tmp >> 8;
+ eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Mid);
+ enaddr[3] = eaddr_tmp & 0xff;
+ enaddr[2] = eaddr_tmp >> 8;
+ eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Hi);
+ enaddr[1] = eaddr_tmp & 0xff;
+ enaddr[0] = eaddr_tmp >> 8;
+
+ return;
+}
+
+int
+ex_card_type (u_char *enaddr)
+{
+ if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9))
+ return (CARD_TYPE_EX_10_PLUS);
+
+ return (CARD_TYPE_EX_10);
+}
+
+/*
+ * Caller is responsible for eventually calling
+ * ex_release_resources() on failure.
+ */
+int
+ex_alloc_resources (device_t dev)
+{
+ struct ex_softc * sc = device_get_softc(dev);
+ int error = 0;
+
+ sc->ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->ioport_rid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (!sc->ioport) {
+ device_printf(dev, "No I/O space?!\n");
+ error = ENOMEM;
+ goto bad;
+ }
+
+ sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
+ 0, ~0, 1, RF_ACTIVE);
+
+ if (!sc->irq) {
+ device_printf(dev, "No IRQ?!\n");
+ error = ENOMEM;
+ goto bad;
+ }
+
+bad:
+ return (error);
+}
+
+void
+ex_release_resources (device_t dev)
+{
+ struct ex_softc * sc = device_get_softc(dev);
+
+ if (sc->ih) {
+ bus_teardown_intr(dev, sc->irq, sc->ih);
+ sc->ih = NULL;
+ }
+
+ if (sc->ioport) {
+ bus_release_resource(dev, SYS_RES_IOPORT,
+ sc->ioport_rid, sc->ioport);
+ sc->ioport = NULL;
+ }
+
+ if (sc->irq) {
+ bus_release_resource(dev, SYS_RES_IRQ,
+ sc->irq_rid, sc->irq);
+ sc->irq = NULL;
+ }
+
+ return;
+}
+
+int
+ex_attach(device_t dev)
+{
+ struct ex_softc * sc = device_get_softc(dev);
+ struct ifnet * ifp = &sc->arpcom.ac_if;
+ struct ifmedia * ifm;
+ int unit = device_get_unit(dev);
+ u_int16_t temp;
+
+ /* work out which set of irq <-> internal tables to use */
+ if (ex_card_type(sc->arpcom.ac_enaddr) == CARD_TYPE_EX_10_PLUS) {
+ sc->irq2ee = plus_irq2eemap;
+ sc->ee2irq = plus_ee2irqmap;
+ } else {
+ sc->irq2ee = irq2eemap;
+ sc->ee2irq = ee2irqmap;
+ }
+
+ sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */
+
+ /*
+ * Initialize the ifnet structure.
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = unit;
+ ifp->if_name = "ex";
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+ ifp->if_output = ether_output;
+ ifp->if_start = ex_start;
+ ifp->if_ioctl = ex_ioctl;
+ ifp->if_watchdog = ex_watchdog;
+ ifp->if_init = ex_init;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ ifmedia_init(&sc->ifmedia, 0, ex_ifmedia_upd, ex_ifmedia_sts);
+
+ temp = eeprom_read(sc->iobase, EE_W5);
+ if (temp & EE_W5_PORT_TPE)
+ ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
+ if (temp & EE_W5_PORT_BNC)
+ ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL);
+ if (temp & EE_W5_PORT_AUI)
+ ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL);
+
+ ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);
+ ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_NONE, 0, NULL);
+ ifmedia_set(&sc->ifmedia, ex_get_media(sc->iobase));
+
+ ifm = &sc->ifmedia;
+ ifm->ifm_media = ifm->ifm_cur->ifm_media;
+ ex_ifmedia_upd(ifp);
+
+ /*
+ * Attach the interface.
+ */
+ ether_ifattach(ifp, sc->arpcom.ac_enaddr);
+
+ device_printf(sc->dev, "Ethernet address %6D\n",
+ sc->arpcom.ac_enaddr, ":");
+
+ return(0);
+}
+
+int
+ex_detach (device_t dev)
+{
+ struct ex_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ ifp = &sc->arpcom.ac_if;
+
+ ex_stop(sc);
+
+ ifp->if_flags &= ~IFF_RUNNING;
+ ether_ifdetach(ifp);
+
+ ex_release_resources(dev);
+
+ return (0);
+}
+
+static void
+ex_init(void *xsc)
+{
+ struct ex_softc * sc = (struct ex_softc *) xsc;
+ struct ifnet * ifp = &sc->arpcom.ac_if;
+ int s;
+ int i;
+ register int iobase = sc->iobase;
+ unsigned short temp_reg;
+
+ DODEBUG(Start_End, printf("ex_init%d: start\n", ifp->if_unit););
+
+ if (TAILQ_FIRST(&ifp->if_addrhead) == NULL) {
+ return;
+ }
+ s = splimp();
+ ifp->if_timer = 0;
+
+ /*
+ * Load the ethernet address into the card.
+ */
+ outb(iobase + CMD_REG, Bank2_Sel);
+ temp_reg = inb(iobase + EEPROM_REG);
+ if (temp_reg & Trnoff_Enable) {
+ outb(iobase + EEPROM_REG, temp_reg & ~Trnoff_Enable);
+ }
+ for (i = 0; i < ETHER_ADDR_LEN; i++) {
+ outb(iobase + I_ADDR_REG0 + i, sc->arpcom.ac_enaddr[i]);
+ }
+ /*
+ * - Setup transmit chaining and discard bad received frames.
+ * - Match broadcast.
+ * - Clear test mode.
+ * - Set receiving mode.
+ * - Set IRQ number.
+ */
+ outb(iobase + REG1, inb(iobase + REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr);
+ outb(iobase + REG2, inb(iobase + REG2) | No_SA_Ins | RX_CRC_InMem);
+ outb(iobase + REG3, inb(iobase + REG3) & 0x3f /* XXX constants. */ );
+ outb(iobase + CMD_REG, Bank1_Sel);
+ outb(iobase + INT_NO_REG, (inb(iobase + INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]);
+
+ /*
+ * Divide the available memory in the card into rcv and xmt buffers.
+ * By default, I use the first 3/4 of the memory for the rcv buffer,
+ * and the remaining 1/4 of the memory for the xmt buffer.
+ */
+ sc->rx_mem_size = sc->mem_size * 3 / 4;
+ sc->tx_mem_size = sc->mem_size - sc->rx_mem_size;
+ sc->rx_lower_limit = 0x0000;
+ sc->rx_upper_limit = sc->rx_mem_size - 2;
+ sc->tx_lower_limit = sc->rx_mem_size;
+ sc->tx_upper_limit = sc->mem_size - 2;
+ outb(iobase + RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8);
+ outb(iobase + RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8);
+ outb(iobase + XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8);
+ outb(iobase + XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8);
+
+ /*
+ * Enable receive and transmit interrupts, and clear any pending int.
+ */
+ outb(iobase + REG1, inb(iobase + REG1) | TriST_INT);
+ outb(iobase + CMD_REG, Bank0_Sel);
+ outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int));
+ outb(iobase + STATUS_REG, All_Int);
+
+ /*
+ * Initialize receive and transmit ring buffers.
+ */
+ outw(iobase + RCV_BAR, sc->rx_lower_limit);
+ sc->rx_head = sc->rx_lower_limit;
+ outw(iobase + RCV_STOP_REG, sc->rx_upper_limit | 0xfe);
+ outw(iobase + XMT_BAR, sc->tx_lower_limit);
+ sc->tx_head = sc->tx_tail = sc->tx_lower_limit;
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ DODEBUG(Status, printf("OIDLE init\n"););
+
+ ex_setmulti(sc);
+
+ /*
+ * Final reset of the board, and enable operation.
+ */
+ outb(iobase + CMD_REG, Sel_Reset_CMD);
+ DELAY(2);
+ outb(iobase + CMD_REG, Rcv_Enable_CMD);
+
+ ex_start(ifp);
+ splx(s);
+
+ DODEBUG(Start_End, printf("ex_init%d: finish\n", ifp->if_unit););
+}
+
+
+static void
+ex_start(struct ifnet *ifp)
+{
+ struct ex_softc * sc = ifp->if_softc;
+ int iobase = sc->iobase;
+ int i, s, len, data_len, avail, dest, next;
+ unsigned char tmp16[2];
+ struct mbuf * opkt;
+ struct mbuf * m;
+
+ DODEBUG(Start_End, printf("ex_start%d: start\n", unit););
+
+ s = splimp();
+
+ /*
+ * Main loop: send outgoing packets to network card until there are no
+ * more packets left, or the card cannot accept any more yet.
+ */
+ while (((opkt = ifp->if_snd.ifq_head) != NULL) &&
+ !(ifp->if_flags & IFF_OACTIVE)) {
+
+ /*
+ * Ensure there is enough free transmit buffer space for
+ * this packet, including its header. Note: the header
+ * cannot wrap around the end of the transmit buffer and
+ * must be kept together, so we allow space for twice the
+ * length of the header, just in case.
+ */
+
+ for (len = 0, m = opkt; m != NULL; m = m->m_next) {
+ len += m->m_len;
+ }
+
+ data_len = len;
+
+ DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len););
+
+ if (len & 1) {
+ len += XMT_HEADER_LEN + 1;
+ } else {
+ len += XMT_HEADER_LEN;
+ }
+
+ if ((i = sc->tx_tail - sc->tx_head) >= 0) {
+ avail = sc->tx_mem_size - i;
+ } else {
+ avail = -i;
+ }
+
+ DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail););
+
+ if (avail >= len + XMT_HEADER_LEN) {
+ IF_DEQUEUE(&ifp->if_snd, opkt);
+
+#ifdef EX_PSA_INTR
+ /*
+ * Disable rx and tx interrupts, to avoid corruption
+ * of the host address register by interrupt service
+ * routines.
+ * XXX Is this necessary with splimp() enabled?
+ */
+ outb(iobase + MASK_REG, All_Int);
+#endif
+
+ /*
+ * Compute the start and end addresses of this
+ * frame in the tx buffer.
+ */
+ dest = sc->tx_tail;
+ next = dest + len;
+
+ if (next > sc->tx_upper_limit) {
+ if ((sc->tx_upper_limit + 2 - sc->tx_tail) <=
+ XMT_HEADER_LEN) {
+ dest = sc->tx_lower_limit;
+ next = dest + len;
+ } else {
+ next = sc->tx_lower_limit +
+ next - sc->tx_upper_limit - 2;
+ }
+ }
+
+ /*
+ * Build the packet frame in the card's ring buffer.
+ */
+ DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next););
+
+ outw(iobase + HOST_ADDR_REG, dest);
+ outw(iobase + IO_PORT_REG, Transmit_CMD);
+ outw(iobase + IO_PORT_REG, 0);
+ outw(iobase + IO_PORT_REG, next);
+ outw(iobase + IO_PORT_REG, data_len);
+
+ /*
+ * Output the packet data to the card. Ensure all
+ * transfers are 16-bit wide, even if individual
+ * mbufs have odd length.
+ */
+
+ for (m = opkt, i = 0; m != NULL; m = m->m_next) {
+ DODEBUG(Sent_Pkts, printf("[%d]", m->m_len););
+ if (i) {
+ tmp16[1] = *(mtod(m, caddr_t));
+ outsw(iobase + IO_PORT_REG, tmp16, 1);
+ }
+ outsw(iobase + IO_PORT_REG,
+ mtod(m, caddr_t) + i, (m->m_len - i) / 2);
+
+ if ((i = (m->m_len - i) & 1) != 0) {
+ tmp16[0] = *(mtod(m, caddr_t) +
+ m->m_len - 1);
+ }
+ }
+ if (i) {
+ outsw(iobase + IO_PORT_REG, tmp16, 1);
+ }
+
+ /*
+ * If there were other frames chained, update the
+ * chain in the last one.
+ */
+ if (sc->tx_head != sc->tx_tail) {
+ if (sc->tx_tail != dest) {
+ outw(iobase + HOST_ADDR_REG,
+ sc->tx_last + XMT_Chain_Point);
+ outw(iobase + IO_PORT_REG, dest);
+ }
+ outw(iobase + HOST_ADDR_REG,
+ sc->tx_last + XMT_Byte_Count);
+ i = inw(iobase + IO_PORT_REG);
+ outw(iobase + HOST_ADDR_REG,
+ sc->tx_last + XMT_Byte_Count);
+ outw(iobase + IO_PORT_REG, i | Ch_bit);
+ }
+
+ /*
+ * Resume normal operation of the card:
+ * - Make a dummy read to flush the DRAM write
+ * pipeline.
+ * - Enable receive and transmit interrupts.
+ * - Send Transmit or Resume_XMT command, as
+ * appropriate.
+ */
+ inw(iobase + IO_PORT_REG);
+#ifdef EX_PSA_INTR
+ outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int));
+#endif
+ if (sc->tx_head == sc->tx_tail) {
+ outw(iobase + XMT_BAR, dest);
+ outb(iobase + CMD_REG, Transmit_CMD);
+ sc->tx_head = dest;
+ DODEBUG(Sent_Pkts, printf("Transmit\n"););
+ } else {
+ outb(iobase + CMD_REG, Resume_XMT_List_CMD);
+ DODEBUG(Sent_Pkts, printf("Resume\n"););
+ }
+
+ sc->tx_last = dest;
+ sc->tx_tail = next;
+
+ BPF_MTAP(ifp, opkt);
+
+ ifp->if_timer = 2;
+ ifp->if_opackets++;
+ m_freem(opkt);
+ } else {
+ ifp->if_flags |= IFF_OACTIVE;
+ DODEBUG(Status, printf("OACTIVE start\n"););
+ }
+ }
+
+ splx(s);
+
+ DODEBUG(Start_End, printf("ex_start%d: finish\n", unit););
+}
+
+void
+ex_stop(struct ex_softc *sc)
+{
+ int iobase = sc->iobase;
+
+ DODEBUG(Start_End, printf("ex_stop%d: start\n", unit););
+
+ /*
+ * Disable card operation:
+ * - Disable the interrupt line.
+ * - Flush transmission and disable reception.
+ * - Mask and clear all interrupts.
+ * - Reset the 82595.
+ */
+ outb(iobase + CMD_REG, Bank1_Sel);
+ outb(iobase + REG1, inb(iobase + REG1) & ~TriST_INT);
+ outb(iobase + CMD_REG, Bank0_Sel);
+ outb(iobase + CMD_REG, Rcv_Stop);
+ sc->tx_head = sc->tx_tail = sc->tx_lower_limit;
+ sc->tx_last = 0; /* XXX I think these two lines are not necessary, because ex_init will always be called again to reinit the interface. */
+ outb(iobase + MASK_REG, All_Int);
+ outb(iobase + STATUS_REG, All_Int);
+ outb(iobase + CMD_REG, Reset_CMD);
+ DELAY(200);
+
+ DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit););
+
+ return;
+}
+
+void
+ex_intr(void *arg)
+{
+ struct ex_softc * sc = (struct ex_softc *)arg;
+ struct ifnet * ifp = &sc->arpcom.ac_if;
+ int iobase = sc->iobase;
+ int int_status, send_pkts;
+
+ DODEBUG(Start_End, printf("ex_intr%d: start\n", unit););
+
+#ifdef EXDEBUG
+ if (++exintr_count != 1)
+ printf("WARNING: nested interrupt (%d). Mail the author.\n", exintr_count);
+#endif
+
+ send_pkts = 0;
+ while ((int_status = inb(iobase + STATUS_REG)) & (Tx_Int | Rx_Int)) {
+ if (int_status & Rx_Int) {
+ outb(iobase + STATUS_REG, Rx_Int);
+
+ ex_rx_intr(sc);
+ } else if (int_status & Tx_Int) {
+ outb(iobase + STATUS_REG, Tx_Int);
+
+ ex_tx_intr(sc);
+ send_pkts = 1;
+ }
+ }
+
+ /*
+ * If any packet has been transmitted, and there are queued packets to
+ * be sent, attempt to send more packets to the network card.
+ */
+
+ if (send_pkts && (ifp->if_snd.ifq_head != NULL)) {
+ ex_start(ifp);
+ }
+
+#ifdef EXDEBUG
+ exintr_count--;
+#endif
+
+ DODEBUG(Start_End, printf("ex_intr%d: finish\n", unit););
+
+ return;
+}
+
+static void
+ex_tx_intr(struct ex_softc *sc)
+{
+ struct ifnet * ifp = &sc->arpcom.ac_if;
+ int iobase = sc->iobase;
+ int tx_status;
+
+ DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit););
+
+ /*
+ * - Cancel the watchdog.
+ * For all packets transmitted since last transmit interrupt:
+ * - Advance chain pointer to next queued packet.
+ * - Update statistics.
+ */
+
+ ifp->if_timer = 0;
+
+ while (sc->tx_head != sc->tx_tail) {
+ outw(iobase + HOST_ADDR_REG, sc->tx_head);
+
+ if (! inw(iobase + IO_PORT_REG) & Done_bit)
+ break;
+
+ tx_status = inw(iobase + IO_PORT_REG);
+ sc->tx_head = inw(iobase + IO_PORT_REG);
+
+ if (tx_status & TX_OK_bit) {
+ ifp->if_opackets++;
+ } else {
+ ifp->if_oerrors++;
+ }
+
+ ifp->if_collisions += tx_status & No_Collisions_bits;
+ }
+
+ /*
+ * The card should be ready to accept more packets now.
+ */
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ DODEBUG(Status, printf("OIDLE tx_intr\n"););
+ DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit););
+
+ return;
+}
+
+static void
+ex_rx_intr(struct ex_softc *sc)
+{
+ struct ifnet * ifp = &sc->arpcom.ac_if;
+ int iobase = sc->iobase;
+ int rx_status;
+ int pkt_len;
+ int QQQ;
+ struct mbuf * m;
+ struct mbuf * ipkt;
+ struct ether_header * eh;
+
+ DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit););
+
+ /*
+ * For all packets received since last receive interrupt:
+ * - If packet ok, read it into a new mbuf and queue it to interface,
+ * updating statistics.
+ * - If packet bad, just discard it, and update statistics.
+ * Finally, advance receive stop limit in card's memory to new location.
+ */
+
+ outw(iobase + HOST_ADDR_REG, sc->rx_head);
+
+ while (inw(iobase + IO_PORT_REG) == RCV_Done) {
+
+ rx_status = inw(iobase + IO_PORT_REG);
+ sc->rx_head = inw(iobase + IO_PORT_REG);
+ QQQ = pkt_len = inw(iobase + IO_PORT_REG);
+
+ if (rx_status & RCV_OK_bit) {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ ipkt = m;
+ if (ipkt == NULL) {
+ ifp->if_iqdrops++;
+ } else {
+ ipkt->m_pkthdr.rcvif = ifp;
+ ipkt->m_pkthdr.len = pkt_len;
+ ipkt->m_len = MHLEN;
+
+ while (pkt_len > 0) {
+ if (pkt_len > MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (m->m_flags & M_EXT) {
+ m->m_len = MCLBYTES;
+ } else {
+ m_freem(ipkt);
+ ifp->if_iqdrops++;
+ goto rx_another;
+ }
+ }
+ m->m_len = min(m->m_len, pkt_len);
+
+ /*
+ * NOTE: I'm assuming that all mbufs allocated are of even length,
+ * except for the last one in an odd-length packet.
+ */
+
+ insw(iobase + IO_PORT_REG,
+ mtod(m, caddr_t), m->m_len / 2);
+
+ if (m->m_len & 1) {
+ *(mtod(m, caddr_t) + m->m_len - 1) = inb(iobase + IO_PORT_REG);
+ }
+ pkt_len -= m->m_len;
+
+ if (pkt_len > 0) {
+ MGET(m->m_next, M_DONTWAIT, MT_DATA);
+ if (m->m_next == NULL) {
+ m_freem(ipkt);
+ ifp->if_iqdrops++;
+ goto rx_another;
+ }
+ m = m->m_next;
+ m->m_len = MLEN;
+ }
+ }
+ eh = mtod(ipkt, struct ether_header *);
+#ifdef EXDEBUG
+ if (debug_mask & Rcvd_Pkts) {
+ if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) {
+ printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":");
+ printf("%6D\n", eh->ether_dhost, ":");
+ } /* QQQ */
+ }
+#endif
+ (*ifp->if_input)(ifp, ipkt);
+ ifp->if_ipackets++;
+ }
+ } else {
+ ifp->if_ierrors++;
+ }
+ outw(iobase + HOST_ADDR_REG, sc->rx_head);
+rx_another: ;
+ }
+
+ if (sc->rx_head < sc->rx_lower_limit + 2)
+ outw(iobase + RCV_STOP_REG, sc->rx_upper_limit);
+ else
+ outw(iobase + RCV_STOP_REG, sc->rx_head - 2);
+
+ DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit););
+
+ return;
+}
+
+
+static int
+ex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct ex_softc * sc = ifp->if_softc;
+ struct ifreq * ifr = (struct ifreq *)data;
+ int s;
+ int error = 0;
+
+ DODEBUG(Start_End, printf("ex_ioctl%d: start ", ifp->if_unit););
+
+ s = splimp();
+
+ switch(cmd) {
+ case SIOCSIFADDR:
+ case SIOCGIFADDR:
+ case SIOCSIFMTU:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ DODEBUG(Start_End, printf("SIOCSIFFLAGS"););
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ (ifp->if_flags & IFF_RUNNING)) {
+
+ ifp->if_flags &= ~IFF_RUNNING;
+ ex_stop(sc);
+ } else {
+ ex_init(sc);
+ }
+ break;
+#ifdef NODEF
+ case SIOCGHWADDR:
+ DODEBUG(Start_End, printf("SIOCGHWADDR"););
+ bcopy((caddr_t)sc->sc_addr, (caddr_t)&ifr->ifr_data,
+ sizeof(sc->sc_addr));
+ break;
+#endif
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ex_init(sc);
+ error = 0;
+ break;
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd);
+ break;
+ default:
+ DODEBUG(Start_End, printf("unknown"););
+ error = EINVAL;
+ }
+
+ splx(s);
+
+ DODEBUG(Start_End, printf("\nex_ioctl%d: finish\n", ifp->if_unit););
+
+ return(error);
+}
+
+static void
+ex_setmulti(struct ex_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *maddr;
+ u_int16_t *addr;
+ int iobase = sc->iobase;
+ int count;
+ int timeout, status;
+
+ ifp = &sc->arpcom.ac_if;
+
+ count = 0;
+ TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) {
+ if (maddr->ifma_addr->sa_family != AF_LINK)
+ continue;
+ count++;
+ }
+
+ if ((ifp->if_flags & IFF_PROMISC) || (ifp->if_flags & IFF_ALLMULTI)
+ || count > 63) {
+ /* Interface is in promiscuous mode or there are too many
+ * multicast addresses for the card to handle */
+ outb(iobase + CMD_REG, Bank2_Sel);
+ outb(iobase + REG2, inb(iobase + REG2) | Promisc_Mode);
+ outb(iobase + REG3, inb(iobase + REG3));
+ outb(iobase + CMD_REG, Bank0_Sel);
+ }
+ else if ((ifp->if_flags & IFF_MULTICAST) && (count > 0)) {
+ /* Program multicast addresses plus our MAC address
+ * into the filter */
+ outb(iobase + CMD_REG, Bank2_Sel);
+ outb(iobase + REG2, inb(iobase + REG2) | Multi_IA);
+ outb(iobase + REG3, inb(iobase + REG3));
+ outb(iobase + CMD_REG, Bank0_Sel);
+
+ /* Borrow space from TX buffer; this should be safe
+ * as this is only called from ex_init */
+
+ outw(iobase + HOST_ADDR_REG, sc->tx_lower_limit);
+ outw(iobase + IO_PORT_REG, MC_Setup_CMD);
+ outw(iobase + IO_PORT_REG, 0);
+ outw(iobase + IO_PORT_REG, 0);
+ outw(iobase + IO_PORT_REG, (count + 1) * 6);
+
+ TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) {
+ if (maddr->ifma_addr->sa_family != AF_LINK)
+ continue;
+
+ addr = (u_int16_t*)LLADDR((struct sockaddr_dl *)
+ maddr->ifma_addr);
+ outw(iobase + IO_PORT_REG, *addr++);
+ outw(iobase + IO_PORT_REG, *addr++);
+ outw(iobase + IO_PORT_REG, *addr++);
+ }
+
+ /* Program our MAC address as well */
+ /* XXX: Is this necessary? The Linux driver does this
+ * but the NetBSD driver does not */
+ addr = (u_int16_t*)(&sc->arpcom.ac_enaddr);
+ outw(iobase + IO_PORT_REG, *addr++);
+ outw(iobase + IO_PORT_REG, *addr++);
+ outw(iobase + IO_PORT_REG, *addr++);
+
+ inw(iobase + IO_PORT_REG);
+ outw(iobase + XMT_BAR, sc->tx_lower_limit);
+ outb(iobase + CMD_REG, MC_Setup_CMD);
+
+ sc->tx_head = sc->tx_lower_limit;
+ sc->tx_tail = sc->tx_head + XMT_HEADER_LEN + (count + 1) * 6;
+
+ for (timeout=0; timeout<100; timeout++) {
+ DELAY(2);
+ if ((inb(iobase + STATUS_REG) & Exec_Int) == 0)
+ continue;
+
+ status = inb(iobase + CMD_REG);
+ outb(iobase + STATUS_REG, Exec_Int);
+ break;
+ }
+
+ sc->tx_head = sc->tx_tail;
+ }
+ else
+ {
+ /* No multicast or promiscuous mode */
+ outb(iobase + CMD_REG, Bank2_Sel);
+ outb(iobase + REG2, inb(iobase + REG2) & 0xDE);
+ /* ~(Multi_IA | Promisc_Mode) */
+ outb(iobase + REG3, inb(iobase + REG3));
+ outb(iobase + CMD_REG, Bank0_Sel);
+ }
+}
+
+static void
+ex_reset(struct ex_softc *sc)
+{
+ int s;
+
+ DODEBUG(Start_End, printf("ex_reset%d: start\n", unit););
+
+ s = splimp();
+
+ ex_stop(sc);
+ ex_init(sc);
+
+ splx(s);
+
+ DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit););
+
+ return;
+}
+
+static void
+ex_watchdog(struct ifnet *ifp)
+{
+ struct ex_softc * sc = ifp->if_softc;
+
+ DODEBUG(Start_End, printf("ex_watchdog%d: start\n", ifp->if_unit););
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ DODEBUG(Status, printf("OIDLE watchdog\n"););
+
+ ifp->if_oerrors++;
+ ex_reset(sc);
+ ex_start(ifp);
+
+ DODEBUG(Start_End, printf("ex_watchdog%d: finish\n", ifp->if_unit););
+
+ return;
+}
+
+static int
+ex_get_media (u_int32_t iobase)
+{
+ int current;
+ int media;
+
+ media = eeprom_read(iobase, EE_W5);
+
+ outb(iobase + CMD_REG, Bank2_Sel);
+ current = inb(iobase + REG3);
+ outb(iobase + CMD_REG, Bank0_Sel);
+
+ if ((current & TPE_bit) && (media & EE_W5_PORT_TPE))
+ return(IFM_ETHER|IFM_10_T);
+ if ((current & BNC_bit) && (media & EE_W5_PORT_BNC))
+ return(IFM_ETHER|IFM_10_2);
+
+ if (media & EE_W5_PORT_AUI)
+ return (IFM_ETHER|IFM_10_5);
+
+ return (IFM_ETHER|IFM_AUTO);
+}
+
+static int
+ex_ifmedia_upd (ifp)
+ struct ifnet * ifp;
+{
+ struct ex_softc * sc = ifp->if_softc;
+
+ if (IFM_TYPE(sc->ifmedia.ifm_media) != IFM_ETHER)
+ return EINVAL;
+
+ return (0);
+}
+
+static void
+ex_ifmedia_sts(ifp, ifmr)
+ struct ifnet * ifp;
+ struct ifmediareq * ifmr;
+{
+ struct ex_softc * sc = ifp->if_softc;
+
+ ifmr->ifm_active = ex_get_media(sc->iobase);
+ ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
+
+ return;
+}
+
+u_short
+eeprom_read(u_int32_t iobase, int location)
+{
+ int i;
+ u_short data = 0;
+ int ee_addr;
+ int read_cmd = location | EE_READ_CMD;
+ short ctrl_val = EECS;
+
+ ee_addr = iobase + EEPROM_REG;
+ outb(iobase + CMD_REG, Bank2_Sel);
+ outb(ee_addr, EECS);
+ for (i = 8; i >= 0; i--) {
+ short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val;
+ outb(ee_addr, outval);
+ outb(ee_addr, outval | EESK);
+ DELAY(3);
+ outb(ee_addr, outval);
+ DELAY(2);
+ }
+ outb(ee_addr, ctrl_val);
+
+ for (i = 16; i > 0; i--) {
+ outb(ee_addr, ctrl_val | EESK);
+ DELAY(3);
+ data = (data << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0);
+ outb(ee_addr, ctrl_val);
+ DELAY(2);
+ }
+
+ ctrl_val &= ~EECS;
+ outb(ee_addr, ctrl_val | EESK);
+ DELAY(3);
+ outb(ee_addr, ctrl_val);
+ DELAY(2);
+ outb(iobase + CMD_REG, Bank0_Sel);
+ return(data);
+}
diff --git a/sys/dev/ex/if_ex_isa.c b/sys/dev/ex/if_ex_isa.c
new file mode 100644
index 0000000..b5d9681
--- /dev/null
+++ b/sys/dev/ex/if_ex_isa.c
@@ -0,0 +1,311 @@
+/*-
+ * Copyright (c) 2000 Matthew N. Dodd
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+
+#include <isa/isavar.h>
+#include <isa/pnpvar.h>
+
+#include <dev/ex/if_exreg.h>
+#include <dev/ex/if_exvar.h>
+
+/* Bus Front End Functions */
+static void ex_isa_identify (driver_t *, device_t);
+static int ex_isa_probe (device_t);
+static int ex_isa_attach (device_t);
+
+#if 0
+static void ex_pnp_wakeup (void *);
+
+SYSINIT(ex_pnpwakeup, SI_SUB_CPU, SI_ORDER_ANY, ex_pnp_wakeup, NULL);
+#endif
+
+static device_method_t ex_isa_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, ex_isa_identify),
+ DEVMETHOD(device_probe, ex_isa_probe),
+ DEVMETHOD(device_attach, ex_isa_attach),
+ DEVMETHOD(device_detach, ex_detach),
+
+ { 0, 0 }
+};
+
+static driver_t ex_isa_driver = {
+ "ex",
+ ex_isa_methods,
+ sizeof(struct ex_softc),
+};
+
+DRIVER_MODULE(ex, isa, ex_isa_driver, ex_devclass, 0, 0);
+
+static struct isa_pnp_id ex_ids[] = {
+ { 0x3110d425, NULL }, /* INT1031 */
+ { 0x3010d425, NULL }, /* INT1030 */
+ { 0, NULL },
+};
+
+#if 0
+#define EX_PNP_WAKE 0x279
+
+static u_int8_t ex_pnp_wake_seq[] =
+ { 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE,
+ 0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61,
+ 0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1,
+ 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x43 };
+
+static void
+ex_pnp_wakeup (void * dummy)
+{
+ int tmp;
+
+ if (bootverbose)
+ printf("ex_pnp_wakeup()\n");
+
+ outb(EX_PNP_WAKE, 0);
+ outb(EX_PNP_WAKE, 0);
+ for (tmp = 0; tmp < 32; tmp++) {
+ outb(EX_PNP_WAKE, ex_pnp_wake_seq[tmp]);
+ }
+}
+#endif
+
+/*
+ * Non-destructive identify.
+ */
+static void
+ex_isa_identify (driver_t *driver, device_t parent)
+{
+ device_t child;
+ u_int32_t ioport;
+ u_char enaddr[6];
+ u_int irq;
+ int tmp;
+ const char * desc;
+
+ if (bootverbose)
+ printf("ex_isa_identify()\n");
+
+ for (ioport = 0x200; ioport < 0x3a0; ioport += 0x10) {
+
+ /* No board found at address */
+ if (!look_for_card(ioport)) {
+ continue;
+ }
+
+ if (bootverbose)
+ printf("ex: Found card at 0x%03x!\n", ioport);
+
+ /* Board in PnP mode */
+ if (eeprom_read(ioport, EE_W0) & EE_W0_PNP) {
+ /* Reset the card. */
+ outb(ioport + CMD_REG, Reset_CMD);
+ DELAY(500);
+ if (bootverbose)
+ printf("ex: card at 0x%03x in PnP mode!\n", ioport);
+ continue;
+ }
+
+ bzero(enaddr, sizeof(enaddr));
+
+ /* Reset the card. */
+ outb(ioport + CMD_REG, Reset_CMD);
+ DELAY(400);
+
+ ex_get_address(ioport, enaddr);
+ tmp = eeprom_read(ioport, EE_W1) & EE_W1_INT_SEL;
+
+ /* work out which set of irq <-> internal tables to use */
+ if (ex_card_type(enaddr) == CARD_TYPE_EX_10_PLUS) {
+ irq = plus_ee2irqmap[tmp];
+ desc = "Intel Pro/10+";
+ } else {
+ irq = ee2irqmap[tmp];
+ desc = "Intel Pro/10";
+ }
+
+ child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ex", -1);
+ device_set_desc_copy(child, desc);
+ device_set_driver(child, driver);
+ bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
+ bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, EX_IOSIZE);
+
+ if (bootverbose)
+ printf("ex: Adding board at 0x%03x, irq %d\n", ioport, irq);
+ }
+
+ return;
+}
+
+static int
+ex_isa_probe(device_t dev)
+{
+ u_int iobase;
+ u_int irq;
+ char * irq2ee;
+ u_char * ee2irq;
+ u_char enaddr[6];
+ int tmp;
+ int error;
+
+ /* Check isapnp ids */
+ error = ISA_PNP_PROBE(device_get_parent(dev), dev, ex_ids);
+
+ /* If the card had a PnP ID that didn't match any we know about */
+ if (error == ENXIO) {
+ return(error);
+ }
+
+ /* If we had some other problem. */
+ if (!(error == 0 || error == ENOENT)) {
+ return(error);
+ }
+
+ iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0);
+ if (!iobase) {
+ printf("ex: no iobase?\n");
+ return(ENXIO);
+ }
+
+ if (!look_for_card(iobase)) {
+ printf("ex: no card found at 0x%03x\n", iobase);
+ return(ENXIO);
+ }
+
+ if (bootverbose)
+ printf("ex: ex_isa_probe() found card at 0x%03x\n", iobase);
+
+ /*
+ * Reset the card.
+ */
+ outb(iobase + CMD_REG, Reset_CMD);
+ DELAY(800);
+
+ ex_get_address(iobase, enaddr);
+
+ /* work out which set of irq <-> internal tables to use */
+ if (ex_card_type(enaddr) == CARD_TYPE_EX_10_PLUS) {
+ irq2ee = plus_irq2eemap;
+ ee2irq = plus_ee2irqmap;
+ } else {
+ irq2ee = irq2eemap;
+ ee2irq = ee2irqmap;
+ }
+
+ tmp = eeprom_read(iobase, EE_W1) & EE_W1_INT_SEL;
+ irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0);
+
+ if (irq > 0) {
+ /* This will happen if board is in PnP mode. */
+ if (ee2irq[tmp] != irq) {
+ printf("ex: WARNING: board's EEPROM is configured"
+ " for IRQ %d, using %d\n",
+ ee2irq[tmp], irq);
+ }
+ } else {
+ irq = ee2irq[tmp];
+ bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1);
+ }
+
+ if (irq == 0) {
+ printf("ex: invalid IRQ.\n");
+ return(ENXIO);
+ }
+
+ return(0);
+}
+
+static int
+ex_isa_attach(device_t dev)
+{
+ struct ex_softc * sc = device_get_softc(dev);
+ int error = 0;
+ u_int16_t temp;
+
+ sc->dev = dev;
+ sc->ioport_rid = 0;
+ sc->irq_rid = 0;
+
+ if ((error = ex_alloc_resources(dev)) != 0) {
+ device_printf(dev, "ex_alloc_resources() failed!\n");
+ goto bad;
+ }
+
+ /*
+ * Fill in several fields of the softc structure:
+ * - I/O base address.
+ * - Hardware Ethernet address.
+ * - IRQ number (if not supplied in config file, read it from EEPROM).
+ * - Connector type.
+ */
+ sc->iobase = rman_get_start(sc->ioport);
+ sc->irq_no = rman_get_start(sc->irq);
+
+ ex_get_address(sc->iobase, sc->arpcom.ac_enaddr);
+
+ temp = eeprom_read(sc->iobase, EE_W0);
+ device_printf(sc->dev, "%s config, %s bus, ",
+ (temp & EE_W0_PNP) ? "PnP" : "Manual",
+ (temp & EE_W0_BUS16) ? "16-bit" : "8-bit");
+
+ temp = eeprom_read(sc->iobase, EE_W6);
+ printf("board id 0x%03x, stepping 0x%01x\n",
+ (temp & EE_W6_BOARD_MASK) >> EE_W6_BOARD_SHIFT,
+ temp & EE_W6_STEP_MASK);
+
+ if ((error = ex_attach(dev)) != 0) {
+ device_printf(dev, "ex_attach() failed!\n");
+ goto bad;
+ }
+
+ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET,
+ ex_intr, (void *)sc, &sc->ih);
+ if (error) {
+ device_printf(dev, "bus_setup_intr() failed!\n");
+ goto bad;
+ }
+
+ return(0);
+bad:
+ ex_release_resources(dev);
+ return (error);
+}
diff --git a/sys/dev/ex/if_ex_pccard.c b/sys/dev/ex/if_ex_pccard.c
new file mode 100644
index 0000000..ca876e6
--- /dev/null
+++ b/sys/dev/ex/if_ex_pccard.c
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 2000 Mitsuru IWASAKI
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+
+#include <dev/ex/if_exreg.h>
+#include <dev/ex/if_exvar.h>
+
+#include <dev/pccard/pccardvar.h>
+
+/* Bus Front End Functions */
+static int ex_pccard_probe (device_t);
+static int ex_pccard_attach (device_t);
+
+static device_method_t ex_pccard_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ex_pccard_probe),
+ DEVMETHOD(device_attach, ex_pccard_attach),
+ DEVMETHOD(device_detach, ex_detach),
+
+ { 0, 0 }
+};
+
+static driver_t ex_pccard_driver = {
+ "ex",
+ ex_pccard_methods,
+ sizeof(struct ex_softc),
+};
+
+DRIVER_MODULE(ex, pccard, ex_pccard_driver, ex_devclass, 0, 0);
+
+static int
+ex_pccard_probe(device_t dev)
+{
+ u_int iobase;
+ u_int irq;
+
+ iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0);
+ if (!iobase) {
+ printf("ex: no iobase?\n");
+ return(ENXIO);
+ }
+
+ if (bootverbose)
+ printf("ex: ex_pccard_probe() found card at 0x%03x\n", iobase);
+
+ irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0);
+
+ if (irq == 0) {
+ printf("ex: invalid IRQ.\n");
+ return(ENXIO);
+ }
+
+ return(0);
+}
+
+static int
+ex_pccard_attach(device_t dev)
+{
+ struct ex_softc * sc = device_get_softc(dev);
+ int error = 0;
+ int i;
+ u_char sum;
+ u_char ether_addr[ETHER_ADDR_LEN];
+
+ sc->dev = dev;
+ sc->ioport_rid = 0;
+ sc->irq_rid = 0;
+
+ if ((error = ex_alloc_resources(dev)) != 0) {
+ device_printf(dev, "ex_alloc_resources() failed!\n");
+ goto bad;
+ }
+
+ /*
+ * Fill in several fields of the softc structure:
+ * - I/O base address.
+ * - Hardware Ethernet address.
+ * - IRQ number.
+ */
+ sc->iobase = rman_get_start(sc->ioport);
+ sc->irq_no = rman_get_start(sc->irq);
+
+ pccard_get_ether(dev, ether_addr);
+ for (i = 0, sum = 0; i < ETHER_ADDR_LEN; i++)
+ sum |= ether_addr[i];
+ if (sum)
+ bcopy(ether_addr, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+ if ((error = ex_attach(dev)) != 0) {
+ device_printf(dev, "ex_attach() failed!\n");
+ goto bad;
+ }
+
+ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET,
+ ex_intr, (void *)sc, &sc->ih);
+ if (error) {
+ device_printf(dev, "bus_setup_intr() failed!\n");
+ goto bad;
+ }
+
+ return(0);
+bad:
+ ex_release_resources(dev);
+ return (error);
+}
diff --git a/sys/dev/ex/if_exreg.h b/sys/dev/ex/if_exreg.h
new file mode 100644
index 0000000..a924c28
--- /dev/null
+++ b/sys/dev/ex/if_exreg.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 1996, Javier Martín Rueda (jmrueda@diatel.upm.es)
+ * 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 unmodified, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Intel EtherExpress Pro/10 Ethernet driver
+ */
+
+/*
+ * Several constants.
+ */
+
+#define CARD_TYPE_EX_10 1
+#define CARD_TYPE_EX_10_PLUS 2
+
+/* Length of an ethernet address. */
+#define ETHER_ADDR_LEN 6
+/* Default RAM size in board. */
+#define CARD_RAM_SIZE 0x8000
+/* Number of I/O ports used. */
+#define EX_IOSIZE 16
+
+/*
+ * Intel EtherExpress Pro (i82595 based) registers
+ */
+
+/* Common registers to all banks. */
+
+#define CMD_REG 0
+#define REG1 1
+#define REG2 2
+#define REG3 3
+#define REG4 4
+#define REG5 5
+#define REG6 6
+#define REG7 7
+#define REG8 8
+#define REG9 9
+#define REG10 10
+#define REG11 11
+#define REG12 12
+#define REG13 13
+#define REG14 14
+#define REG15 15
+
+/* Definitions for command register (CMD_REG). */
+
+#define Switch_Bank_CMD 0
+#define MC_Setup_CMD 3
+#define Transmit_CMD 4
+#define Diagnose_CMD 7
+#define Rcv_Enable_CMD 8
+#define Rcv_Stop 11
+#define Reset_CMD 14
+#define Resume_XMT_List_CMD 28
+#define Sel_Reset_CMD 30
+#define Abort 0x20
+#define Bank0_Sel 0x00
+#define Bank1_Sel 0x40
+#define Bank2_Sel 0x80
+
+/* Bank 0 specific registers. */
+
+#define STATUS_REG 1
+#define ID_REG 2
+#define Id_Mask 0x2c
+#define Id_Sig 0x24
+#define Counter_bits 0xc0
+#define MASK_REG 3
+#define Exec_Int 0x08
+#define Tx_Int 0x04
+#define Rx_Int 0x02
+#define Rx_Stp_Int 0x01
+#define All_Int 0x0f
+#define RCV_BAR 4
+#define RCV_BAR_Lo 4
+#define RCV_BAR_Hi 5
+#define RCV_STOP_REG 6
+#define XMT_BAR 10
+#define HOST_ADDR_REG 12 /* 16-bit register */
+#define IO_PORT_REG 14 /* 16-bit register */
+
+/* Bank 1 specific registers. */
+
+#define TriST_INT 0x80
+#define INT_NO_REG 2
+#define RCV_LOWER_LIMIT_REG 8
+#define RCV_UPPER_LIMIT_REG 9
+#define XMT_LOWER_LIMIT_REG 10
+#define XMT_UPPER_LIMIT_REG 11
+
+/* Bank 2 specific registers. */
+
+#define Disc_Bad_Fr 0x80
+#define Tx_Chn_ErStp 0x40
+#define Tx_Chn_Int_Md 0x20
+#define Multi_IA 0x20
+#define No_SA_Ins 0x10
+#define RX_CRC_InMem 0x04
+#define Promisc_Mode 0x01
+#define BNC_bit 0x20
+#define TPE_bit 0x04
+#define I_ADDR_REG0 4
+#define EEPROM_REG 10
+#define Trnoff_Enable 0x10
+
+/* EEPROM memory positions (16-bit wide). */
+
+#define EE_W0 0x00
+# define EE_W0_PNP 0x0001
+# define EE_W0_BUS16 0x0004
+# define EE_W0_FLASH_ADDR_MASK 0x0038
+# define EE_W0_FLASH_ADDR_SHIFT 3
+# define EE_W0_AUTO_IO 0x0040
+# define EE_W0_FLASH 0x0100
+# define EE_W0_AUTO_NEG 0x0200
+# define EE_W0_IO_MASK 0xFC00
+# define EE_W0_IO_SHIFT 10
+
+#define EE_IRQ_No 1
+#define IRQ_No_Mask 0x07
+
+#define EE_W1 0x01
+# define EE_W1_INT_SEL 0x0007
+# define EE_W1_NO_LINK_INT 0x0008 /* Link Integrity Off */
+# define EE_W1_NO_POLARITY 0x0010 /* Polarity Correction Off */
+# define EE_W1_TPE_AUI 0x0020 /* 1 = TPE, 0 = AUI */
+# define EE_W1_NO_JABBER_PREV 0x0040 /* Jabber prevention Off */
+# define EE_W1_NO_AUTO_SELECT 0x0080 /* Auto Port Selection Off */
+# define EE_W1_SMOUT 0x0100 /* SMout Pin Control 0= Input */
+# define EE_W1_PROM 0x0200 /* Flash = 0, PROM = 1 */
+# define EE_W1_ALT_READY 0x2000 /* Alternate Ready, 0=normal */
+# define EE_W1_FULL_DUPLEX 0x8000
+
+#define EE_W2 0x02
+#define EE_W3 0x03
+#define EE_W4 0x04
+
+#define EE_Eth_Addr_Lo 2
+#define EE_Eth_Addr_Mid 3
+#define EE_Eth_Addr_Hi 4
+
+#define EE_W5 0x05
+# define EE_W5_BNC_TPE 0x0001 /* 0 = TPE, 1 = BNC */
+# define EE_W5_BOOT_IPX 0x0002
+# define EE_W5_BOOT_ODI 0x0004
+# define EE_W5_BOOT_NDIS (EE_W5_BOOT_IPX|EE_W5_BOOT_ODI)
+# define EE_W5_NUM_CONN 0x0008 /* 0 = 2, 1 = 3 */
+# define EE_W5_NOFLASH 0x0010 /* No flash socket present */
+# define EE_W5_PORT_TPE 0x0020 /* TPE present */
+# define EE_W5_PORT_BNC 0x0040 /* BNC present */
+# define EE_W5_PORT_AUI 0x0080 /* AUI present */
+# define EE_W5_PWR_MGT 0x0100 /* Power Management */
+# define EE_W5_CP 0x0200 /* COncurrent Processing */
+
+#define EE_W6 0x05
+# define EE_W6_STEP_MASK 0x000F
+# define EE_W6_BOARD_MASK 0xFFF0
+# define EE_W6_BOARD_SHIFT 4
+
+/* EEPROM serial interface. */
+
+#define EESK 0x01
+#define EECS 0x02
+#define EEDI 0x04
+#define EEDO 0x08
+#define EE_READ_CMD (6 << 6)
+
+/* Frame chain constants. */
+
+/* Transmit header length (in board's ring buffer). */
+#define XMT_HEADER_LEN 8
+#define XMT_Chain_Point 4
+#define XMT_Byte_Count 6
+#define Done_bit 0x0080
+#define Ch_bit 0x8000
+
+/* Transmit result bits. */
+#define No_Collisions_bits 0x000f
+#define TX_OK_bit 0x2000
+
+/* Receive result bits. */
+#define RCV_Done 8
+#define RCV_OK_bit 0x2000
diff --git a/sys/dev/ex/if_exvar.h b/sys/dev/ex/if_exvar.h
new file mode 100644
index 0000000..2f94c41
--- /dev/null
+++ b/sys/dev/ex/if_exvar.h
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 1996, Javier Mart^mn Rueda (jmrueda@diatel.upm.es)
+ * All rights reserved.
+ *
+ * Copyright (c) 2000 Matthew N. Dodd
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+struct ex_softc {
+ struct arpcom arpcom; /* Ethernet common data */
+ struct ifmedia ifmedia;
+
+ device_t dev;
+ struct resource *ioport;
+ int ioport_rid;
+ struct resource *irq;
+ int irq_rid;
+ void * ih;
+
+ u_int iobase; /* I/O base address. */
+ u_short irq_no; /* IRQ number. */
+
+ char * irq2ee; /* irq <-> internal */
+ u_char * ee2irq; /* representation conversion */
+
+ u_int mem_size; /* Total memory size, in bytes. */
+ u_int rx_mem_size; /* Rx memory size (by default, */
+ /* first 3/4 of total memory). */
+
+ u_int rx_lower_limit; /* Lower and upper limits of */
+ u_int rx_upper_limit; /* receive buffer. */
+
+ u_int rx_head; /* Head of receive ring buffer. */
+ u_int tx_mem_size; /* Tx memory size (by default, */
+ /* last quarter of total memory).*/
+
+ u_int tx_lower_limit; /* Lower and upper limits of */
+ u_int tx_upper_limit; /* transmit buffer. */
+
+ u_int tx_head; /* Head and tail of */
+ u_int tx_tail; /* transmit ring buffer. */
+
+ u_int tx_last; /* Pointer to beginning of last */
+ /* frame in the chain. */
+};
+
+extern devclass_t ex_devclass;
+
+extern char irq2eemap[];
+extern u_char ee2irqmap[];
+extern char plus_irq2eemap[];
+extern u_char plus_ee2irqmap[];
+
+int ex_alloc_resources (device_t);
+void ex_release_resources (device_t);
+int ex_attach (device_t);
+int ex_detach (device_t);
+
+driver_intr_t ex_intr;
+
+u_int16_t eeprom_read (u_int32_t, int);
+
+int look_for_card (u_int32_t);
+void ex_get_address (u_int32_t, u_char *);
+int ex_card_type (u_char *);
+
+void ex_stop (struct ex_softc *);
OpenPOWER on IntegriCloud