diff options
author | jkh <jkh@FreeBSD.org> | 1997-01-16 12:19:21 +0000 |
---|---|---|
committer | jkh <jkh@FreeBSD.org> | 1997-01-16 12:19:21 +0000 |
commit | c1b63f3e2739461cdcdf2788777afcc3a5f2fd3b (patch) | |
tree | 99064457f22d0f8d50bd368fb0aeec6df5817370 /sys/i386/isa/if_ex.c | |
parent | e5c0b85f37f6f70f19fac7f19ef9d01c7813389e (diff) | |
download | FreeBSD-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/i386/isa/if_ex.c')
-rw-r--r-- | sys/i386/isa/if_ex.c | 929 |
1 files changed, 929 insertions, 0 deletions
diff --git a/sys/i386/isa/if_ex.c b/sys/i386/isa/if_ex.c new file mode 100644 index 0000000..e5c988c --- /dev/null +++ b/sys/i386/isa/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 */ |