summaryrefslogtreecommitdiff
path: root/sys/dev/isa
diff options
context:
space:
mode:
authorgene <gene@cvs.openbsd.org>1997-09-11 21:30:50 +0000
committergene <gene@cvs.openbsd.org>1997-09-11 21:30:50 +0000
commit5c997d224d33aa6fe96d1b467191e3147f2f9f0f (patch)
treec735b55e19ccd67e2321c1995b3eab0efb765816 /sys/dev/isa
parent5c06d5db6383a49fa955b990dfa3ba026146b89b (diff)
Driver for ISA EtherExpress Pro/10 ethernet card.
This is ported from the FreeBSD driver, and all porting work (and testing) was done by Don Schmidt (yensid@imsa.edu).
Diffstat (limited to 'sys/dev/isa')
-rw-r--r--sys/dev/isa/files.isa9
-rw-r--r--sys/dev/isa/if_ex.c998
-rw-r--r--sys/dev/isa/if_exreg.h155
3 files changed, 1160 insertions, 2 deletions
diff --git a/sys/dev/isa/files.isa b/sys/dev/isa/files.isa
index dcf73b35c43..9a80ce6adb6 100644
--- a/sys/dev/isa/files.isa
+++ b/sys/dev/isa/files.isa
@@ -1,4 +1,4 @@
-# $OpenBSD: files.isa,v 1.33 1997/07/10 23:06:33 provos Exp $
+# $OpenBSD: files.isa,v 1.34 1997/09/11 21:30:48 gene Exp $
# $NetBSD: files.isa,v 1.21 1996/05/16 03:45:55 mycroft Exp $
#
# Config.new file and device description for machine-independent ISA code.
@@ -188,11 +188,16 @@ attach ie at isa
file dev/isa/if_ie.c ie
# Intel i82595-based boards.
-# (Intel EtherExpress PRO)
+# (Intel EtherExpress PRO/100)
device iy: ether, ifnet
attach iy at isa
file dev/isa/if_iy.c iy
+# Intel EtherExpress PRO/10
+device ex: ether, ifnet
+attach ex at isa
+file dev/isa/if_ex.c ex needs-flag
+
# XXX ???
# XXX NOT IN TREE?
#device ix: ether, ifnet
diff --git a/sys/dev/isa/if_ex.c b/sys/dev/isa/if_ex.c
new file mode 100644
index 00000000000..29296f33d6d
--- /dev/null
+++ b/sys/dev/isa/if_ex.c
@@ -0,0 +1,998 @@
+/* $OpenBSD: if_ex.c,v 1.1 1997/09/11 21:30:49 gene Exp $ */
+/*
+ * 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.
+ */
+
+/*
+ * Intel EtherExpress Pro/10 Ethernet driver
+ *
+ * Revision history:
+ *
+ * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast.
+ */
+
+#include "ex.h"
+#if NEX > 0
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/device.h>
+
+#include <net/if.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef IPX
+#include <netipx/ipx.h>
+#include <netipx/ipx_if.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#include <machine/cpu.h>
+#include <machine/pio.h> /* XXX convert */
+
+#include <dev/isa/isavar.h>
+#include <dev/isa/if_exreg.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
+
+
+#define Conn_BNC 1
+#define Conn_TPE 2
+#define Conn_AUI 3
+
+struct ex_softc {
+ struct arpcom arpcom; /* Ethernet common data */
+ int iobase; /* I/O base address. */
+ u_short connector; /* Connector type. */
+ u_short irq_no; /* IRQ number. */
+ 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,
+ rx_upper_limit; /* Lower and upper limits of 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,
+ tx_upper_limit; /* Lower and upper limits of transmit buffer. */
+ u_int tx_head, tx_tail; /* Head and tail of transmit ring buffer. */
+ u_int tx_last; /* Pointer to beginning of last frame in the
+ chain. */
+ void *sc_ih; /* Device interrupt handler */
+};
+
+/* static struct ex_softc ex_sc[NEX]; XXX would it be better to malloc(3)
+ the memory? */
+
+static char irq2eemap[] = { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1,
+ -1, -1 };
+static u_char ee2irqmap[] = { 9, 3, 5, 10, 11, 0, 0, 0 };
+
+static int ex_probe __P((struct device *, void *, void *));
+static void ex_attach __P((struct device *, struct device *, void *));
+static void ex_init __P((struct ex_softc *));
+static void ex_start __P((struct ifnet *));
+static void ex_stop __P((struct ex_softc *));
+static int ex_ioctl __P((struct ifnet *, u_long, caddr_t));
+static void ex_reset __P((struct ex_softc *));
+static void ex_watchdog __P((struct ifnet *));
+
+static u_short eeprom_read __P((int, int));
+static int look_for_card __P((u_int));
+static int exintr __P((void *));
+static void ex_tx_intr __P((struct ex_softc *));
+static void ex_rx_intr __P((struct ex_softc *));
+
+
+struct cfattach ex_ca = {
+ sizeof(struct ex_softc), ex_probe, ex_attach
+};
+
+struct cfdriver ex_cd = {
+ NULL, "ex", DV_IFNET
+};
+
+static int
+look_for_card(iobase)
+ u_int 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));
+}
+
+
+int
+ex_probe(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct ex_softc *sc = match;
+ struct isa_attach_args *ia = aux;
+
+ int iobase;
+ u_short eaddr_tmp;
+ int tmp;
+
+ DODEBUG(Start_End, printf("ex_probe: start\n"););
+
+ iobase = ia->ia_iobase;
+
+ if ((iobase >= 0x200) && (iobase <= 0x3a0)) {
+ if (look_for_card(iobase) == 0)
+ return(0);
+ }
+ else if (iobase == IOBASEUNK) {
+ tmp = 0;
+ for (iobase = 0x200; iobase <= 0x3a0; iobase += EX_IOSIZE) {
+ if (look_for_card(iobase) == 1) {
+ tmp = 1;
+ break;
+ }
+ }
+ if (tmp != 1)
+ return(0);
+ } else
+ return(0);
+
+ ia->ia_iosize = EX_IOSIZE;
+ ia->ia_iobase = iobase;
+
+ /*
+ * Reset the card.
+ */
+ outb(iobase + CMD_REG, Reset_CMD);
+ delay(200);
+
+ /*
+ * 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 = iobase;
+ eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Lo);
+ sc->arpcom.ac_enaddr[5] = eaddr_tmp & 0xff;
+ sc->arpcom.ac_enaddr[4] = eaddr_tmp >> 8;
+ eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Mid);
+ sc->arpcom.ac_enaddr[3] = eaddr_tmp & 0xff;
+ sc->arpcom.ac_enaddr[2] = eaddr_tmp >> 8;
+ eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Hi);
+ sc->arpcom.ac_enaddr[1] = eaddr_tmp & 0xff;
+ sc->arpcom.ac_enaddr[0] = eaddr_tmp >> 8;
+ tmp = eeprom_read(iobase, EE_IRQ_No) & IRQ_No_Mask;
+ if (ia->ia_irq > 0) {
+ if (ee2irqmap[tmp] != ia->ia_irq)
+ printf("ex: WARING: board's EEPROM is configured for IRQ %d, using %d\n", ee2irqmap[tmp], ia->ia_irq);
+ sc->irq_no = ia->ia_irq;
+ }
+ else {
+ sc->irq_no = ee2irqmap[tmp];
+ ia->ia_irq = sc->irq_no;
+ }
+ if (sc->irq_no == 0) {
+ printf("ex: invalid IRQ.\n");
+ return(0);
+ }
+ outb(iobase + CMD_REG, Bank2_Sel);
+ tmp = inb(iobase + REG3);
+ if (tmp & TPE_bit)
+ sc->connector = Conn_TPE;
+ else if (tmp & BNC_bit)
+ sc->connector = Conn_BNC;
+ else
+ sc->connector = Conn_AUI;
+ sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card
+ itself. */
+
+ outb(iobase + CMD_REG, Bank0_Sel);
+
+ DODEBUG(Start_End, printf("ex_probe: finish\n"););
+ return(1);
+}
+
+
+void
+ex_attach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct ex_softc *sc = (void *)self;
+ struct isa_attach_args *ia = aux;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ /* struct ifaddr *ifa; XXX what are these for? */
+ /* struct sockaddr_dl *sdl; */
+
+ DODEBUG(Start_End, printf("ex_attach: start\n"););
+
+ /*
+ * Initialize the ifnet structure.
+
+ */
+ ifp->if_softc = sc;
+ bcopy(self->dv_xname, ifp->if_xname, IFNAMSIZ);
+ ifp->if_output = ether_output;
+ ifp->if_start = ex_start;
+ ifp->if_ioctl = ex_ioctl;
+ ifp->if_watchdog = ex_watchdog;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST; /* XXX not done yet.
+ | IFF_MULTICAST */
+
+ /*
+ * Attach the interface.
+ */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+ printf(": address %s, connecter ",
+ ether_sprintf(sc->arpcom.ac_enaddr));
+ switch(sc->connector) {
+ case Conn_TPE: printf("TPE\n"); break;
+ case Conn_BNC: printf("BNC\n"); break;
+ case Conn_AUI: printf("AUI\n"); break;
+ default: printf("???\n");
+ }
+
+ /*
+ * If BPF is in the kernel, call the attach for it
+ */
+#if NBPFILTER > 0
+ bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB,
+ sizeof(struct ether_header));
+#endif
+
+ sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE,
+ IPL_NET, exintr, sc, self->dv_xname);
+ ex_init(sc);
+
+ DODEBUG(Start_End, printf("ex_attach: finish\n"););
+}
+
+
+void
+ex_init(sc)
+ struct ex_softc *sc;
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int s, i;
+ register int iobase = sc->iobase;
+ unsigned short temp_reg;
+
+ DODEBUG(Start_End, printf("ex_init: start\n"););
+
+ if (ifp->if_addrlist.tqh_first == NULL)
+ return;
+ s = splimp();
+ sc->arpcom.ac_if.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) |
+ irq2eemap[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"););
+
+ /*
+ * 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: finish\n"););
+}
+
+
+void
+ex_start(ifp)
+ struct ifnet *ifp;
+{
+ register struct ex_softc *sc = ifp->if_softc;
+ register int iobase = sc->iobase;
+ int i, s, len, data_len, avail, dest, next;
+ unsigned char tmp16[2];
+ struct mbuf *opkt;
+ register struct mbuf *m;
+
+ DODEBUG(Start_End, printf("ex_start: start\n"););
+
+ 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))
+ 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;
+#if NBPFILTER > 0
+ if (ifp->if_bpf != NULL)
+ bpf_mtap(ifp->if_bpf, opkt);
+#endif
+ 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: finish\n"););
+}
+
+
+void
+ex_stop(sc)
+ struct ex_softc *sc;
+{
+ int iobase = sc->iobase;
+
+ DODEBUG(Start_End, printf("ex_stop: start\n"););
+
+ /*
+ * 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: finish\n"););
+}
+
+
+int
+exintr(arg)
+ void *arg;
+{
+ struct ex_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int iobase = sc->iobase;
+ int int_status, send_pkts;
+ int handled;
+
+ DODEBUG(Start_End, printf("exintr: start\n"););
+
+#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);
+ handled = 1;
+ ex_rx_intr(sc);
+ } else if (int_status & Tx_Int) {
+ outb(iobase + STATUS_REG, Tx_Int);
+ handled = 1;
+ ex_tx_intr(sc);
+ send_pkts = 1;
+ } else
+ handled = 0;
+ }
+
+ /*
+ * 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("exintr: finish\n"););
+
+ return handled;
+}
+
+
+void
+ex_tx_intr(sc)
+ struct ex_softc *sc;
+{
+ register struct ifnet *ifp = &sc->arpcom.ac_if;
+ register int iobase = sc->iobase;
+ int tx_status;
+
+ DODEBUG(Start_End, printf("ex_tx_intr: start\n"););
+ /*
+ * - 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: finish\n"););
+}
+
+
+void
+ex_rx_intr(sc)
+ struct ex_softc *sc;
+{
+ register struct ifnet *ifp = &sc->arpcom.ac_if;
+ register int iobase = sc->iobase;
+ int rx_status, pkt_len, QQQ;
+ register struct mbuf *m, *ipkt;
+ struct ether_header *eh;
+
+ DODEBUG(Start_End, printf("ex_rx_intr: start\n"););
+ /*
+ * 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
+#if NBPFILTER > 0
+ if (ifp->if_bpf != NULL) {
+ bpf_mtap(ifp->if_bpf, ipkt);
+ /*
+ * Note that the interface cannot be in
+ * promiscuous mode if there are no BPF
+ * listeners. And if we are in promiscuous
+ * mode, we have to check if this packet is
+ * really ours.
+ */
+ if ((ifp->if_flags & IFF_PROMISC) &&
+ (eh->ether_dhost[0] & 1) == 0 &&
+ bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0 &&
+ bcmp(eh->ether_dhost, etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0) {
+ m_freem(ipkt);
+ goto rx_another;
+ }
+ }
+#endif
+ m_adj(ipkt, sizeof(struct ether_header));
+ ether_input(ifp, eh, 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: finish\n"););
+}
+
+
+int
+ex_ioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ u_long cmd;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *) data;
+ struct ex_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int s, error = 0;
+
+ DODEBUG(Start_End, printf("ex_ioctl: start "););
+
+ s = splimp();
+
+ switch(cmd) {
+ case SIOCSIFADDR:
+ DODEBUG(Start_End, printf("SIOCSIFADDR"););
+ ifp->if_flags |= IFF_UP;
+
+ switch(ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ ex_init(sc);
+ arp_ifinit((struct arpcom *) ifp, ifa);
+ break;
+#endif
+#ifdef IPX_NOTYET
+ case AF_IPX:
+ {
+ register struct ipx_addr *ina =
+ &(IA_SIPX(ifa)->sipx_addr);
+
+ if (ipx_nullhost(*ina))
+ ina->x_host = *(union ipx_host *)
+ (sc->arpcom.ac_enaddr);
+ else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t)
+ ina->x_host.c_host,
+ (caddr_t)
+ sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr)
+ );
+ }
+ ex_init(sc);
+ break;
+ }
+#endif
+#ifdef NS
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host = *(union ns_host *) (sc->arpcom.ac_enaddr);
+ else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t) ina->x_host.c_host, (caddr_t) sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+ ex_init(sc);
+ break;
+ }
+#endif
+ default:
+ ex_init(sc);
+ break;
+ }
+ break;
+ case SIOCGIFADDR:
+ {
+ struct sockaddr *sa;
+
+ DODEBUG(Start_End, printf("SIOCGIFADDR"););
+ sa = (struct sockaddr *) &ifr->ifr_data;
+ bcopy((caddr_t) sc->arpcom.ac_enaddr, (caddr_t) sa->sa_data,
+ ETHER_ADDR_LEN);
+ }
+ 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
+#if 0 /* XXX can we do this? */
+ case SIOCSIFMTU:
+ DODEBUG(Start_End, printf("SIOCSIFMTU"););
+ if (ifr->if_mtu > ETHERMTU)
+ error = EINVAL;
+ else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+#endif
+ case SIOCADDMULTI:
+ DODEBUG(Start_End, printf("SIOCADDMULTI"););
+ case SIOCDELMULTI:
+ DODEBUG(Start_End, printf("SIOCDELMULTI"););
+ /* XXX Support not done yet. */
+ error = EINVAL;
+ break;
+ default:
+ DODEBUG(Start_End, printf("unknown"););
+ error = EINVAL;
+ }
+
+ splx(s);
+
+ DODEBUG(Start_End, printf("\nex_ioctl: finish\n"););
+ return(error);
+}
+
+
+void
+ex_reset(sc)
+ struct ex_softc *sc;
+{
+ int s;
+
+ DODEBUG(Start_End, printf("ex_reset: start\n"););
+
+ s = splimp();
+ ex_stop(sc);
+ ex_init(sc);
+ splx(s);
+
+ DODEBUG(Start_End, printf("ex_reset: finish\n"););
+}
+
+
+void
+ex_watchdog(ifp)
+ struct ifnet *ifp;
+{
+ struct ex_softc *sc = ifp->if_softc;
+
+ DODEBUG(Start_End, printf("ex_watchdog: start\n"););
+
+ 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: finish\n"););
+}
+
+
+static u_short
+eeprom_read(iobase, location)
+ int 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);
+}
+
+#endif /* NEX > 0 */
diff --git a/sys/dev/isa/if_exreg.h b/sys/dev/isa/if_exreg.h
new file mode 100644
index 00000000000..0e7c9a4065a
--- /dev/null
+++ b/sys/dev/isa/if_exreg.h
@@ -0,0 +1,155 @@
+/* $OpenBSD: if_exreg.h,v 1.1 1997/09/11 21:30:49 gene Exp $ */
+/*
+ * 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.
+ */
+
+/*
+ * Intel EtherExpress Pro/10 Ethernet driver
+ */
+
+/*
+ * Several constants.
+ */
+
+/* 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 No_SA_Ins 0x10
+#define RX_CRC_InMem 0x04
+#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_IRQ_No 1
+#define IRQ_No_Mask 0x07
+#define EE_Eth_Addr_Lo 2
+#define EE_Eth_Addr_Mid 3
+#define EE_Eth_Addr_Hi 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