diff options
author | gene <gene@cvs.openbsd.org> | 1997-09-11 21:30:50 +0000 |
---|---|---|
committer | gene <gene@cvs.openbsd.org> | 1997-09-11 21:30:50 +0000 |
commit | 5c997d224d33aa6fe96d1b467191e3147f2f9f0f (patch) | |
tree | c735b55e19ccd67e2321c1995b3eab0efb765816 /sys/dev/isa | |
parent | 5c06d5db6383a49fa955b990dfa3ba026146b89b (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.isa | 9 | ||||
-rw-r--r-- | sys/dev/isa/if_ex.c | 998 | ||||
-rw-r--r-- | sys/dev/isa/if_exreg.h | 155 |
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 |