diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1996-04-24 11:09:01 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1996-04-24 11:09:01 +0000 |
commit | 6bbb90fd25d2dcd7a4acdc8969a442fd71b98a06 (patch) | |
tree | da1eb150bc647bb89c88e694d8714e5193738055 /sys/arch/arm32/podulebus/if_ea.c | |
parent | 00287be7e84692788588bfeb60508f1022602abb (diff) |
Initial import of arm32 port
Diffstat (limited to 'sys/arch/arm32/podulebus/if_ea.c')
-rw-r--r-- | sys/arch/arm32/podulebus/if_ea.c | 1557 |
1 files changed, 1557 insertions, 0 deletions
diff --git a/sys/arch/arm32/podulebus/if_ea.c b/sys/arch/arm32/podulebus/if_ea.c new file mode 100644 index 00000000000..1104d512f7f --- /dev/null +++ b/sys/arch/arm32/podulebus/if_ea.c @@ -0,0 +1,1557 @@ +/* $NetBSD: if_ea.c,v 1.4 1996/03/27 21:49:26 mark Exp $ */ + +/* + * Copyright (c) 1995 Mark Brinicombe + * 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, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Mark Brinicombe. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. + * + * RiscBSD kernel project + * + * if_ea.c + * + * Ether3 device driver + * + * Created : 08/07/95 + */ + +/* + * SEEQ 8005 device driver + */ + +/* + * Bugs/possible improvements: + * - Does not currently support DMA + * - Does not currently support multicasts + * - Does not transmit multiple packets in one go + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/syslog.h> +#include <sys/device.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 NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <machine/cpu.h> +#include <machine/katelib.h> +#include <machine/io.h> +#include <machine/irqhandler.h> + +#include <arm32/podulebus/if_eareg.h> +#include <arm32/podulebus/podulebus.h> + +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1514 +#define ETHER_ADDR_LEN 6 + +#ifndef EA_TIMEOUT +#define EA_TIMEOUT 60 +#endif + +/*#define EA_TX_DEBUG*/ +/*#define EA_RX_DEBUG*/ +/*#define EA_DEBUG*/ +/*#define EA_PACKET_DEBUG*/ + +/* for debugging convenience */ +#ifdef EA_DEBUG +#define dprintf(x) printf x +#else +#define dprintf(x) +#endif + +#define MY_MANUFACTURER 0x11 +#define MY_PODULE 0xa4 + +/* + * per-line info and status + */ + +struct ea_softc { + struct device sc_dev; + irqhandler_t sc_ih; + int sc_irq; /* IRQ number */ + podule_t *sc_podule; /* Our podule */ + int sc_podule_number; /* Our podule number */ + u_int sc_iobase; /* base I/O addr */ + struct arpcom sc_arpcom; /* ethernet common */ + char sc_pktbuf[EA_BUFSIZ]; /* frame buffer */ + int sc_config1; /* Current config1 bits */ + int sc_config2; /* Current config2 bits */ + int sc_command; /* Current command bits */ + int sc_irqclaimed; /* Whether we have an IRQ claimed */ + int sc_rx_ptr; /* Receive buffer pointer */ + int sc_tx_ptr; /* Transmit buffer pointer */ +}; + +/* + * prototypes + */ + +static int eaintr __P((void *)); +static int ea_init __P((struct ea_softc *)); +static int ea_ioctl __P((struct ifnet *, u_long, caddr_t)); +static void ea_start __P((struct ifnet *)); +static void ea_watchdog __P((int)); +static void ea_reinit __P((struct ea_softc *)); +static void ea_chipreset __P((struct ea_softc *)); +static void ea_ramtest __P((struct ea_softc *)); +static int ea_stoptx __P((struct ea_softc *)); +static int ea_stoprx __P((struct ea_softc *)); +static void ea_stop __P((struct ea_softc *)); +static void ea_writebuf __P((struct ea_softc *, u_char *, int, int)); +static void ea_readbuf __P((struct ea_softc *, u_char *, int, int)); +static void earead __P((struct ea_softc *, caddr_t, int)); +static struct mbuf *eaget __P((caddr_t, int, struct ifnet *)); +static void ea_hardreset __P((struct ea_softc *)); +static void eagetpackets __P((struct ea_softc *)); +static void eatxpacket __P((struct ea_softc *)); + +int eaprobe __P((struct device *, void *, void *)); +void eaattach __P((struct device *, struct device *, void *)); + +/* driver structure for autoconf */ + +struct cfattach ea_ca = { + sizeof(struct ea_softc), eaprobe, eaattach +}; + +struct cfdriver ea_cd = { + NULL, "ea", DV_IFNET +}; + +#if 0 + +/* + * Dump the chip registers + */ + +void +eadump(iobase) + u_int iobase; +{ + dprintf(("%08x: %04x %04x %04x %04x %04x %04x %04x %04x\n", iobase, + ReadShort(iobase + 0x00), ReadShort(iobase + 0x40), + ReadShort(iobase + 0x80), ReadShort(iobase + 0xc0), + ReadShort(iobase + 0x100), ReadShort(iobase + 0x140), + ReadShort(iobase + 0x180), ReadShort(iobase + 0x1c0))); +} +#endif + +/* + * Dump the interface buffer + */ + +void +ea_dump_buffer(sc, offset) + struct ea_softc *sc; + int offset; +{ +#ifdef EA_PACKET_DEBUG + u_int iobase = sc->sc_iobase; + int addr; + int loop; + int size; + int ctrl; + int ptr; + + addr = offset; + + do { + WriteShort(sc->sc_iobase + EA_8005_COMMAND, sc->sc_command | EA_CMD_FIFO_READ); + WriteShort(iobase + EA_8005_CONFIG1, sc->sc_config1 | EA_BUFCODE_LOCAL_MEM); + WriteShort(iobase + EA_8005_DMA_ADDR, addr); + + ptr = ReadShort(iobase + EA_8005_BUFWIN); + ctrl = ReadShort(iobase + EA_8005_BUFWIN); + ptr = ((ptr & 0xff) << 8) | ((ptr >> 8) & 0xff); + + if (ptr == 0) break; + size = ptr - addr; + + printf("addr=%04x size=%04x ", addr, size); + printf("cmd=%02x st=%02x\n", ctrl & 0xff, ctrl >> 8); + + for (loop = 0; loop < size - 4; loop += 2) + printf("%04x ", ReadShort(iobase + EA_8005_BUFWIN)); + printf("\n"); + addr = ptr; + } while (size != 0); +#endif +} + +/* + * Probe routine. + */ + +/* + * int eaprobe(struct device *parent, void *match, void *aux) + * + * Probe for the ether3 podule. + */ + +int +eaprobe(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct podule_attach_args *pa = (void *)aux; + int podule; + u_int iobase; + +/* dprintf(("Probing for SEEQ 8005... \n"));*/ + +/* Look for a network slot interface */ + + podule = findpodule(MY_MANUFACTURER, MY_PODULE, pa->pa_podule_number); + +/* Fail if we did not find it */ + + if (podule == -1) + return(0); + + iobase = podules[podule].mod_base + EA_8005_BASE; + +/* Reset it - Why here ? */ + + WriteShort(iobase + EA_8005_CONFIG2, EA_CFG2_RESET); + delay(100); + +/* We found it */ + + pa->pa_podule_number = podule; + pa->pa_podule = &podules[podule]; + + return(1); +} + + +/* + * void eaattach(struct device *parent, struct device *dev, void *aux) + * + * Attach podule. + */ + +void +eaattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct ea_softc *sc = (void *)self; + struct podule_attach_args *pa = (void *)aux; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + int loop; + int sum; + +/* dprintf(("Attaching %s...\n", sc->sc_dev.dv_xname));*/ + +/* Note the podule number and validate */ + + sc->sc_podule_number = pa->pa_podule_number; + if (sc->sc_podule_number == -1) + panic("Podule has disappeared !"); + + sc->sc_podule = &podules[sc->sc_podule_number]; + podules[sc->sc_podule_number].attached = 1; + +/* Set the address of the controller for easy access */ + + sc->sc_iobase = sc->sc_podule->mod_base + EA_8005_BASE; + + sc->sc_irqclaimed = 0; + +/* Set up the interrupt structure */ + + sc->sc_ih.ih_func = eaintr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_level = IPL_NET; + sc->sc_ih.ih_name = "net: ea"; + +/* Claim either a network slot interrupt or a podule interrupt */ + + if (sc->sc_podule_number >= MAX_PODULES) + sc->sc_irq = IRQ_NETSLOT; + else + sc->sc_irq = IRQ_PODULE /*+ sc->sc_podule_number*/; + + /* Stop the board. */ + + ea_chipreset(sc); + ea_stoptx(sc); + ea_stoprx(sc); + + /* Initialise ifnet structure. */ + + ifp->if_unit = sc->sc_dev.dv_unit; + ifp->if_name = ea_cd.cd_name; + ifp->if_start = ea_start; + ifp->if_ioctl = ea_ioctl; + ifp->if_watchdog = ea_watchdog; + ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS; + + /* Now we can attach the interface. */ + +/* dprintf(("Attaching interface...\n"));*/ + if_attach(ifp); + ether_ifattach(ifp); + +/* Read the station address - the receiver must be off */ + + WriteShort(sc->sc_iobase + EA_8005_CONFIG1, EA_BUFCODE_STATION_ADDR0); + + for (sum = 0, loop = 0; loop < ETHER_ADDR_LEN; ++loop) { + sc->sc_arpcom.ac_enaddr[loop] = + ReadByte(sc->sc_iobase + EA_8005_BUFWIN); + sum += sc->sc_arpcom.ac_enaddr[loop]; + } + +/* + * Hard code the ether address if we don't have one. + * Need to work out how I get the real address + */ + + if (sum == 0) { + sc->sc_arpcom.ac_enaddr[0] = 0x00; + sc->sc_arpcom.ac_enaddr[1] = 0x00; + sc->sc_arpcom.ac_enaddr[2] = 0xa4; + sc->sc_arpcom.ac_enaddr[3] = 0x10; + sc->sc_arpcom.ac_enaddr[4] = 0x02; + sc->sc_arpcom.ac_enaddr[5] = 0x87; + } + + /* Print out some information for the user. */ + + printf(" SEEQ8005 address %s", ether_sprintf(sc->sc_arpcom.ac_enaddr)); + + /* Finally, attach to bpf filter if it is present. */ + +#if NBPFILTER > 0 +/* dprintf(("Attaching to BPF...\n"));*/ + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + + /* Should test the RAM */ + + ea_ramtest(sc); + +/* dprintf(("eaattach() finished.\n"));*/ +} + + +/* + * Test the RAM on the ethernet card. This does not work yet + */ + +void +ea_ramtest(sc) + struct ea_softc *sc; +{ + register u_int iobase = sc->sc_iobase; + register int loop; + register u_int sum = 0; + +/* dprintf(("ea_ramtest()\n"));*/ + + /* + * Test the buffer memory on the board. + * Write simple pattens to it and read them back. + */ + + /* Set up the whole buffer RAM for writing */ + + WriteShort(iobase + EA_8005_CONFIG1, EA_BUFCODE_TX_EAP); + WriteShort(iobase + EA_8005_BUFWIN, ((EA_BUFFER_SIZE >> 8) - 1)); + WriteShort(iobase + EA_8005_TX_PTR, 0x0000); + WriteShort(iobase + EA_8005_RX_PTR, EA_BUFFER_SIZE - 2); + + /* Set the write start address and write a pattern */ + + ea_writebuf(sc, NULL, 0x0000, 0); + + for (loop = 0; loop < EA_BUFFER_SIZE; loop += 2) + WriteShort(iobase + EA_8005_BUFWIN, loop); + + /* Set the read start address and verify the pattern */ + + ea_readbuf(sc, NULL, 0x0000, 0); + + for (loop = 0; loop < EA_BUFFER_SIZE; loop += 2) + if (ReadShort(iobase + EA_8005_BUFWIN) != loop) + ++sum; + + if (sum != 0) + dprintf(("sum=%d\n", sum)); + + /* Set the write start address and write a pattern */ + + ea_writebuf(sc, NULL, 0x0000, 0); + + for (loop = 0; loop < EA_BUFFER_SIZE; loop += 2) + WriteShort(iobase + EA_8005_BUFWIN, loop ^ (EA_BUFFER_SIZE - 1)); + + /* Set the read start address and verify the pattern */ + + ea_readbuf(sc, NULL, 0x0000, 0); + + for (loop = 0; loop < EA_BUFFER_SIZE; loop += 2) + if (ReadShort(iobase + EA_8005_BUFWIN) != (loop ^ (EA_BUFFER_SIZE - 1))) + ++sum; + + if (sum != 0) + dprintf(("sum=%d\n", sum)); + + /* Set the write start address and write a pattern */ + + ea_writebuf(sc, NULL, 0x0000, 0); + + for (loop = 0; loop < EA_BUFFER_SIZE; loop += 2) + WriteShort(iobase + EA_8005_BUFWIN, 0xaa55); + + /* Set the read start address and verify the pattern */ + + ea_readbuf(sc, NULL, 0x0000, 0); + + for (loop = 0; loop < EA_BUFFER_SIZE; loop += 2) + if (ReadShort(iobase + EA_8005_BUFWIN) != 0xaa55) + ++sum; + + if (sum != 0) + dprintf(("sum=%d\n", sum)); + + /* Set the write start address and write a pattern */ + + ea_writebuf(sc, NULL, 0x0000, 0); + + for (loop = 0; loop < EA_BUFFER_SIZE; loop += 2) + WriteShort(iobase + EA_8005_BUFWIN, 0x55aa); + + /* Set the read start address and verify the pattern */ + + ea_readbuf(sc, NULL, 0x0000, 0); + + for (loop = 0; loop < EA_BUFFER_SIZE; loop += 2) + if (ReadShort(iobase + EA_8005_BUFWIN) != 0x55aa) + ++sum; + + if (sum != 0) + dprintf(("sum=%d\n", sum)); + + /* Report */ + + if (sum == 0) + printf(" %dK buffer RAM\n", EA_BUFFER_SIZE / 1024); + else + printf(" buffer RAM failed self test, %d faults\n", sum); +} + + +/* Claim an irq for the board */ + +void +ea_claimirq(sc) + struct ea_softc *sc; +{ +/* Have we claimed one already ? */ + + if (sc->sc_irqclaimed) return; + +/* Claim it */ + + dprintf(("ea_claimirq(%d)\n", sc->sc_irq)); + if (irq_claim(sc->sc_irq, &sc->sc_ih)) + panic("Cannot install IRQ handler for IRQ %d\n", sc->sc_irq); + + sc->sc_irqclaimed = 1; +} + + +/* Release an irq */ + +void +ea_releaseirq(sc) + struct ea_softc *sc; +{ +/* Have we claimed one ? */ + + if (!sc->sc_irqclaimed) return; + + dprintf(("ea_releaseirq(%d)\n", sc->sc_irq)); + if (irq_release(sc->sc_irq, &sc->sc_ih)) + panic("Cannot release IRQ handler for IRQ %d\n", sc->sc_irq); + + sc->sc_irqclaimed = 0; +} + + +/* + * Stop and reinitialise the interface. + */ + +static void +ea_reinit(sc) + struct ea_softc *sc; +{ + int s; + + dprintf(("eareinit()\n")); + +/* Stop and reinitialise the interface */ + + s = splimp(); + ea_stop(sc); + ea_init(sc); + (void)splx(s); +} + + +/* + * Stop the tx interface. + * + * Returns 0 if the tx was already stopped or 1 if it was active + */ + +static int +ea_stoptx(sc) + struct ea_softc *sc; +{ + u_int iobase = sc->sc_iobase; + int timeout; + int status; + + dprintf(("ea_stoptx()\n")); + + status = ReadShort(iobase + EA_8005_STATUS); + if (!(status & EA_STATUS_TX_ON)) + return(0); + +/* Stop any tx and wait for confirmation */ + + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command | EA_CMD_TX_OFF); + + timeout = 20000; + do { + status = ReadShort(iobase + EA_8005_STATUS); + } while ((status & EA_STATUS_TX_ON) && --timeout > 0); + if (timeout == 0) + dprintf(("ea_stoptx: timeout waiting for tx termination\n")); + +/* Clear any pending tx interrupt */ + + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command | EA_CMD_TX_INTACK); + return(1); +} + + +/* + * Stop the rx interface. + * + * Returns 0 if the tx was already stopped or 1 if it was active + */ + +static int +ea_stoprx(sc) + struct ea_softc *sc; +{ + u_int iobase = sc->sc_iobase; + int timeout; + int status; + + dprintf(("ea_stoprx()\n")); + + status = ReadShort(iobase + EA_8005_STATUS); + if (!(status & EA_STATUS_RX_ON)) + return(0); + +/* Stop any rx and wait for confirmation */ + + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command | EA_CMD_RX_OFF); + + timeout = 20000; + do { + status = ReadShort(iobase + EA_8005_STATUS); + } while ((status & EA_STATUS_RX_ON) && --timeout > 0); + if (timeout == 0) + dprintf(("ea_stoprx: timeout waiting for rx termination\n")); + +/* Clear any pending rx interrupt */ + + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command | EA_CMD_RX_INTACK); + return(1); +} + + +/* + * Stop interface. + * Stop all IO and shut the interface down + */ + +static void +ea_stop(sc) + struct ea_softc *sc; +{ + u_int iobase = sc->sc_iobase; + + dprintf(("ea_stop()\n")); + +/* Stop all IO */ + + ea_stoptx(sc); + ea_stoprx(sc); + +/* Disable rx and tx interrupts */ + + sc->sc_command &= (EA_CMD_RX_INTEN | EA_CMD_TX_INTEN); + +/* Clear any pending interrupts */ + + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command + | EA_CMD_RX_INTACK | EA_CMD_TX_INTACK | EA_CMD_DMA_INTACK + | EA_CMD_BW_INTACK); + dprintf(("st=%08x", ReadShort(iobase + EA_8005_STATUS))); + +/* Release the irq */ + + ea_releaseirq(sc); + + /* Cancel any watchdog timer */ + + sc->sc_arpcom.ac_if.if_timer = 0; +} + + +/* + * Reset the chip + * Following this the software registers are reset + */ + +static void +ea_chipreset(sc) + struct ea_softc *sc; +{ + u_int iobase = sc->sc_iobase; + + dprintf(("ea_chipreset()\n")); + +/* Reset the controller. Min of 4us delay here */ + + WriteShort(iobase + EA_8005_CONFIG2, EA_CFG2_RESET); + delay(100); + + sc->sc_command = 0; + sc->sc_config1 = 0; + sc->sc_config2 = 0; +} + + +/* + * Do a hardware reset of the board, and upload the ethernet address again in + * case the board forgets. + */ + +static void +ea_hardreset(sc) + struct ea_softc *sc; +{ + u_int iobase = sc->sc_iobase; + int loop; + + dprintf(("ea_hardreset()\n")); + +/* Stop any activity */ + + ea_stoptx(sc); + ea_stoprx(sc); + + ea_chipreset(sc); + +/* Set up defaults for the registers */ + + sc->sc_config2 = 0; + WriteShort(iobase + EA_8005_CONFIG2, sc->sc_config2); + sc->sc_command = 0x00; + sc->sc_config1 = EA_CFG1_STATION_ADDR0 | EA_CFG1_DMA_BSIZE_1 | EA_CFG1_DMA_BURST_CONT; + WriteShort(iobase + EA_8005_CONFIG1, sc->sc_config1); + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command); + + WriteShort(iobase + EA_8005_CONFIG1, EA_BUFCODE_TX_EAP); + WriteShort(iobase + EA_8005_BUFWIN, ((EA_TX_BUFFER_SIZE >> 8) - 1)); + +/* Write the station address - the receiver must be off */ + + WriteShort(sc->sc_iobase + EA_8005_CONFIG1, + sc->sc_config1 | EA_BUFCODE_STATION_ADDR0); + for (loop = 0; loop < ETHER_ADDR_LEN; ++loop) { + WriteByte(sc->sc_iobase + EA_8005_BUFWIN, sc->sc_arpcom.ac_enaddr[loop]); + } +} + + +/* + * write to the buffer memory on the interface + * + * If addr is within range for the interface buffer then the buffer + * address is set to addr. + * If len != 0 then data is copied from the address starting at buf + * to the interface buffer. + */ + +static void +ea_writebuf(sc, buf, addr, len) + struct ea_softc *sc; + u_char *buf; + int addr; + int len; +{ + u_int iobase = sc->sc_iobase; + int loop; + int timeout; + + dprintf(("writebuf: st=%04x\n", ReadShort(iobase + EA_8005_STATUS))); + +/* If we have a valid buffer address set the buffer pointer and direction */ + + if (addr >= 0 && addr < EA_BUFFER_SIZE) { + WriteShort(iobase + EA_8005_CONFIG1, sc->sc_config1 | EA_BUFCODE_LOCAL_MEM); + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command | EA_CMD_FIFO_WRITE); + + /* Should wait here of FIFO empty flag */ + + timeout = 20000; + while ((ReadShort(iobase + EA_8005_STATUS) & EA_STATUS_FIFO_EMPTY) == 0 && --timeout > 0); + + + WriteShort(iobase + EA_8005_DMA_ADDR, addr); + } + + for (loop = 0; loop < len; loop += 2) + WriteShort(iobase + EA_8005_BUFWIN, buf[loop] | buf[loop + 1] << 8); + +/* if (len > 0) + outsw(iobase + EA_8005_BUFWIN, buf, len / 2);*/ +} + + +/* + * read from the buffer memory on the interface + * + * If addr is within range for the interface buffer then the buffer + * address is set to addr. + * If len != 0 then data is copied from the interface buffer to the + * address starting at buf. + */ + +static void +ea_readbuf(sc, buf, addr, len) + struct ea_softc *sc; + u_char *buf; + int addr; + int len; +{ + u_int iobase = sc->sc_iobase; + int loop; + int word; + int timeout; + + dprintf(("readbuf: st=%04x addr=%04x len=%d\n", ReadShort(iobase + EA_8005_STATUS), addr, len)); + +/* If we have a valid buffer address set the buffer pointer and direction */ + + if (addr >= 0 && addr < EA_BUFFER_SIZE) { + if ((ReadShort(iobase + EA_8005_STATUS) & EA_STATUS_FIFO_DIR) == 0) { + /* Should wait here of FIFO empty flag */ + + timeout = 20000; + while ((ReadShort(iobase + EA_8005_STATUS) & EA_STATUS_FIFO_EMPTY) == 0 && --timeout > 0); + } + WriteShort(iobase + EA_8005_CONFIG1, sc->sc_config1 | EA_BUFCODE_LOCAL_MEM); + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command | EA_CMD_FIFO_WRITE); + + /* Should wait here of FIFO empty flag */ + + timeout = 20000; + while ((ReadShort(iobase + EA_8005_STATUS) & EA_STATUS_FIFO_EMPTY) == 0 && --timeout > 0); + + WriteShort(iobase + EA_8005_DMA_ADDR, addr); + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command | EA_CMD_FIFO_READ); + + /* Should wait here of FIFO full flag */ + + timeout = 20000; + while ((ReadShort(iobase + EA_8005_STATUS) & EA_STATUS_FIFO_FULL) == 0 && --timeout > 0); + + + } + + for (loop = 0; loop < len; loop += 2) { + word = ReadShort(iobase + EA_8005_BUFWIN); + buf[loop] = word & 0xff; + buf[loop + 1] = word >> 8; + } +} + + +/* + * Initialize interface. + * + * This should leave the interface in a state for packet reception and transmission + */ + +static int +ea_init(sc) + struct ea_softc *sc; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + u_int iobase = sc->sc_iobase; + int s; + + dprintf(("ea_init()\n")); + + s = splimp(); + +/* Grab an irq */ + + ea_claimirq(sc); + + /* First, reset the board. */ + + ea_hardreset(sc); + +/* Configure rx. */ + + dprintf(("Configuring rx...\n")); + if (ifp->if_flags & IFF_PROMISC) + sc->sc_config1 = EA_CFG1_PROMISCUOUS; + else + sc->sc_config1 = EA_CFG1_BROADCAST; + + sc->sc_config1 |= EA_CFG1_DMA_BSIZE_8 | EA_CFG1_STATION_ADDR0 | EA_CFG1_DMA_BURST_CONT; + WriteShort(iobase + EA_8005_CONFIG1, sc->sc_config1); + +/* Configure TX. */ + + dprintf(("Configuring tx...\n")); + + WriteShort(iobase + EA_8005_CONFIG1, sc->sc_config1 | EA_BUFCODE_TX_EAP); + WriteShort(iobase + EA_8005_BUFWIN, ((EA_TX_BUFFER_SIZE >> 8) - 1)); + WriteShort(iobase + EA_8005_TX_PTR, 0x0000); + + sc->sc_config2 |= EA_CFG2_OUTPUT; + WriteShort(iobase + EA_8005_CONFIG2, sc->sc_config2); + +/* Place a NULL header at the beginning of the transmit area */ + + ea_writebuf(sc, NULL, 0x0000, 0); + + WriteShort(iobase + EA_8005_BUFWIN, 0x0000); + WriteShort(iobase + EA_8005_BUFWIN, 0x0000); + + sc->sc_command |= EA_CMD_TX_INTEN; + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command); + +/* Setup the Rx pointers */ + + sc->sc_rx_ptr = EA_TX_BUFFER_SIZE; + + WriteShort(iobase + EA_8005_RX_PTR, sc->sc_rx_ptr); + WriteShort(iobase + EA_8005_RX_END, (sc->sc_rx_ptr >> 8)); + +/* Place a NULL header at the beginning of the receive area */ + + ea_writebuf(sc, NULL, sc->sc_rx_ptr, 0); + + WriteShort(iobase + EA_8005_BUFWIN, 0x0000); + WriteShort(iobase + EA_8005_BUFWIN, 0x0000); + +/* Turn on Rx */ + + sc->sc_command |= EA_CMD_RX_INTEN; + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command | EA_CMD_RX_ON); + + /* Set flags appropriately. */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + dprintf(("init: st=%04x\n", ReadShort(iobase + EA_8005_STATUS))); + + /* And start output. */ + ea_start(ifp); + + (void)splx(s); + return(0); +} + + +/* + * Start output on interface. Get datagrams from the queue and output them, + * giving the receiver a chance between datagrams. Call only from splimp or + * interrupt level! + */ + +static void +ea_start(ifp) + struct ifnet *ifp; +{ + struct ea_softc *sc = ea_cd.cd_devs[ifp->if_unit]; + int s; + + s = splimp(); +#ifdef EA_TX_DEBUG + dprintf(("ea_start()...\n")); +#endif + + /* Don't do anything if output is active. */ + + if (sc->sc_arpcom.ac_if.if_flags & IFF_OACTIVE) + return; + + /* Mark interface as output active */ + + sc->sc_arpcom.ac_if.if_flags |= IFF_OACTIVE; + + /* tx packets */ + + eatxpacket(sc); + (void)splx(s); +} + + +/* + * Transfer a packet to the interface buffer and start transmission + * + * Called at splimp() + */ + +void +eatxpacket(sc) + struct ea_softc *sc; +{ + u_int iobase = sc->sc_iobase; + struct mbuf *m, *m0; + int len; + +/* Dequeue the next datagram. */ + + IF_DEQUEUE(&sc->sc_arpcom.ac_if.if_snd, m0); + +/* If there's nothing to send, return. */ + + if (!m0) { + sc->sc_arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + sc->sc_config2 |= EA_CFG2_OUTPUT; + WriteShort(iobase + EA_8005_CONFIG2, sc->sc_config2); +#ifdef EA_TX_DEBUG + dprintf(("tx finished\n")); +#endif + return; + } + + /* Give the packet to the bpf, if any. */ +#if NBPFILTER > 0 + if (sc->sc_arpcom.ac_if.if_bpf) + bpf_mtap(sc->sc_arpcom.ac_if.if_bpf, m0); +#endif + +#ifdef EA_TX_DEBUG + dprintf(("Tx new packet\n")); +#endif + +/* + * Copy the datagram to the temporary buffer. + * + * Eventually we may as well just copy straight into the interface buffer + */ + + len = 0; + for (m = m0; m; m = m->m_next) { + if (m->m_len == 0) + continue; + bcopy(mtod(m, caddr_t), sc->sc_pktbuf + len, m->m_len); + len += m->m_len; + } + m_freem(m0); + +/* If packet size is odd round up to the next 16 bit boundry */ + + if (len % 2) + ++len; + + len = max(len, ETHER_MIN_LEN); + + if (len > ETHER_MAX_LEN) + log(LOG_WARNING, "ea: oversize packet = %d bytes\n", len); + +/* Ok we now have a packet len bytes long in our packet buffer */ + +/* Transfer datagram to board. */ + +#ifdef EA_TX_DEBUG + dprintf(("ea: xfr pkt length=%d...\n", len)); + + dprintf(("%s-->", ether_sprintf(sc->sc_pktbuf+6))); + dprintf(("%s\n", ether_sprintf(sc->sc_pktbuf))); +#endif + + sc->sc_config2 &= ~EA_CFG2_OUTPUT; + WriteShort(iobase + EA_8005_CONFIG2, sc->sc_config2); + +/* dprintf(("st=%04x\n", ReadShort(iobase + EA_8005_STATUS)));*/ + +/* Write the packet to the interface buffer, skipping the packet header */ + + ea_writebuf(sc, sc->sc_pktbuf, 0x0004, len); + +/* Follow it with a NULL packet header */ + + WriteShort(iobase + EA_8005_BUFWIN, 0x00); + WriteShort(iobase + EA_8005_BUFWIN, 0x00); + +/* Write the packet header */ + + ea_writebuf(sc, NULL, 0x0000, 0); + + WriteShort(iobase + EA_8005_BUFWIN, (((len+4) & 0xff00) >> 8) | (((len+4) & 0xff) << 8)); + WriteShort(iobase + EA_8005_BUFWIN, 0x00aa); + + WriteShort(iobase + EA_8005_TX_PTR, 0x0000); + +/* dprintf(("st=%04x\n", ReadShort(iobase + EA_8005_STATUS)));*/ + +#ifdef EA_DEBUG + ea_dump_buffer(sc, 0); +#endif + +/* Now transmit the datagram. */ + +/* dprintf(("st=%04x\n", ReadShort(iobase + EA_8005_STATUS)));*/ + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command | EA_CMD_TX_ON); +#ifdef EA_TX_DEBUG + dprintf(("st=%04x\n", ReadShort(iobase + EA_8005_STATUS))); + dprintf(("tx: queued\n")); +#endif +} + + +/* + * Ethernet controller interrupt. + */ + +int +eaintr(arg) + void *arg; +{ + register struct ea_softc *sc = arg; + u_int iobase = sc->sc_iobase; + int status, s; + u_int txstatus; + + dprintf(("eaintr: ")); + +/* Get the controller status */ + + status = ReadShort(iobase + EA_8005_STATUS); + dprintf(("st=%04x ", status)); + +/* Tx interrupt ? */ + + if (status & EA_STATUS_TX_INT) { + dprintf(("txint ")); + +/* Acknowledge the interrupt */ + + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command + | EA_CMD_TX_INTACK); + + ea_readbuf(sc, (u_char *)&txstatus, 0x0000, 4); + +#ifdef EA_TX_DEBUG + dprintf(("txstatus=%08x\n", txstatus)); +#endif + txstatus = (txstatus >> 24) & 0xff; + +/* + * Did it succeed ? Did we collide ? + * + * The exact proceedure here is not clear. We should get + * an interrupt on a sucessfull tx or on a collision. + * The done flag is set after successfull tx or 16 collisions + * We should thus get a interrupt for each of collision + * and the done bit should not be set. However it does appear + * to be set at the same time as the collision bit ... + * + * So we will count collisions and output errors and will assume + * that if the done bit is set the packet was transmitted. + * Stats may be wrong if 16 collisions occur on a packet + * as the done flag should be set but the packet may not have been + * transmitted. so the output count might not require incrementing + * if the 16 collisions flags is set. I don;t know abou this until + * it happens. + */ + + if (txstatus & EA_TXHDR_COLLISION) { + sc->sc_arpcom.ac_if.if_collisions++; + } else if (txstatus & EA_TXHDR_ERROR_MASK) { + sc->sc_arpcom.ac_if.if_oerrors++; + } + +/* if (txstatus & EA_TXHDR_ERROR_MASK) { + log(LOG_WARNING, "tx packet error =%02x\n", txstatus); + }*/ + + if (txstatus & EA_PKTHDR_DONE) { + sc->sc_arpcom.ac_if.if_opackets++; + + /* Tx next packet */ + + s = splimp(); + eatxpacket(sc); + (void)splx(s); + } + } + +/* Rx interrupt ? */ + + if (status & EA_STATUS_RX_INT) { + dprintf(("rxint ")); + +/* Acknowledge the interrupt */ + + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command + | EA_CMD_RX_INTACK); + +/* Install a watchdog timer needed atm to fixed rx lockups */ + + sc->sc_arpcom.ac_if.if_timer = EA_TIMEOUT; + +/* Processes the received packets */ + eagetpackets(sc); + +/* Make sure the receiver is on */ + +/* if ((status & EA_STATUS_RX_ON) == 0) { + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command + | EA_CMD_RX_ON); + printf("rxintr: rx is off st=%04x\n",status); + }*/ + } + +#ifdef EA_DEBUG + status = ReadShort(iobase + EA_8005_STATUS); + dprintf(("st=%04x\n", status)); +#endif + return(0); +} + +void +eagetpackets(sc) + struct ea_softc *sc; +{ + u_int iobase = sc->sc_iobase; + int addr; + int len; + int ctrl; + int ptr; + int pack; + int status; + u_int rxstatus; + +/* We start from the last rx pointer position */ + + addr = sc->sc_rx_ptr; + sc->sc_config2 &= ~EA_CFG2_OUTPUT; + WriteShort(iobase + EA_8005_CONFIG2, sc->sc_config2); + + do { +/* Read rx header */ + + ea_readbuf(sc, (u_char *)&rxstatus, addr, 4); + +/* Split the packet header */ + + ptr = ((rxstatus & 0xff) << 8) | ((rxstatus >> 8) & 0xff); + ctrl = (rxstatus >> 16) & 0xff; + status = (rxstatus >> 24) & 0xff; + +#ifdef EA_RX_DEBUG + dprintf(("addr=%04x ptr=%04x ctrl=%02x status=%02x\n", addr, ptr, ctrl, status)); +#endif + +/* Zero packet ptr ? then must be null header so exit */ + + if (ptr == 0) break; + +/* Get packet length */ + + len = (ptr - addr) - 4; + + if (len < 0) { + len += EA_RX_BUFFER_SIZE; + } + +#ifdef EA_RX_DEBUG + dprintf(("len=%04x\n", len)); +#endif + +/* Has the packet rx completed ? if not then exit */ + + if ((status & EA_PKTHDR_DONE) == 0) + break; + +/* Did we have any errors ? then note error and go to next packet */ + + if (status & 0x0f) { + ++sc->sc_arpcom.ac_if.if_ierrors; + printf("rx packet error (%02x) - dropping packet\n", status & 0x0f); +/* sc->sc_config2 |= EA_CFG2_OUTPUT; + WriteShort(iobase + EA_8005_CONFIG2, sc->sc_config2); + ea_reinit(sc); + return; */ + addr = ptr; + continue; + } + +/* Is the packet too big ? - this will probably be trapped above as a receive error */ + + if (len > ETHER_MAX_LEN) { + ++sc->sc_arpcom.ac_if.if_ierrors; + printf("rx packet size error len=%d\n", len); +/* sc->sc_config2 |= EA_CFG2_OUTPUT; + WriteShort(iobase + EA_8005_CONFIG2, sc->sc_config2); + ea_reinit(sc); + return;*/ + addr = ptr; + continue; + } + + ea_readbuf(sc, sc->sc_pktbuf, addr + 4, len); + +#ifdef EA_RX_DEBUG + dprintf(("%s-->", ether_sprintf(sc->sc_pktbuf+6))); + dprintf(("%s\n", ether_sprintf(sc->sc_pktbuf))); +#endif + sc->sc_arpcom.ac_if.if_ipackets++; + /* Pass data up to upper levels. */ + earead(sc, (caddr_t)sc->sc_pktbuf, len); + + addr = ptr; + ++pack; + } while (len != 0); + + sc->sc_config2 |= EA_CFG2_OUTPUT; + WriteShort(iobase + EA_8005_CONFIG2, sc->sc_config2); + +#ifdef EA_RX_DEBUG + dprintf(("new rx ptr=%04x\n", addr)); +#endif + +/* Store new rx pointer */ + + sc->sc_rx_ptr = addr; + WriteShort(iobase + EA_8005_RX_END, (sc->sc_rx_ptr >> 8)); + +/* Make sure the receiver is on */ + + WriteShort(iobase + EA_8005_COMMAND, sc->sc_command + | EA_CMD_RX_ON); + +} + + +/* + * Pass a packet up to the higher levels. + */ + +static void +earead(sc, buf, len) + struct ea_softc *sc; + caddr_t buf; + int len; +{ + register struct ether_header *eh; + struct mbuf *m; + + eh = (struct ether_header *)buf; + len -= sizeof(struct ether_header); + if (len <= 0) + return; + + /* Pull packet off interface. */ + m = eaget(buf, len, &sc->sc_arpcom.ac_if); + if (m == 0) + return; + +#if NBPFILTER > 0 + /* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to bpf. + */ + if (sc->sc_arpcom.ac_if.if_bpf) { + bpf_tap(sc->sc_arpcom.ac_if.if_bpf, buf, len + sizeof(struct ether_header)); +/* bpf_mtap(sc->sc_arpcom.ac_if.if_bpf, m);*/ + + /* + * 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 ((sc->sc_arpcom.ac_if.if_flags & IFF_PROMISC) && + (eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */ + bcmp(eh->ether_dhost, sc->sc_arpcom.ac_enaddr, + sizeof(eh->ether_dhost)) != 0) { + m_freem(m); + return; + } + } +#endif + + ether_input(&sc->sc_arpcom.ac_if, eh, m); +} + +/* + * Pull read data off a interface. Len is length of data, with local net + * header stripped. We copy the data into mbufs. When full cluster sized + * units are present we copy into clusters. + */ + +struct mbuf * +eaget(buf, totlen, ifp) + caddr_t buf; + int totlen; + struct ifnet *ifp; +{ + struct mbuf *top, **mp, *m; + int len; + register caddr_t cp = buf; + char *epkt; + + buf += sizeof(struct ether_header); + cp = buf; + epkt = cp + totlen; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return 0; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = totlen; + m->m_len = MHLEN; + top = 0; + mp = ⊤ + + while (totlen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return 0; + } + m->m_len = MLEN; + } + len = min(totlen, epkt - cp); + if (len >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + m->m_len = len = min(len, MCLBYTES); + else + len = m->m_len; + } else { + /* + * Place initial small packet/header at end of mbuf. + */ + if (len < m->m_len) { + if (top == 0 && len + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = len; + } else + len = m->m_len; + } + bcopy(cp, mtod(m, caddr_t), (unsigned)len); + cp += len; + *mp = m; + mp = &m->m_next; + totlen -= len; + if (cp == epkt) + cp = buf; + } + + return top; +} + +/* + * Process an ioctl request. This code needs some work - it looks pretty ugly. + */ +static int +ea_ioctl(ifp, cmd, data) + register struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct ea_softc *sc = ea_cd.cd_devs[ifp->if_unit]; + struct ifaddr *ifa = (struct ifaddr *)data; +/* struct ifreq *ifr = (struct ifreq *)data;*/ + int s, error = 0; + + s = splimp(); + + switch (cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + dprintf(("if_flags=%08x\n", ifp->if_flags)); + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + arp_ifinit(&sc->sc_arpcom, ifa); + dprintf(("Interface ea is coming up (AF_INET)\n")); + ea_init(sc); + break; +#endif +#ifdef NS + /* XXX - This code is probably wrong. */ + case AF_NS: + { + register struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *)(sc->sc_arpcom.ac_enaddr); + else + bcopy(ina->x_host.c_host, + sc->sc_arpcom.ac_enaddr, + sizeof(sc->sc_arpcom.ac_enaddr)); + /* Set new address. */ + dprintf(("Interface ea is coming up (AF_NS)\n")); + ea_init(sc); + break; + } +#endif + default: + dprintf(("Interface ea is coming up (default)\n")); + ea_init(sc); + break; + } + break; + + case SIOCSIFFLAGS: + dprintf(("if_flags=%08x\n", ifp->if_flags)); + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + /* + * If interface is marked down and it is running, then + * stop it. + */ + dprintf(("Interface ea is stopping\n")); + ea_stop(sc); + ifp->if_flags &= ~IFF_RUNNING; + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* + * If interface is marked up and it is stopped, then + * start it. + */ + dprintf(("Interface ea is restarting(1)\n")); + ea_init(sc); + } else { + /* + * Some other important flag might have changed, so + * reset. + */ + dprintf(("Interface ea is reinitialising\n")); + ea_reinit(sc); + } + break; + + default: + error = EINVAL; + break; + } + + (void)splx(s); + return error; +} + +/* + * Device timeout routine. + * + * Ok I am not sure exactly how the device timeout should work.... + * Currently what will happens is that that the device timeout is only + * set when a packet it received. This indicates we are on an active + * network and thus we should expect more packets. If non arrive in + * in the timeout period then we reinitialise as we may have jammed. + * We zero the timeout at this point so that we don't end up with + * an endless stream of timeouts if the network goes down. + */ + +static void +ea_watchdog(unit) + int unit; +{ + struct ea_softc *sc = ea_cd.cd_devs[unit]; + + log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname); + sc->sc_arpcom.ac_if.if_oerrors++; + dprintf(("ea_watchdog: ")); + dprintf(("st=%04x\n", ReadShort(sc->sc_iobase + EA_8005_STATUS))); + + /* Kick the interface */ + + ea_reinit(sc); + +/* sc->sc_arpcom.ac_if.if_timer = EA_TIMEOUT;*/ + sc->sc_arpcom.ac_if.if_timer = 0; +} + +/* End of if_ea.c */ |