summaryrefslogtreecommitdiffstats
path: root/sys/dev/ex
diff options
context:
space:
mode:
authorjkh <jkh@FreeBSD.org>1997-01-16 12:19:21 +0000
committerjkh <jkh@FreeBSD.org>1997-01-16 12:19:21 +0000
commitc1b63f3e2739461cdcdf2788777afcc3a5f2fd3b (patch)
tree99064457f22d0f8d50bd368fb0aeec6df5817370 /sys/dev/ex
parente5c0b85f37f6f70f19fac7f19ef9d01c7813389e (diff)
downloadFreeBSD-src-c1b63f3e2739461cdcdf2788777afcc3a5f2fd3b.zip
FreeBSD-src-c1b63f3e2739461cdcdf2788777afcc3a5f2fd3b.tar.gz
Add the ex driver (Intel EtherExpress Pro/10).
I have no idea if this works since I don't have one of the cards to test. I also don't know what the LINT and GENERIC entries should look like, so I just made up some values for now and left them commented out. Someone who knows the factory settings for a Pro/10, please contact me! Submitted-By: Javier Martín Rueda <jmrueda@diatel.upm.es>
Diffstat (limited to 'sys/dev/ex')
-rw-r--r--sys/dev/ex/if_ex.c929
-rw-r--r--sys/dev/ex/if_exreg.h154
2 files changed, 1083 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..e5c988c
--- /dev/null
+++ b/sys/dev/ex/if_ex.c
@@ -0,0 +1,929 @@
+/*
+ * 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/kernel.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.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 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>
+#include <net/bpfdesc.h>
+#endif
+
+#include <machine/clock.h>
+
+#include <i386/isa/isa_device.h>
+#include <i386/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 */
+ u_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. */
+};
+
+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 isa_device *));
+static int ex_attach __P((struct isa_device *));
+static void ex_init __P((void *));
+static void ex_start __P((struct ifnet *));
+static void ex_stop __P((int));
+static int ex_ioctl __P((struct ifnet *, int, caddr_t));
+static void ex_reset __P((int));
+static void ex_watchdog __P((struct ifnet *));
+
+static u_short eeprom_read __P((int, int));
+static int look_for_card __P((u_int));
+static void ex_tx_intr __P((int));
+static void ex_rx_intr __P((int));
+
+struct isa_driver exdriver = {
+ ex_probe,
+ ex_attach,
+ "ex",
+ 0
+};
+
+static int look_for_card(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(struct isa_device *dev)
+{
+ int unit = dev->id_unit;
+ struct ex_softc *sc = &ex_sc[unit];
+ u_int iobase;
+ u_short eaddr_tmp;
+ int tmp;
+
+ DODEBUG(Start_End, printf("ex_probe%d: start\n", unit););
+
+ /*
+ * If an I/O address was supplied in the configuration file, probe only
+ * that. Otherwise, cycle through the predefined set of possible addresses.
+ */
+ if (dev->id_iobase != -1) {
+ if (! look_for_card(iobase = dev->id_iobase))
+ return(0);
+ }
+ else {
+ for (iobase = 0x200; iobase < 0x3a0; iobase += 0x10)
+ if (look_for_card(iobase))
+ break;
+ if (iobase >= 0x3a0)
+ return(0);
+ else
+ dev->id_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 (dev->id_irq > 0) {
+ if (ee2irqmap[tmp] != ffs(dev->id_irq) - 1)
+ printf("ex%d: WARNING: board's EEPROM is configured for IRQ %d, using %d\n", unit, ee2irqmap[tmp], ffs(dev->id_irq) - 1);
+ sc->irq_no = ffs(dev->id_irq) - 1;
+ }
+ else {
+ sc->irq_no = ee2irqmap[tmp];
+ dev->id_irq = 1 << sc->irq_no;
+ }
+ if (sc->irq_no == 0) {
+ printf("ex%d: invalid IRQ.\n", unit);
+ 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%d: finish\n", unit););
+ return(EX_IOSIZE);
+}
+
+
+int ex_attach(struct isa_device *dev)
+{
+ int unit = dev->id_unit;
+ struct ex_softc *sc = &ex_sc[unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+
+ DODEBUG(Start_End, printf("ex_attach%d: start\n", unit););
+
+ /*
+ * Initialize the ifnet structure.
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = unit;
+ ifp->if_name = "ex";
+ ifp->if_init = ex_init;
+ 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("ex%d: Intel EtherExpress Pro/10, address %6D, connector ", dev->id_unit, 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, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ DODEBUG(Start_End, printf("ex_attach%d: finish\n", unit););
+ return(1);
+}
+
+
+void ex_init(void *xsc)
+{
+ register struct ex_softc *sc = (struct ex_softc *) xsc;
+ 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%d: start\n", ifp->if_unit););
+
+ if (ifp->if_addrhead.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%d: finish\n", ifp->if_unit););
+}
+
+
+void ex_start(struct ifnet *ifp)
+{
+ int unit = ifp->if_unit;
+ register struct ex_softc *sc = &ex_sc[unit];
+ register int iobase = sc->iobase;
+ int i, s, len, data_len, avail, dest, next;
+ unsigned char tmp16[2], *cP;
+ struct mbuf *opkt;
+ register 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)
+ 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, 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%d: finish\n", unit););
+}
+
+
+void ex_stop(int unit)
+{
+ struct ex_softc *sc = &ex_sc[unit];
+ 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););
+}
+
+
+void exintr(int unit)
+{
+ struct ex_softc *sc = &ex_sc[unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int iobase = sc->iobase;
+ int s, int_status, send_pkts;
+
+ DODEBUG(Start_End, printf("exintr%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(unit);
+ }
+ else if (int_status & Tx_Int) {
+ outb(iobase + STATUS_REG, Tx_Int);
+ ex_tx_intr(unit);
+ 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("exintr%d: finish\n", unit););
+}
+
+
+void ex_tx_intr(int unit)
+{
+ register struct ex_softc *sc = &ex_sc[unit];
+ register struct ifnet *ifp = &sc->arpcom.ac_if;
+ register 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););
+}
+
+
+void ex_rx_intr(int unit)
+{
+ register struct ex_softc *sc = &ex_sc[unit];
+ 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%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
+#if NBPFILTER > 0
+ if (ifp->if_bpf != NULL) {
+ bpf_mtap(ifp, 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%d: finish\n", unit););
+}
+
+
+int ex_ioctl(register struct ifnet *ifp, int cmd, caddr_t data)
+{
+ register struct ifaddr *ifa = (struct ifaddr *) data;
+ struct ex_softc *sc = &ex_sc[ifp->if_unit];
+ struct ifreq *ifr = (struct ifreq *) data;
+ int s, error = 0;
+
+ DODEBUG(Start_End, printf("ex_ioctl%d: start ", ifp->if_unit););
+
+ 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(ifp->if_unit);
+ }
+ 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 SIOCSIFMTU:
+ DODEBUG(Start_End, printf("SIOCSIFMTU"););
+ if (ifr->ifr_mtu > ETHERMTU)
+ error = EINVAL;
+ else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+ 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%d: finish\n", ifp->if_unit););
+ return(error);
+}
+
+
+void ex_reset(int unit)
+{
+ struct ex_softc *sc = &ex_sc[unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int s;
+
+ DODEBUG(Start_End, printf("ex_reset%d: start\n", unit););
+
+ s = splimp();
+
+ ex_stop(unit);
+ ex_init(sc);
+
+ splx(s);
+
+ DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit););
+}
+
+
+void ex_watchdog(struct ifnet *ifp)
+{
+ struct ex_softc *sc = &ex_sc[ifp->if_unit];
+
+ 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(ifp->if_unit);
+ ex_start(ifp);
+
+ DODEBUG(Start_End, printf("ex_watchdog%d: finish\n", ifp->if_unit););
+}
+
+
+static u_short eeprom_read(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/ex/if_exreg.h b/sys/dev/ex/if_exreg.h
new file mode 100644
index 0000000..63b843f
--- /dev/null
+++ b/sys/dev/ex/if_exreg.h
@@ -0,0 +1,154 @@
+/*
+ * 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
OpenPOWER on IntegriCloud