summaryrefslogtreecommitdiffstats
path: root/sys/dev/ex/if_ex.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ex/if_ex.c')
-rw-r--r--sys/dev/ex/if_ex.c948
1 files changed, 948 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..f7458af
--- /dev/null
+++ b/sys/dev/ex/if_ex.c
@@ -0,0 +1,948 @@
+/*
+ * 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:
+ *
+ * 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_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
+
+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_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 /* XXX not done yet. | 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_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);
+}
+
+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"););
+
+ /*
+ * 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:
+ DODEBUG(Start_End, printf("SIOCADDMULTI"););
+ case SIOCDELMULTI:
+ DODEBUG(Start_End, printf("SIOCDELMULTI"););
+ /* XXX Support not done yet. */
+ error = EINVAL;
+ 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_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 tmp;
+
+ outb(iobase + CMD_REG, Bank2_Sel);
+ tmp = inb(iobase + REG3);
+ outb(iobase + CMD_REG, Bank0_Sel);
+
+ if (tmp & TPE_bit)
+ return(IFM_ETHER|IFM_10_T);
+ if (tmp & BNC_bit)
+ return(IFM_ETHER|IFM_10_2);
+
+ return (IFM_ETHER|IFM_10_5);
+}
+
+static int
+ex_ifmedia_upd (ifp)
+ struct ifnet * ifp;
+{
+
+ 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);
+
+ 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);
+}
OpenPOWER on IntegriCloud