diff --git a/sys/dev/ep/if_ep.c b/sys/dev/ep/if_ep.c index a526ca39a09..95341823f31 100644 --- a/sys/dev/ep/if_ep.c +++ b/sys/dev/ep/if_ep.c @@ -1,28 +1,253 @@ /* - * Copyright (c) 1993 Herb Peyerl - * All rights reserved. - * + * Copyright (c) 1993 Herb Peyerl 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. The name of the author may not 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 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. - * - * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp - * $Id: if_ep.c,v 1.13 1994/09/16 13:33:41 davidg Exp $ + * 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. The name + * of the author may not 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 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. + * + * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp $ $Id: if_ep.c,v 1.9 + * 1994/05/02 22:27:33 ats Exp $ + * + * October 26, 1994 + * + * Modified by: Andres Vega Garcia + * INRIA - Sophia Antipolis, France + * e-mail: avega@sophia.inria.fr + * finger: avega@pax.inria.fr + * + * + * What is new: + * + * 1) We can recognize more than 1 board. + * + * 2) The problem which used to happen with high trafic is corrected, + * (No more need to 'down' and 'up' the interface). + * + * 3) In the transmission, we use the TX start threshold in a more dynamic + * fashion (IMO the throughput is higher this way). + * + * 4) In the reception, we use the RX early threshold, that parameter is + * adapted as the packets arrive (IMO the throughput is higher this way). + * + * 5) Supports EISA cards. + * + * NB 0: The 32 bits acces is allowed for the EISA configured cards, thoung I + * wasn't able to test the code added. + * + * NB 1: I added the option EP_LOCAL_STATS, it can be temporary as IMO is just + * used while working on this driver and the program that displays this + * information (epstat). + * + * NB 2: About trailers, I didn't care if this implementation was OK, I just + * adapted it to have the same behaviour as in the original driver (donne + * just for epread()). + * + * + * Some driver statistics can be viewed with the epstat utility. In order to + * use this, you have to compile if_ep.c with + * + * -DEP_LOCAL_STATS + * + * which can be included in your machine config file (e.g. GENERICAH_EP) + * as an option (option EP_LOCAL_STATS). + * + * + * Modifications since FreeBSD 1.1.5.1 Release: + * + * This explanation concerns the epstart(), epread() and epintr() functions. + * + * ========================================================================= + * epstart() + * ========================================================================= + * + * + * Let's see what the idea is: + * + * + * Packet |------------------ LEN ---------------------| + * + * A + * CPU |---------------|----------------------------| + * + * + * Card |----------------------------| + * + * + * + * We suppose the Card is able to *write* bytes (send them to the media) + * at the speed S_CARD (bytes/s), and that is faster than the speed of the CPU, + * S_CPU, to write bytes to the TX FIFO, then, we have to write A bytes to the + * FIFO before enableing the transmision in the card. This way both, the card + * and the CPU must finish their writing at the same time. + * + * + * Let TX_RATE = S_CPU / S_CARD, where TX_RATE <= 1 + * + * We can find that: + * + * (1) A = LEN * (1 - TX_RATE) + * + * + * Let TX_RATE_R be the *very real* value. + * + * If TX_RATE > TX_RATE_R + * We are supposing the CPU is faster than it really is and + * certainly the card will *finish* before the CPU, having a + * TX Underrun Error, then, in such a case, we have to do: + * + * TX_RATE -= STEP, where STEP is the step at which we + * move TX_RATE + * + * If TX_RATE < TX_RATE_R + * We won't have the TX Underrun Error but it is possible that + * we don't use eficiently the TX START THRESH. feature. + * We prevent this by doing: + * + * TX_RATE += STEP every time we have sent succesfuly + * (without Underrun) a certain number + * of packets. + * + * Now, to avoid dealing with reals I used a FACTOR, then (1) will be + * transformed: + * + * Let tx_rate = FACTOR * TX_RATE, tx_rate is the parameter + * really used. + * + * A = (LEN * (FACTOR - tx_rate)) / FACTOR + * + * Actually FACTOR = 64 + * + * (2) A = (LEN * (64 - tx_rate)) >> 6 + * + * As I want to have some margin, and + * as I have to write a number multiple of 4: + * + * A = tx_start_threshold = (((LEN * (64 - tx_rate)) >> 6) & ~3) + 16 + * + * + * ========================================================================= + * epread() + * ========================================================================= + * + * I mantain an estimation of the RX packet's average length, and an + * estimation of the RX latency. + * + * Every time I receive a complete packet I compute the average packet's + * length, rx_avg_pkt: + * + * DELTA = LEN - rx_avg_pkt, where LEN is this packet's length + * + * if DELTA > 0 + * rx_avg_pkt += AVG_UP * DELTA + * else + * rx_avg_pkt += AVG_DOWN *DELTA + * + * + * AVG_UP < AVG_DOWN + * + * In the first case, I'm interested in being conservative about the + * average packet'length, because if I let it go up *too fast*, a shorter packet + * than expected will probably cause an RX Overrun Error. But if I consider + * the next packet will be smaller than it will, I just will have to wait + * for the packet to complete reception. + * + * In the other case, I'm interested in leting the rx_avg_pkt follow + * the real packet's lenght closer, as it is important not to think the average + * packet is bigger than it realy is. If I don't do that, and the rx_avg_pkt + * goes down *too slow*, I'll find myself thinking the packets are big when they + * are really small and I'll have probably Rx Overrun Errors. + * + * Actualy: + * + * AVG_UP = 1/32 + * AVG_DOWN = 1/8 + * + * + * Every time I receive an incomplete packet I recompute the RX latency + * (rx_latency). + * + * I know that if rx_latency = 0, when I go read the bytes from the RX + * FIFO, I'll find as many bytes as I programmed in the RX Early Threshold, but + * if rx_latency > 0, I'll find more bytes. + * + * Let CUR_LAT be the RX latency seen by this packet. + * + * CUR_LAT = LEN - rx_early_threshold, where LEN is the number of + * bytes I have just received. + * + * DELTA = CUR_LAT - rx_latency + * + * + * if DELTA >= 0 + * rx_latency += LAT_UP * DELTA + * else + * rx_latency += LAT_DOWN * DELTA + * + * + * LAT_UP > LAT_DOWN + * + * In a similar way as for rx average packet's length, I try to be more + * conservative in the more critical case. + * + * In the first case, I have to follow closer the incremets of the RX + * latency, because if I don't, I can find myself thinking that we (CPU) are + * *enough fast* and wait up to the last minute to go read data to find we have + * had RX Overrun Error. + * + * In the other case I must be more conservative to avoid falling in + * the situation I have just described, because if I go down *to fast* I'll + * think we are enough fast and we'll wake up later than due. + * + * Actually: + * + * LAT_UP = 1/4 + * LAT_DOWN = 1/32 + * + * Finally, I compute the rx_early_threshold for the next packet as: + * + * rx_early_threshold = rx_avg_pkt - rx_latency + * + * But, as I want to have a margin and + * as I have to write a value multiple of 4. + * + * + * rx_early_threshold = (rx_avg_pkt - rx_latency - 16) & ~3 + * + * + * But if I have to wait for the rest of an incomplete packet + * from which I have already received CUR_LEN bytes: + * + * + * rx_early_threshold = (rx_avg_pkt-CUR_LEN - rx_latency - 16) & ~3 + * + * + * ========================================================================= + * epintr() + * ========================================================================= + * + * For this function I just tryed to do what is stated in the + * Etherlink III Technical Reference. + * + * It was here where I really solved the problem that used to happen with + * high traffic. + * + * + * Andres + * avega@pax.inria.fr */ #include "ep.h" @@ -34,13 +259,13 @@ #if defined(__FreeBSD__) #include #include +#include #endif #include #include #include #include #include -#include #if defined(__NetBSD__) #include #endif @@ -71,359 +296,324 @@ #include #include #include -#include - -/* For backwards compatibility */ -#ifndef IFF_ALTPHYS -#define IFF_ALTPHYS IFF_LINK0 -#endif - - -#define ETHER_MIN_LEN 64 -#define ETHER_MAX_LEN 1518 -#define ETHER_ADDR_LEN 6 - -/* - * Ethernet software status per interface. - */ -struct ep_softc { - struct arpcom arpcom; /* Ethernet common part */ - ushort ep_iobase; /* i/o bus address */ - char ep_connectors; /* Connectors on this card. */ -#define MAX_MBS 8 /* # of mbufs we keep around */ - struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */ - int next_mb; /* Which mbuf to use next. */ - int last_mb; /* Last mbuf. */ - int tx_start_thresh; /* Current TX_start_thresh. */ - int tx_succ_ok; /* # packets sent in sequence w/o underrun */ - caddr_t bpf; /* BPF "magic cookie" */ - char bus32bit; /* 32bit access possible */ -} ep_softc[NEP]; static int epprobe __P((struct isa_device *)); static int epattach __P((struct isa_device *)); static int epioctl __P((struct ifnet * ifp, int, caddr_t)); +static void epmbuffill __P((caddr_t, int)); +static void epmbufempty __P((struct ep_softc *)); void epinit __P((int)); void epintr __P((int)); -void epmbuffill __P((void *)); -static void epmbufempty __P((struct ep_softc *)); void epread __P((struct ep_softc *)); void epreset __P((int)); void epstart __P((struct ifnet *)); void epstop __P((int)); void epwatchdog __P((int)); +static int send_ID_sequence __P((int)); +static int get_eeprom_data __P((int, int)); + +struct ep_softc ep_softc[NEP]; + +#define ep_ftst(f) (sc->stat&(f)) +#define ep_fset(f) (sc->stat|=(f)) +#define ep_frst(f) (sc->stat&=~(f)) + struct isa_driver epdriver = { - epprobe, - epattach, - "ep" + epprobe, + epattach, + "ep" }; static struct kern_devconf kdc_ep[NEP] = { { - 0, 0, 0, /* filled in by dev_attach */ - "ep", 0, { MDDT_ISA, 0, "net" }, - isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, - &kdc_isa0, /* parent */ - 0, /* parentdata */ - DC_BUSY, /* network interfaces are always ``open'' */ - "3Com 3C509 Ethernet adapter" + 0, 0, 0, /* filled in by dev_attach */ + "ep", 0, { MDDT_ISA, 0, "net" }, + isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, + &kdc_isa0, /* parent */ + 0, /* parentdata */ + DC_BUSY, /* network interfaces are always ``open'' */ + "3Com 3C509 Ethernet adapter" } }; static inline void ep_registerdev(struct isa_device *id) { - if(id->id_unit) - kdc_ep[id->id_unit] = kdc_ep[0]; - kdc_ep[id->id_unit].kdc_unit = id->id_unit; - kdc_ep[id->id_unit].kdc_parentdata = id; - dev_attach(&kdc_ep[id->id_unit]); + if(id->id_unit) + kdc_ep[id->id_unit] = kdc_ep[0]; + kdc_ep[id->id_unit].kdc_unit = id->id_unit; + kdc_ep[id->id_unit].kdc_parentdata = id; + dev_attach(&kdc_ep[id->id_unit]); } -static int send_ID_sequence __P((u_short)); -static u_short epreadeeprom __P((int, int)); -static int epbusyeeprom __P((int, ushort)); +int ep_current_tag = EP_LAST_TAG + 1; +int ep_board[EP_MAX_BOARDS + 1]; -#define MAXEPCARDS 20 /* if you have 21 cards in your machine... you lose */ - -static struct epcard { - int iobase; - u_short irq; - char available; - char bus32bit; -} epcards[MAXEPCARDS]; - -static int nepcards; - -static void -epaddcard(p, i, mode) - short p; - u_short i; - char mode; +static int +eeprom_rdy(is) + struct isa_device *is; { - if (nepcards >= sizeof(epcards)/sizeof(epcards[0])) - return; - epcards[nepcards].iobase = p; - epcards[nepcards].irq = 1 << ((i == 2) ? 9 : i); - epcards[nepcards].available = 1; - epcards[nepcards].bus32bit = mode; - nepcards++; -} + int i; - -/* - * 3c579 cards on the EISA bus are probed by their slot number. 3c509 - * cards on the ISA bus are probed in ethernet address order. The probe - * sequence requires careful orchestration, and we'd like like to allow - * the irq and base address to be wildcarded. So, we probe all the cards - * the first time epprobe() is called. On subsequent calls we look for - * matching cards. - */ -int -epprobe(is) - struct isa_device *is; -{ - struct ep_softc *sc = &ep_softc[is->id_unit]; - static int probed; - int slot, iobase, i; - u_short k, k2; - u_short prodid; - - if (probed==0) { - probed = 1; - - /* find all EISA cards */ - for (slot = 1; slot < 16; slot++) { - iobase = 0x1000 * slot; - outw(iobase + EP_COMMAND, GLOBAL_RESET); - DELAY(1000); - if (inw(iobase + EISA_VENDOR) != MFG_ID) - continue; - k = inw(iobase + EISA_MODEL); -#ifdef EP_DEBUG -printf("prod id = %x ", k); -prodid = k; -#endif - if ((k & 0xf0ff) != PROD_ID) - continue; - - k = inw(iobase + EP_W0_CONFIG_CTRL); - /* enable adapter */ - outw(iobase + EP_W0_CONFIG_CTRL, k | 1); -#ifdef EP_DEBUG -printf("config = %x ", k); -#endif - - /* read in eeprom address configuration */ - if (epbusyeeprom(slot - 1, iobase)) - continue; - outw(iobase + EP_W0_EEPROM_COMMAND, READ_EEPROM | EEPROM_ADDR_CFG); - if (epbusyeeprom(slot - 1, iobase)) - continue; - k = inw(iobase + EP_W0_EEPROM_DATA); -#ifdef EP_DEBUG -printf("addr_cfg = %x ", k); -#endif - outw(iobase + EP_W0_ADDRESS_CFG, k); - /* read in eeprom resource configuration */ - if (epbusyeeprom(slot - 1, iobase)) - continue; - outw(iobase + EP_W0_EEPROM_COMMAND, READ_EEPROM | EEPROM_RESOURCE_CFG); - if (epbusyeeprom(slot - 1, iobase)) - continue; - k2 = inw(iobase + EP_W0_EEPROM_DATA); - -#ifdef EP_DEBUG -/** XXXXXXXXXXXXXXXXXXXXX*/ -/* This doesn't give back the actual IRQ number as it should be , ATS */ -/* In the moment simply hardcoded the IRQ's for testing purposes */ -printf("resource config = %x\n", k2); -if (prodid == 0x9150) /* the 3c509 card */ - k2 = 7 << 12; -else - k2 = 3 << 12; /* the eisa 3c579 card set to irq 3 */ -#endif - - outw(iobase + EP_W0_RESOURCE_CFG, k2); - epaddcard(iobase, k2 >> 12, 1); - } - - /* find all isa cards */ -#ifdef 0 - outw(BASE + EP_COMMAND, GLOBAL_RESET); -#endif - DELAY(1000); - elink_reset(); /* global reset to ELINK_ID_PORT */ - DELAY(1000); - - for (slot = 0; slot < 10; slot++) { - outb(ELINK_ID_PORT, 0x00); - elink_idseq(ELINK_509_POLY); - DELAY(1000); - - k = epreadeeprom(ELINK_ID_PORT, EEPROM_MFG_ID); - if (k != MFG_ID) - continue; - k = epreadeeprom(ELINK_ID_PORT, EEPROM_PROD_ID); - if ((k & 0xf0ff) != PROD_ID) - continue; - - k = epreadeeprom(ELINK_ID_PORT, EEPROM_ADDR_CFG); - k = (k & 0x1f) * 0x10 + 0x200; - - k2 = epreadeeprom(ELINK_ID_PORT, EEPROM_RESOURCE_CFG); - k2 >>= 12; - epaddcard(k, k2, 0); - - /* so card will not respond to contention again */ - outb(ELINK_ID_PORT, TAG_ADAPTER_0 + 1); - - /* - * XXX: this should probably not be done here - * because it enables the drq/irq lines from - * the board. Perhaps it should be done after - * we have checked for irq/drq collisions? - */ - outb(ELINK_ID_PORT, ACTIVATE_ADAPTER_TO_CONFIG); - } - /* XXX should we sort by ethernet address? */ - } - - /* - * a very specific search order: - * exact iobase & irq - * exact iobase, wildcard irq - * wildcard iobase, exact irq - * wildcard iobase & irq - * else fail.. - */ - if (is->id_iobase != 0 && is->id_irq != (u_short)0) { - for (i = 0; iid_iobase == epcards[i].iobase && - is->id_irq == epcards[i].irq) - goto good; - } - } - if (is->id_iobase != 0 && is->id_irq == (u_short)0) { - for (i = 0; iid_iobase == epcards[i].iobase) - goto good; - } - } - if (is->id_iobase == 0 && is->id_irq != (u_short)0) { - for (i = 0; iid_irq == epcards[i].irq) - goto good; - } - } - return 0; - -good: - epcards[i].available = 0; - sc->bus32bit = epcards[i].bus32bit; - is->id_iobase = epcards[i].iobase; - - return (0x10); /* 16 bytes of I/O space used. */ + for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++); + if (i >= MAX_EEPROMBUSY) { + printf("ep%d: eeprom failed to come ready.\n", is->id_unit); + return (0); + } + return (1); } static int -epattach(is) - struct isa_device *is; +ep_look_for_board_at(is) + struct isa_device *is; { - struct ep_softc *sc = &ep_softc[is->id_unit]; - struct ifnet *ifp = &sc->arpcom.ac_if; - u_short i; - struct ifaddr *ifa; - struct sockaddr_dl *sdl; + int data, i, j, io_base, id_port = EP_ID_PORT; + int nisa = 0, neisa = 0; - sc->ep_iobase = is->id_iobase; + if (ep_current_tag == (EP_LAST_TAG + 1)) { + /* Come here just one time */ + + /* Look for the EISA boards, leave them activated */ + for(j = 1; j < 16; j++) { + io_base = (j * EP_EISA_START) | EP_EISA_W0; + if (inw(io_base + EP_W0_MFG_ID) != MFG_ID) + continue; - printf("ep%d: ", is->id_unit); + /* we must found 0x1f if the board is EISA configurated */ + if ((inw(io_base + EP_W0_ADDRESS_CFG) & 0x1f) != 0x1f) + continue; - sc->ep_connectors = 0; - i = inw(is->id_iobase + EP_W0_CONFIG_CTRL); - if (i & IS_AUI) { - printf("aui"); - sc->ep_connectors |= AUI; + /* Reset and Enable the card */ + outb(io_base + EP_W0_CONFIG_CTRL, W0_P4_CMD_RESET_ADAPTER); + DELAY(1000); /* we must wait at least 1 ms */ + outb(io_base + EP_W0_CONFIG_CTRL, W0_P4_CMD_ENABLE_ADAPTER); + + /* + * Once activated, all the registers are mapped in the range + * x000 - x00F, where x is the slot number. + */ + ep_board[neisa++] = j * EP_EISA_START; } - if (i & IS_BNC) { - if (sc->ep_connectors) - printf("/"); - printf("bnc"); - sc->ep_connectors |= BNC; + ep_current_tag--; + + /* Look for the ISA boards. Init and leave them actived */ + outb(id_port, 0xc0); /* Global reset */ + DELAY(1000); + for (i = 0; i < EP_MAX_BOARDS; i++) { + outb(id_port, 0); + outb(id_port, 0); + send_ID_sequence(id_port); + + data = get_eeprom_data(id_port, EEPROM_MFG_ID); + if (data != MFG_ID) + break; + + /* resolve contention using the Ethernet address */ + for (j = 0; j < 3; j++) + data = get_eeprom_data(id_port, j); + + ep_board[neisa+nisa++] = + (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x10 + 0x200; + outb(id_port, ep_current_tag); /* tags board */ + outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG); + ep_current_tag--; } - if (i & IS_UTP) { - if (sc->ep_connectors) - printf("/"); - printf("utp"); - sc->ep_connectors |= UTP; + + ep_board[neisa+nisa] = 0; + if (neisa) { + printf("%d 3C5x9 board(s) on EISA found at", neisa); + for (j = 0; ep_board[j]; j++) + if (ep_board[j] >= EP_EISA_START) + printf(" 0x%x", ep_board[j]); + printf("\n"); } - if (!sc->ep_connectors) - printf("no connectors!"); - - /* - * Read the station address from the eeprom - */ - for (i = 0; i < 3; i++) { - u_short *p; - GO_WINDOW(0); - if (epbusyeeprom(is->id_unit, sc->ep_iobase)) - return(0); - outw(BASE + EP_W0_EEPROM_COMMAND, READ_EEPROM | i); - if (epbusyeeprom(is->id_unit, sc->ep_iobase)) - return(0); - p =(u_short *)&sc->arpcom.ac_enaddr[i*2]; - *p = htons(inw(BASE + EP_W0_EEPROM_DATA)); - GO_WINDOW(2); - outw(BASE + EP_W2_ADDR_0 + (i * 2), ntohs(*p)); + if (nisa) { + printf("%d 3C5x9 board(s) on ISA found at", nisa); + for (j = 0; ep_board[j]; j++) + if (ep_board[j] < EP_EISA_START) + printf(" 0x%x", ep_board[j]); + printf("\n"); } - printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr)); + } - ifp->if_unit = is->id_unit; - ifp->if_name = "ep"; - ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | - IFF_MULTICAST ; - ifp->if_init = epinit; - ifp->if_output = ether_output; - ifp->if_start = epstart; - ifp->if_ioctl = epioctl; - ifp->if_watchdog = epwatchdog; + for (i = 0; ep_board[i] && ep_board[i] != IS_BASE; i++); + if (ep_board[i] == IS_BASE) { + if (inw(IS_BASE + EP_W0_EEPROM_COMMAND) & EEPROM_TST_MODE) + printf("ep%d: 3c5x9 at 0x%x in test mode. Erase pencil mark!\n", + is->id_unit, IS_BASE); + return (1); + } + return (0); +} - if_attach(ifp); - ep_registerdev(is); +/* + * get_e: gets a 16 bits word from the EEPROM. we must have set the window + * before + */ +static int +get_e(is, offset) + struct isa_device *is; + int offset; +{ + if (!eeprom_rdy(is)) + return (0xffff); + outw(IS_BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset); + if (!eeprom_rdy(is)) + return (0xffff); + return (inw(IS_BASE + EP_W0_EEPROM_DATA)); +} - /* - * Fill the hardware address into ifa_addr if we find an - * AF_LINK entry. We need to do this so bpf's can get the hardware - * addr of this card. netstat likes this too! - */ - ifa = ifp->if_addrlist; - while ((ifa != 0) && (ifa->ifa_addr != 0) && - (ifa->ifa_addr->sa_family != AF_LINK)) - ifa = ifa->ifa_next; +int +epprobe(is) + struct isa_device *is; +{ + struct ep_softc *sc = &ep_softc[is->id_unit]; + u_short k; + int i; - if ((ifa != 0) && (ifa->ifa_addr != 0)) { - sdl = (struct sockaddr_dl *) ifa->ifa_addr; - sdl->sdl_type = IFT_ETHER; - sdl->sdl_alen = ETHER_ADDR_LEN; - sdl->sdl_slen = 0; - bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); - } -#if NBPFILTER > 0 - bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); + if (!ep_look_for_board_at(is)) + return (0); + /* + * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be + * 0x9[0-f]50 + */ + GO_WINDOW(0); + k = get_e(is, EEPROM_PROD_ID); + if ((k & 0xf0ff) != (PROD_ID & 0xf0ff)) { + printf("epprobe: ignoring model %04x\n", k); + return (0); + } + + k = get_e(is, EEPROM_RESOURCE_CFG); + k >>= 12; + if (is->id_irq != (1 << ((k == 2) ? 9 : k))) { + printf("epprobe: interrupt number %d doesn't match\n",is->id_irq); + return (0); + } + + if (BASE >= EP_EISA_START) /* we have an EISA board, we allow 32 bits access */ + sc->stat = F_ACCESS_32_BITS; + else + sc->stat = 0; + + /* By now, the adapter is already activated */ + + return (0x10); /* 16 bytes of I/O space used. */ +} + +static char *ep_conn_type[] = {"UTP", "AUI", "???", "BNC"}; + +static int +epattach(is) + struct isa_device *is; +{ + struct ep_softc *sc = &ep_softc[is->id_unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + u_short i, j, *p; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + + /* BASE = IS_BASE; */ + sc->ep_io_addr = is->id_iobase; + + printf("ep%d: ", is->id_unit); + + sc->ep_connectors = 0; + i = inw(IS_BASE + EP_W0_CONFIG_CTRL); + j = inw(IS_BASE + EP_W0_ADDRESS_CFG) >> 14; + if (i & IS_AUI) { + printf("aui"); + sc->ep_connectors |= AUI; + } + if (i & IS_BNC) { + if (sc->ep_connectors) + printf("/"); + printf("bnc"); + sc->ep_connectors |= BNC; + } + if (i & IS_UTP) { + if (sc->ep_connectors) + printf("/"); + printf("utp"); + sc->ep_connectors |= UTP; + } + if (!(sc->ep_connectors & 7)) + printf("no connectors!"); + else + printf("[*%s*]", ep_conn_type[j]); + + /* + * Read the station address from the eeprom + */ + p = (u_short *) & sc->arpcom.ac_enaddr; + for (i = 0; i < 3; i++) { + GO_WINDOW(0); + p[i] = htons(get_e(is, i)); + GO_WINDOW(2); + outw(BASE + EP_W2_ADDR_0 + (i * 2), ntohs(p[i])); + } + printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr)); + + ifp->if_unit = is->id_unit; + ifp->if_name = "ep"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; + ifp->if_init = epinit; + ifp->if_output = ether_output; + ifp->if_start = epstart; + ifp->if_ioctl = epioctl; + ifp->if_watchdog = epwatchdog; + + if_attach(ifp); + ep_registerdev(is); + + /* + * Fill the hardware address into ifa_addr if we find an AF_LINK entry. + * We need to do this so bpf's can get the hardware addr of this card. + * netstat likes this too! + */ + ifa = ifp->if_addrlist; + while ((ifa != 0) && (ifa->ifa_addr != 0) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + + if ((ifa != 0) && (ifa->ifa_addr != 0)) { + sdl = (struct sockaddr_dl *) ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); + } + /* we give some initial parameters */ + sc->rx_avg_pkt = 128; + + /* + * NOTE: In all this I multiply everything by 64. + * W_s = the speed the CPU is able to write to the TX FIFO. + * T_s = the speed the board sends the info to the Ether. + * W_s/T_s = 16 (represents 16/64) => W_s = 25 % of T_s. + * This will give us for a packet of 1500 bytes + * tx_start_thresh=1125 and for a pkt of 64 bytes tx_start_threshold=48. + * We prefer to start thinking the CPU is much slower than the Ethernet + * transmission. + */ + sc->tx_rate = TX_INIT_RATE; + sc->tx_counter = 0; + sc->rx_latency = RX_INIT_LATENCY; + sc->rx_early_thresh = RX_INIT_EARLY_THRESH; +#ifdef EP_LOCAL_STATS + sc->rx_no_first = sc->rx_no_mbuf = + sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl = + sc->tx_underrun = 0; #endif + ep_fset(F_RX_FIRST); + sc->top = sc->mcur = 0; - sc->tx_start_thresh = 20; /* probably a good starting point. */ - - return 1; +#if NBPFILTER > 0 + bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + return 1; } @@ -433,752 +623,847 @@ epattach(is) */ void epinit(unit) - int unit; + int unit; { - register struct ep_softc *sc = &ep_softc[unit]; - register struct ifnet *ifp = &sc->arpcom.ac_if; - int s, i; + register struct ep_softc *sc = &ep_softc[unit]; + register struct ifnet *ifp = &sc->arpcom.ac_if; + int s, i; - if (ifp->if_addrlist == (struct ifaddr *) 0) - return; + if (ifp->if_addrlist == (struct ifaddr *) 0) + return; - s = splimp(); - while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) - ; + s = splimp(); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); - GO_WINDOW(0); + GO_WINDOW(0); - /* Disable the card */ - outw(BASE + EP_W0_CONFIG_CTRL, 0); + /* Disable the card */ + outw(BASE + EP_W0_CONFIG_CTRL, 0); - /* Enable the card */ - outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); + /* Enable the card */ + outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); - GO_WINDOW(2); + GO_WINDOW(2); - /* Reload the ether_addr. */ - for (i = 0; i < 6; i++) - outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); + /* Reload the ether_addr. */ + for (i = 0; i < 6; i++) + outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); - outw(BASE + EP_COMMAND, RX_RESET); - outw(BASE + EP_COMMAND, TX_RESET); + outw(BASE + EP_COMMAND, RX_RESET); + outw(BASE + EP_COMMAND, TX_RESET); - /* Window 1 is operating window */ - GO_WINDOW(1); - for (i = 0; i < 31; i++) - inb(BASE + EP_W1_TX_STATUS); + /* Window 1 is operating window */ + GO_WINDOW(1); + for (i = 0; i < 31; i++) + inb(BASE + EP_W1_TX_STATUS); - /* get rid of stray intr's */ - outw(BASE + EP_COMMAND, ACK_INTR | 0xff); + /* get rid of stray intr's */ + outw(BASE + EP_COMMAND, ACK_INTR | 0xff); - outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE | - S_TX_COMPLETE | S_TX_AVAIL); - outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE | - S_TX_COMPLETE | S_TX_AVAIL); + outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS); - outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | - FIL_MULTICAST | FIL_BRDCST); + outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS); + + outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | + FIL_GROUP | FIL_BRDCST); /* - * you can `ifconfig (link0|-link0) ep0' to get the following + * you can `ifconfig ep0 (bnc|aui)' to get the following * behaviour: - * -link0 disable AUI/UTP. enable BNC. - * link0 disable BNC. enable AUI. if the card has a UTP + * bnc disable AUI/UTP. enable BNC. + * aui disable BNC. enable AUI. if the card has a UTP * connector, that is enabled too. not sure, but it * seems you have to be careful to not plug things * into both AUI & UTP. */ #if defined(__NetBSD__) - if (!(ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & BNC)) { + if (!(ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & BNC)) { #else - if (!(ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & BNC)) { + if (!(ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & BNC)) { #endif - outw(BASE + EP_COMMAND, START_TRANSCEIVER); - DELAY(1000); - } + outw(BASE + EP_COMMAND, START_TRANSCEIVER); + DELAY(1000); + } #if defined(__NetBSD__) - if ((ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & UTP)) { + if ((ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & UTP)) { #else - if ((ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & UTP)) { + if ((ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & UTP)) { #endif - GO_WINDOW(4); - outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); - GO_WINDOW(1); - } + GO_WINDOW(4); + outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); + GO_WINDOW(1); + } + outw(BASE + EP_COMMAND, RX_ENABLE); + outw(BASE + EP_COMMAND, TX_ENABLE); - outw(BASE + EP_COMMAND, RX_ENABLE); - outw(BASE + EP_COMMAND, TX_ENABLE); + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; /* just in case */ - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; /* just in case */ - /* - * Store up a bunch of mbuf's for use later. (MAX_MBS). First we - * free up any that we had in case we're being called from intr or - * somewhere else. - */ - sc->last_mb = 0; - sc->next_mb = 0; - epmbuffill((void *)sc); + sc->tx_rate = TX_INIT_RATE; + sc->tx_counter = 0; + sc->rx_latency = RX_INIT_LATENCY; + sc->rx_early_thresh = RX_INIT_EARLY_THRESH; +#ifdef EP_LOCAL_STATS + sc->rx_no_first = sc->rx_no_mbuf = + sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl = + sc->tx_underrun = 0; +#endif + ep_fset(F_RX_FIRST); + ep_frst(F_RX_TRAILER); + if (sc->top) { + m_freem(sc->top); + sc->top = sc->mcur = 0; + } + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | sc->rx_early_thresh); - epstart(ifp); + /* + * Store up a bunch of mbuf's for use later. (MAX_MBS). First we free up + * any that we had in case we're being called from intr or somewhere + * else. + */ + sc->last_mb = 0; + sc->next_mb = 0; + epmbuffill((caddr_t) sc, 0); - splx(s); + epstart(ifp); + + splx(s); } static const char padmap[] = {0, 3, 2, 1}; void epstart(ifp) - struct ifnet *ifp; + struct ifnet *ifp; { - register struct ep_softc *sc = &ep_softc[ifp->if_unit]; - struct mbuf *m, *top; - int s, len, pad; - - s = splimp(); - if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) { - splx(s); - return; - } + register struct ep_softc *sc = &ep_softc[ifp->if_unit]; + register u_int len; + register struct mbuf *m; + struct mbuf *top; + int s, pad; + s = splimp(); + if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) { + splx(s); + return; + } startagain: - /* Sneak a peek at the next packet */ - m = sc->arpcom.ac_if.if_snd.ifq_head; - if (m == 0) { - splx(s); - return; - } + /* Sneak a peek at the next packet */ + m = sc->arpcom.ac_if.if_snd.ifq_head; + if (m == 0) { + splx(s); + return; + } #if 0 - len = m->m_pkthdr.len; + len = m->m_pkthdr.len; #else - for (len = 0, top = m; m; m = m->m_next) - len += m->m_len; + for (len = 0, top = m; m; m = m->m_next) + len += m->m_len; #endif - pad = padmap[len & 3]; - - /* - * The 3c509 automatically pads short packets to minimum ethernet - * length, but we drop packets that are too large. Perhaps we should - * truncate them instead? - */ - if (len + pad > ETHER_MAX_LEN) { - /* packet is obviously too large: toss it */ - ++sc->arpcom.ac_if.if_oerrors; - IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); - m_freem(m); - goto readcheck; - } - - if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { - /* no room in FIFO */ - outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); - sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; - splx(s); - return; - } else { - outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 2044); - } + pad = padmap[len & 3]; + /* + * The 3c509 automatically pads short packets to minimum ethernet length, + * but we drop packets that are too large. Perhaps we should truncate + * them instead? + */ + if (len + pad > ETHER_MAX_LEN) { + /* packet is obviously too large: toss it */ + ++sc->arpcom.ac_if.if_oerrors; IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); - if (m == 0) { /* not really needed */ - splx(s); - return; + m_freem(m); + goto readcheck; + } + if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { + /* no room in FIFO */ + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); + sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; + splx(s); + return; + } + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + + outw(BASE + EP_W1_TX_PIO_WR_1, len); + outw(BASE + EP_W1_TX_PIO_WR_1, 0x0); /* Second dword meaningless */ + + /* compute the Tx start threshold for this packet */ + sc->tx_start_thresh = len = + (((len * (64 - sc->tx_rate)) >> 6) & ~3) + 16; + outw(BASE + EP_COMMAND, SET_TX_START_THRESH | len); + + for (top = m; m != 0; m = m->m_next) + if(ep_ftst(F_ACCESS_32_BITS)) { + outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), + m->m_len / 4); + if (m->m_len & 3) + outsb(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t) + m->m_len / 4, + m->m_len & 3); + } else { + outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 2); + if (m->m_len & 1) + outb(BASE + EP_W1_TX_PIO_WR_1, + *(mtod(m, caddr_t) + m->m_len - 1)); } - outw(BASE + EP_COMMAND, SET_TX_START_THRESH | - (len / 4 + sc->tx_start_thresh)); - outw(BASE + EP_W1_TX_PIO_WR_1, len); - outw(BASE + EP_W1_TX_PIO_WR_1, 0xffff); /* Second dword meaningless */ - - for (top = m; m != 0; m = m->m_next) { - if (sc->bus32bit) { - if(m->m_len > 3) - outsl(BASE + EP_W1_TX_PIO_WR_1, - mtod(m, caddr_t), m->m_len/4); - if(m->m_len & 3) - outsb(BASE + EP_W1_TX_PIO_WR_1, - mtod(m, caddr_t) + (m->m_len & ~3), m->m_len & 3); - } else { - if (m->m_len > 1) - outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), - m->m_len/2); - if (m->m_len & 1) - outb(BASE + EP_W1_TX_PIO_WR_1, - *(mtod(m, caddr_t) + m->m_len - 1)); - - } - } - while (pad--) - outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ + while (pad--) + outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ #if NBPFILTER > 0 - if (sc->bpf) { - u_short etype; - int off, datasize, resid; - struct ether_header *eh; - struct trailer_header { - u_short ether_type; - u_short ether_residual; - } trailer_header; - char ether_packet[ETHER_MAX_LEN]; - char *ep; + if (sc->bpf) { + u_short etype; + int off, datasize, resid; + struct ether_header *eh; + struct trailer_header { + u_short ether_type; + u_short ether_residual; + } trailer_header; + char ether_packet[ETHER_MAX_LEN]; + char *ep; - ep = ether_packet; - - /* - * We handle trailers below: - * Copy ether header first, then residual data, - * then data. Put all this in a temporary buffer - * 'ether_packet' and send off to bpf. Since the - * system has generated this packet, we assume - * that all of the offsets in the packet are - * correct; if they're not, the system will almost - * certainly crash in m_copydata. - * We make no assumptions about how the data is - * arranged in the mbuf chain (i.e. how much - * data is in each mbuf, if mbuf clusters are - * used, etc.), which is why we use m_copydata - * to get the ether header rather than assume - * that this is located in the first mbuf. - */ - /* copy ether header */ - m_copydata(top, 0, sizeof(struct ether_header), ep); - eh = (struct ether_header *) ep; - ep += sizeof(struct ether_header); - eh->ether_type = etype = ntohs(eh->ether_type); - if (etype >= ETHERTYPE_TRAIL && - etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { - datasize = ((etype - ETHERTYPE_TRAIL) << 9); - off = datasize + sizeof(struct ether_header); - - /* copy trailer_header into a data structure */ - m_copydata(top, off, sizeof(struct trailer_header), - (caddr_t)&trailer_header.ether_type); - - /* copy residual data */ - resid = trailer_header.ether_residual - - sizeof(struct trailer_header); - resid = ntohs(resid); - m_copydata(top, off + sizeof(struct trailer_header), - resid, ep); - ep += resid; - - /* copy data */ - m_copydata(top, sizeof(struct ether_header), - datasize, ep); - ep += datasize; - - /* restore original ether packet type */ - eh->ether_type = trailer_header.ether_type; - - bpf_tap(sc->bpf, ether_packet, ep - ether_packet); - } else - bpf_mtap(sc->bpf, top); - } -#endif - - m_freem(top); - ++sc->arpcom.ac_if.if_opackets; + ep = ether_packet; /* - * Is another packet coming in? We don't want to overflow the - * tiny RX fifo. + * We handle trailers below: Copy ether header first, then residual + * data, then data. Put all this in a temporary buffer 'ether_packet' + * and send off to bpf. Since the system has generated this packet, + * we assume that all of the offsets in the packet are correct; if + * they're not, the system will almost certainly crash in m_copydata. + * We make no assumptions about how the data is arranged in the mbuf + * chain (i.e. how much data is in each mbuf, if mbuf clusters are + * used, etc.), which is why we use m_copydata to get the ether + * header rather than assume that this is located in the first mbuf. */ + /* copy ether header */ + m_copydata(top, 0, sizeof(struct ether_header), ep); + eh = (struct ether_header *) ep; + ep += sizeof(struct ether_header); + eh->ether_type = etype = ntohs(eh->ether_type); + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { + datasize = ((etype - ETHERTYPE_TRAIL) << 9); + off = datasize + sizeof(struct ether_header); + + /* copy trailer_header into a data structure */ + m_copydata(top, off, sizeof(struct trailer_header), + (caddr_t) & trailer_header.ether_type); + + /* copy residual data */ + resid = trailer_header.ether_residual - + sizeof(struct trailer_header); + resid = ntohs(resid); + m_copydata(top, off + sizeof(struct trailer_header), + resid, ep); + ep += resid; + + /* copy data */ + m_copydata(top, sizeof(struct ether_header), + datasize, ep); + ep += datasize; + + /* restore original ether packet type */ + eh->ether_type = trailer_header.ether_type; + + bpf_tap(sc->bpf, ether_packet, ep - ether_packet); + } else + bpf_mtap(sc->bpf, top); + } +#endif + + sc->arpcom.ac_if.if_opackets++; + m_freem(top); + /* + * Every 1024*4 packets we increment the tx_rate if we haven't had + * errors, that in the case it has abnormaly goten too low + */ + if (!(++sc->tx_counter & (1024 * 4 - 1)) && + sc->tx_rate < TX_INIT_MAX_RATE) + sc->tx_rate++; + + /* + * Is another packet coming in? We don't want to overflow the tiny RX + * fifo. + */ readcheck: - if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) { - splx(s); - return; + if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) { + /* + * we check if we have packets left, in that case we prepare to come + * back later + */ + if (sc->arpcom.ac_if.if_snd.ifq_head) { + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | + sc->tx_start_thresh); } - goto startagain; + splx(s); + return; + } + goto startagain; } void epintr(unit) - int unit; + int unit; { - int status, i; - register struct ep_softc *sc = &ep_softc[unit]; - struct ifnet *ifp = &sc->arpcom.ac_if; - struct mbuf *m; + int i; + register int status; + register struct ep_softc *sc = &ep_softc[unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; - status = 0; -checkintr: - status = inw(BASE + EP_STATUS) & - (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE); - if (status == 0) { - /* No interrupts. */ - outw(BASE + EP_COMMAND, C_INTR_LATCH); - return; + outw(BASE + EP_COMMAND, SET_INTR_MASK); /* disable all Ints */ + outw(BASE + EP_COMMAND, C_INTR_LATCH); /* ACK int Latch */ + + while ((status = inw(BASE + EP_STATUS)) & S_5_INTS) { + if (status & (S_RX_COMPLETE | S_RX_EARLY)) { + /* we just need ACK for RX_EARLY */ + if (status & S_RX_EARLY) + outw(BASE + EP_COMMAND, C_RX_EARLY); + epread(sc); + continue; } - /* important that we do this first. */ - outw(BASE + EP_COMMAND, ACK_INTR | status); - if (status & S_TX_AVAIL) { - status &= ~S_TX_AVAIL; - inw(BASE + EP_W1_FREE_TX); - sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; - epstart(&sc->arpcom.ac_if); - } - if (status & S_RX_COMPLETE) { - status &= ~S_RX_COMPLETE; - epread(sc); + /* we need ACK */ + outw(BASE + EP_COMMAND, C_TX_AVAIL); + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + epstart(&sc->arpcom.ac_if); } if (status & S_CARD_FAILURE) { - printf("ep%d: reset (status: %x)\n", unit, status); - outw(BASE + EP_COMMAND, C_INTR_LATCH); - epinit(unit); - return; +#ifdef EP_LOCAL_STATS + printf("\nep%d:\n\tStatus: %x\n", unit, status); + GO_WINDOW(4); + printf("\tFIFO Diagnostic: %x\n", inw(BASE + EP_W4_FIFO_DIAG)); + printf("\tStat: %x\n", sc->stat); + printf("\tIpackets=%d, Opackets=%d\n", + sc->arpcom.ac_if.if_ipackets, sc->arpcom.ac_if.if_opackets); + printf("\tNOF=%d, NOMB=%d, BPFD=%d, RXOF=%d, RXOL=%d, TXU=%d\n", + sc->rx_no_first, sc->rx_no_mbuf, sc->rx_bpf_disc, sc->rx_overrunf, + sc->rx_overrunl, sc->tx_underrun); +#else + printf("ep%d: Status: %x\n", unit, status); +#endif + epinit(unit); + return; } if (status & S_TX_COMPLETE) { - status &= ~S_TX_COMPLETE; - /* - * We need to read TX_STATUS until we get a 0 status in - * order to turn off the interrupt flag. - */ - while ((i = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) { - outw(BASE + EP_W1_TX_STATUS, 0x0); - if (i & (TXS_MAX_COLLISION | TXS_JABBER | TXS_UNDERRUN)) { - if (i & TXS_MAX_COLLISION) - ++sc->arpcom.ac_if.if_collisions; - if (i & (TXS_JABBER | TXS_UNDERRUN)) { - outw(BASE + EP_COMMAND, TX_RESET); - if (i & TXS_UNDERRUN) { - if (sc->tx_start_thresh < ETHER_MAX_LEN) { - sc->tx_start_thresh += 20; - outw(BASE + EP_COMMAND, - SET_TX_START_THRESH | - sc->tx_start_thresh); - } - } - } - outw(BASE + EP_COMMAND, TX_ENABLE); - ++sc->arpcom.ac_if.if_oerrors; + /* we need ACK. we do it at the end */ + /* + * We need to read TX_STATUS until we get a 0 status in order to + * turn off the interrupt flag. + */ + while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) { + if (status & TXS_SUCCES_INTR_REQ); + else if (status & (TXS_UNDERRUN | TXS_JABBER | TXS_MAX_COLLISION)) { + outw(BASE + EP_COMMAND, TX_RESET); + if (status & TXS_UNDERRUN) { + if (sc->tx_rate > 1) { + sc->tx_rate--; /* Actually in steps of 1/64 */ + sc->tx_counter = 0; /* We reset it */ } +#ifdef EP_LOCAL_STATS + sc->tx_underrun++; +#endif + } else { + if (status & TXS_JABBER); + else /* TXS_MAX_COLLISION - we shouldn't get here */ + ++sc->arpcom.ac_if.if_collisions; + } + ++sc->arpcom.ac_if.if_oerrors; + outw(BASE + EP_COMMAND, TX_ENABLE); + /* + * To have a tx_avail_int but giving the chance to the + * Reception + */ + if (sc->arpcom.ac_if.if_snd.ifq_head) { + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8); + } } - epstart(ifp); - } - goto checkintr; + outb(BASE + EP_W1_TX_STATUS, 0x0); /* pops up the next + * status */ + } /* while */ + } /* end TX_COMPLETE */ + } + /* re-enable ints */ + outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS); } void epread(sc) - register struct ep_softc *sc; + register struct ep_softc *sc; { - struct ether_header *eh; - struct mbuf *mcur, *m, *m0, *top; - int totlen, lenthisone; - int save_totlen; - u_short etype; - int off, resid; - int count, spinwait; - int i; + struct ether_header *eh; + struct mbuf *top, *mcur, *m; + int lenthisone; - totlen = inw(BASE + EP_W1_RX_STATUS); - off = 0; - top = 0; + short rx_fifo2, status; + register short delta; + register short rx_fifo; + u_short etype; - if (totlen & ERR_RX) { - ++sc->arpcom.ac_if.if_ierrors; + /* + * XXX I have just adapted the code for the protocol trailing processing + * as programed in the original driver. FreeBSD 1.1.5.1 release + */ + status = inw(BASE + EP_W1_RX_STATUS); + +read_again: + + if (status & ERR_RX) { + ++sc->arpcom.ac_if.if_ierrors; + if (status & ERR_RX_OVERRUN) { + /* + * we can think the rx latency is actually greather than we + * expect + */ +#ifdef EP_LOCAL_STATS + if (ep_ftst(F_RX_FIRST)) + sc->rx_overrunf++; + else + sc->rx_overrunl++; +#endif + if (sc->rx_latency < ETHERMTU) + sc->rx_latency += 16; + } + goto out; + } + rx_fifo = rx_fifo2 = status & RX_BYTES_MASK; + + if (ep_ftst(F_RX_FIRST)) { + if (m = sc->mb[sc->next_mb]) { + sc->mb[sc->next_mb] = 0; + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + m->m_data = m->m_pktdat; + m->m_flags = M_PKTHDR; + } else { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (!m) goto out; } - save_totlen = totlen &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */ - - m = sc->mb[sc->next_mb]; - sc->mb[sc->next_mb] = 0; - - if (m == 0) { - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == 0) - goto out; - } else { - /* Convert one of our saved mbuf's */ - sc->next_mb = (sc->next_mb + 1) % MAX_MBS; - m->m_data = m->m_pktdat; - m->m_flags = M_PKTHDR; - } - - top = m0 = m; /* We assign top so we can "goto out" */ + sc->top = sc->mcur = top = m; #define EROUND ((sizeof(struct ether_header) + 3) & ~3) #define EOFF (EROUND - sizeof(struct ether_header)) - m0->m_data += EOFF; + top->m_data += EOFF; + /* Read what should be the header. */ insw(BASE + EP_W1_RX_PIO_RD_1, - mtod(m0, caddr_t), sizeof(struct ether_header) / 2); - m->m_len = sizeof(struct ether_header); - totlen -= sizeof(struct ether_header); + mtod(top, caddr_t), sizeof(struct ether_header) / 2); + top->m_len = sizeof(struct ether_header); + rx_fifo -= sizeof(struct ether_header); + sc->cur_len = rx_fifo2; + /* - * mostly deal with trailer here. (untested) - * We do this in a couple of parts. First we check for a trailer, if - * we have one we convert the mbuf back to a regular mbuf and set the offset and - * subtract sizeof(struct ether_header) from the pktlen. - * After we've read the packet off the interface (all except for the trailer - * header, we then get a header mbuf, read the trailer into it, and fix up - * the mbuf pointer chain. + * Test for trailers. + * I didn't care if this implementation was OK, I just adapted it to + * have the same behaviour as in the original driver */ - eh = mtod(m, struct ether_header *); - eh->ether_type = etype = ntohs((u_short) eh->ether_type); + eh = mtod(top, struct ether_header *); + etype = eh->ether_type = ntohs((u_short) eh->ether_type); if (etype >= ETHERTYPE_TRAIL && etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { - m->m_data = m->m_dat; /* Convert back to regular mbuf. */ - m->m_flags = 0; /* This sucks but non-trailers are the norm */ - off = (etype - ETHERTYPE_TRAIL) * 512; - if (off >= ETHERMTU) { - m_freem(m); - return; /* sanity */ - } - totlen -= sizeof(struct ether_header); /* We don't read the trailer */ - m->m_data += 2 * sizeof(u_short); /* Get rid of type & len */ + if ((etype - ETHERTYPE_TRAIL) * 512 >= ETHERMTU) + goto out; + m->m_data = m->m_dat; /* Convert back to regular mbuf. */ + m->m_flags = 0; /* This sucks but non-trailers are the norm */ + m->m_data += 2 * sizeof(u_short); /* Get rid of type & len */ + + sc->cur_len = sizeof(struct ether_header); + ep_fset(F_RX_TRAILER); + + /* in the case of trailers, we prefer to have the packet complete */ + if (status & ERR_RX_INCOMPLETE) { + ep_frst(F_RX_FIRST); + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | 2032); /* disable */ + return; + } else + /* We don't read the trailer, next we are reading the data */ + rx_fifo -= sizeof(struct ether_header); } - while (totlen > 0) { - lenthisone = min(totlen, M_TRAILINGSPACE(m)); - if (lenthisone == 0) { /* no room in this one */ - mcur = m; - m = sc->mb[sc->next_mb]; - sc->mb[sc->next_mb] = 0; - if (!m) { - MGET(m, M_DONTWAIT, MT_DATA); - if (m == 0) - goto out; - } else { - timeout(epmbuffill, (caddr_t)sc, 0); - sc->next_mb = (sc->next_mb + 1) % MAX_MBS; - } - if (totlen >= MINCLSIZE) - MCLGET(m, M_DONTWAIT); - m->m_len = 0; - mcur->m_next = m; - lenthisone = min(totlen, M_TRAILINGSPACE(m)); - } - if (sc->bus32bit) { - if(totlen > 3) { - lenthisone &= ~3; - insl(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone / 4); - } else - insb(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone); - } else { - if (totlen > 1) { - lenthisone &= ~1; - insw(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone / 2); - } else - *(mtod(m, caddr_t) + m->m_len) = - inb(BASE + EP_W1_RX_PIO_RD_1); - } - m->m_len += lenthisone; - totlen -= lenthisone; - } - if (off) { - top = sc->mb[sc->next_mb]; + } else { + /* come here if we didn't have a complete packet last time */ + top = sc->top; + m = sc->mcur; + sc->cur_len += rx_fifo2; + if (ep_ftst(F_RX_TRAILER)) + /* We don't read the trailer */ + rx_fifo -= sizeof(struct ether_header); + } + + /* Reads what is left in the RX FIFO */ + while (rx_fifo > 0) { + lenthisone = min(rx_fifo, M_TRAILINGSPACE(m)); + if (lenthisone == 0) { /* no room in this one */ + mcur = m; + if (m = sc->mb[sc->next_mb]) { sc->mb[sc->next_mb] = 0; - if (top == 0) { - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (top == 0) { - top = m0; - goto out; - } - } else { - /* Convert one of our saved mbuf's */ - sc->next_mb = (sc->next_mb + 1) % MAX_MBS; - top->m_data = top->m_pktdat; - top->m_flags = M_PKTHDR; - } - insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t), - sizeof(struct ether_header)); - top->m_next = m0; - top->m_len = sizeof(struct ether_header); - /* XXX Accomodate for type and len from beginning of trailer */ - top->m_pkthdr.len = save_totlen - (2 * sizeof(u_short)); + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + } else { + MGET(m, M_DONTWAIT, MT_DATA); + if (!m) + goto out; + } + + if (rx_fifo >= MINCLSIZE) + MCLGET(m, M_DONTWAIT); + m->m_len = 0; + mcur->m_next = m; + lenthisone = min(rx_fifo, M_TRAILINGSPACE(m)); + } + if (ep_ftst(F_ACCESS_32_BITS)) { /* default for EISA configured cards*/ + insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 4); + m->m_len += (lenthisone & ~3); + if (lenthisone & 3) + insb(BASE + EP_W1_RX_PIO_RD_1, + mtod(m, caddr_t) + m->m_len, + lenthisone & 3); + m->m_len += (lenthisone & 3); } else { - top = m0; - top->m_pkthdr.len = save_totlen; + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 2); + m->m_len += lenthisone; + if (lenthisone & 1) + *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); } + rx_fifo -= lenthisone; + } - top->m_pkthdr.rcvif = &sc->arpcom.ac_if; - outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) - ; - ++sc->arpcom.ac_if.if_ipackets; -#if NBPFILTER > 0 - if (sc->bpf) { - bpf_mtap(sc->bpf, top); - - /* - * 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->arpcom.ac_if.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(top); - return; - } + if (ep_ftst(F_RX_TRAILER)) {/* reads the trailer */ + if (m = sc->mb[sc->next_mb]) { + sc->mb[sc->next_mb] = 0; + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + m->m_data = m->m_pktdat; + m->m_flags = M_PKTHDR; + } else { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (!m) + goto out; } + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t), + sizeof(struct ether_header)); + m->m_len = sizeof(struct ether_header); + m->m_next = top; + sc->top = top = m; + /* XXX Accomodate for type and len from beginning of trailer */ + sc->cur_len -= (2 * sizeof(u_short)); + ep_frst(F_RX_TRAILER); + goto all_pkt; + } + + if (status & ERR_RX_INCOMPLETE) { /* we haven't received the complete + * packet */ + sc->mcur = m; +#ifdef EP_LOCAL_STATS + sc->rx_no_first++; /* to know how often we come here */ #endif - m_adj(top, sizeof(struct ether_header)); - ether_input(&sc->arpcom.ac_if, eh, top); + /* + * Re-compute rx_latency, the factor used is 1/4 to go up and 1/32 to + * go down + */ + delta = rx_fifo2 - sc->rx_early_thresh; /* last latency seen LLS */ + delta -= sc->rx_latency;/* LLS - estimated_latency */ + if (delta >= 0) + sc->rx_latency += (delta / 4); + else + sc->rx_latency += (delta / 32); + ep_frst(F_RX_FIRST); + if (!((status = inw(BASE + EP_W1_RX_STATUS)) & ERR_RX_INCOMPLETE)) { + /* we see if by now, the packet has completly arrived */ + goto read_again; + } + /* compute rx_early_threshold */ + delta = (sc->rx_avg_pkt - sc->cur_len - sc->rx_latency - 16) & ~3; + if (delta < MIN_RX_EARLY_THRESHL) + delta = MIN_RX_EARLY_THRESHL; + + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | + (sc->rx_early_thresh = delta)); return; + } +all_pkt: + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + /* + * recompute average packet's length, the factor used is 1/8 to go down + * and 1/32 to go up + */ + delta = sc->cur_len - sc->rx_avg_pkt; + if (delta > 0) + sc->rx_avg_pkt += (delta / 32); + else + sc->rx_avg_pkt += (delta / 8); + delta = (sc->rx_avg_pkt - sc->rx_latency - 16) & ~3; + if (delta < MIN_RX_EARLY_THRESHF) + delta = MIN_RX_EARLY_THRESHF; + sc->rx_early_thresh = delta; + ++sc->arpcom.ac_if.if_ipackets; + ep_fset(F_RX_FIRST); + ep_frst(F_RX_TRAILER); + top->m_pkthdr.rcvif = &sc->arpcom.ac_if; + top->m_pkthdr.len = sc->cur_len; -out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) - ; - if (top) - m_freem(top); +#if NBPFILTER > 0 + if (sc->bpf) { + bpf_mtap(sc->bpf, top); + /* + * 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. + */ + eh = mtod(top, struct ether_header *); + if ((sc->arpcom.ac_if.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) { + if (sc->top) { + m_freem(sc->top); + sc->top = 0; + } + ep_fset(F_RX_FIRST); + ep_frst(F_RX_TRAILER); +#ifdef EP_LOCAL_STATS + sc->rx_bpf_disc++; +#endif + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | delta); + return; + } + } +#endif + + eh = mtod(top, struct ether_header *); + m_adj(top, sizeof(struct ether_header)); + ether_input(&sc->arpcom.ac_if, eh, top); + if (!sc->mb[sc->next_mb]) + epmbuffill((caddr_t) sc, 0); + sc->top = 0; + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | delta); + return; + +out: + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + if (sc->top) { + m_freem(sc->top); + sc->top = 0; +#ifdef EP_LOCAL_STATS + sc->rx_no_mbuf++; +#endif + } + delta = (sc->rx_avg_pkt - sc->rx_latency - 16) & ~3; + if (delta < MIN_RX_EARLY_THRESHF) + delta = MIN_RX_EARLY_THRESHF; + ep_fset(F_RX_FIRST); + ep_frst(F_RX_TRAILER); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | + (sc->rx_early_thresh = delta)); } - /* * Look familiar? */ static int epioctl(ifp, cmd, data) - register struct ifnet *ifp; - int cmd; - caddr_t data; + register struct ifnet *ifp; + int cmd; + caddr_t data; { - register struct ifaddr *ifa = (struct ifaddr *) data; - struct ep_softc *sc = &ep_softc[ifp->if_unit]; - struct ifreq *ifr = (struct ifreq *) data; - int s, error = 0; + register struct ifaddr *ifa = (struct ifaddr *) data; + struct ep_softc *sc = &ep_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *) data; + int s, error = 0; - switch (cmd) { - case SIOCSIFADDR: - ifp->if_flags |= IFF_UP; - switch (ifa->ifa_addr->sa_family) { + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { #ifdef INET - case AF_INET: - epinit(ifp->if_unit); /* before arpwhohas */ - ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; - arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr); - break; + case AF_INET: + epinit(ifp->if_unit); /* before arpwhohas */ + ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr); + break; #endif #ifdef NS - case AF_NS: - { - register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + 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)); - } - epinit(ifp->if_unit); - break; + 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)); } + epinit(ifp->if_unit); + break; + } #endif - default: - epinit(ifp->if_unit); - break; - } - break; - case SIOCSIFFLAGS: - if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { - ifp->if_flags &= ~IFF_RUNNING; - epstop(ifp->if_unit); - epmbufempty(sc); - break; - } - if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0) - epinit(ifp->if_unit); - break; + default: + epinit(ifp->if_unit); + break; + } + break; + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { + ifp->if_flags &= ~IFF_RUNNING; + epstop(ifp->if_unit); + epmbufempty(sc); + break; + } + if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0) + epinit(ifp->if_unit); + break; #ifdef notdef - case SIOCGHWADDR: - bcopy((caddr_t) sc->sc_addr, (caddr_t) &ifr->ifr_data, - sizeof(sc->sc_addr)); - break; + case SIOCGHWADDR: + bcopy((caddr_t) sc->sc_addr, (caddr_t) & ifr->ifr_data, + sizeof(sc->sc_addr)); + break; #endif - case SIOCSIFMTU: + case SIOCSIFMTU: /* * Set the interface MTU. */ - if (ifr->ifr_mtu > ETHERMTU) { - error = EINVAL; + if (ifr->ifr_mtu > ETHERMTU) { + error = EINVAL; } else { - ifp->if_mtu = ifr->ifr_mtu; + ifp->if_mtu = ifr->ifr_mtu; } - break; + break; - default: + default: error = EINVAL; - } - return (error); + } + return (error); } void epreset(unit) - int unit; + int unit; { - int s = splimp(); + int s = splimp(); - epstop(unit); - epinit(unit); - splx(s); + epstop(unit); + epinit(unit); + splx(s); } void epwatchdog(unit) - int unit; + int unit; { - struct ep_softc *sc = &ep_softc[unit]; + struct ep_softc *sc = &ep_softc[unit]; - log(LOG_ERR, "ep%d: watchdog\n", unit); - ++sc->arpcom.ac_if.if_oerrors; + log(LOG_ERR, "ep%d: watchdog\n", unit); + ++sc->arpcom.ac_if.if_oerrors; - epreset(unit); + epreset(unit); } void epstop(unit) - int unit; + int unit; { - struct ep_softc *sc = &ep_softc[unit]; + struct ep_softc *sc = &ep_softc[unit]; - outw(BASE + EP_COMMAND, RX_DISABLE); - outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) - ; - outw(BASE + EP_COMMAND, TX_DISABLE); - outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); - outw(BASE + EP_COMMAND, RX_RESET); - outw(BASE + EP_COMMAND, TX_RESET); - outw(BASE + EP_COMMAND, C_INTR_LATCH); - outw(BASE + EP_COMMAND, SET_RD_0_MASK); - outw(BASE + EP_COMMAND, SET_INTR_MASK); - outw(BASE + EP_COMMAND, SET_RX_FILTER); + outw(BASE + EP_COMMAND, RX_DISABLE); + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + outw(BASE + EP_COMMAND, TX_DISABLE); + outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); + outw(BASE + EP_COMMAND, RX_RESET); + outw(BASE + EP_COMMAND, TX_RESET); + outw(BASE + EP_COMMAND, C_INTR_LATCH); + outw(BASE + EP_COMMAND, SET_RD_0_MASK); + outw(BASE + EP_COMMAND, SET_INTR_MASK); + outw(BASE + EP_COMMAND, SET_RX_FILTER); } -/* - * This is adapted straight from the book. There's probably a better way. - */ static int send_ID_sequence(port) - u_short port; + int port; { - char cx, al; + int cx, al; - cx = 0x0ff; - al = 0x0ff; - - outb(port, 0x0); - DELAY(1000); - outb(port, 0x0); - DELAY(1000); - -loop1: cx--; + for (al = 0xff, cx = 0; cx < 255; cx++) { outb(port, al); - if (!(al & 0x80)) { - al = al << 1; - goto loop1; - } - al = al << 1; - al ^= 0xcf; - if (cx) - goto loop1; - - return(1); + al <<= 1; + if (al & 0x100) + al ^= 0xcf; + } + return (1); } /* - * We get eeprom data from the id_port given an offset into the - * eeprom. Basically; after the ID_sequence is sent to all of - * the cards; they enter the ID_CMD state where they will accept - * command requests. 0x80-0xbf loads the eeprom data. We then - * read the port 16 times and with every read; the cards check - * for contention (ie: if one card writes a 0 bit and another - * writes a 1 bit then the host sees a 0. At the end of the cycle; - * each card compares the data on the bus; if there is a difference - * then that card goes into ID_WAIT state again). In the meantime; - * one bit of data is returned in the AX register which is conveniently - * returned to us by inb(). Hence; we read 16 times getting one - * bit of data with each read. + * We get eeprom data from the id_port given an offset into the eeprom. + * Basically; after the ID_sequence is sent to all of the cards; they enter + * the ID_CMD state where they will accept command requests. 0x80-0xbf loads + * the eeprom data. We then read the port 16 times and with every read; the + * cards check for contention (ie: if one card writes a 0 bit and another + * writes a 1 bit then the host sees a 0. At the end of the cycle; each card + * compares the data on the bus; if there is a difference then that card goes + * into ID_WAIT state again). In the meantime; one bit of data is returned in + * the AX register which is conveniently returned to us by inb(). Hence; we + * read 16 times getting one bit of data with each read. */ -static u_short -epreadeeprom(id_port, offset) - int id_port; - int offset; -{ - int i, data = 0; - outb(id_port, 0x80 + offset); - DELAY(1000); - for (i = 0; i < 16; i++) - data = (data << 1) | (inw(id_port) & 1); - return (data); -} - static int -epbusyeeprom(unit, base) - int unit; ushort base; +get_eeprom_data(id_port, offset) + int id_port; + int offset; { - int i = 0, j; - - while (i++ < 100) { - j = inw(base + EP_W0_EEPROM_COMMAND); - if (j & EEPROM_BUSY) - DELAY(100); - else - break; - } - if (i >= 100) { - printf("\nep%d: eeprom failed to come ready.\n", unit); - return (1); - } - if (j & EEPROM_TST_MODE) { - printf("\nep%d: 3c509 in test mode. Erase pencil mark!\n", unit); - return (1); - } - return (0); + int i, data = 0; + outb(id_port, 0x80 + offset); + DELAY(1000); + for (i = 0; i < 16; i++) + data = (data << 1) | (inw(id_port) & 1); + return (data); } -void -epmbuffill(sp) - void *sp; +/* + * We suppose this is always called inside a splimp(){...}splx() region + */ +static void +epmbuffill(sp, dummy_arg) + caddr_t sp; + int dummy_arg; { - struct ep_softc *sc = (struct ep_softc *)sp; - int s, i; + struct ep_softc *sc = (struct ep_softc *) sp; + int i; - s = splimp(); - i = sc->last_mb; - do { - if(sc->mb[i] == NULL) - MGET(sc->mb[i], M_DONTWAIT, MT_DATA); - if(sc->mb[i] == NULL) - break; - i = (i + 1) % MAX_MBS; - } while (i != sc->next_mb); - sc->last_mb = i; - splx(s); + i = sc->last_mb; + do { + if (sc->mb[i] == NULL) + MGET(sc->mb[i], M_DONTWAIT, MT_DATA); + if (sc->mb[i] == NULL) + break; + i = (i + 1) % MAX_MBS; + } while (i != sc->next_mb); + sc->last_mb = i; } static void epmbufempty(sc) - struct ep_softc *sc; + struct ep_softc *sc; { - int s, i; + int s, i; - s = splimp(); - for (i = 0; imb[i]) { - m_freem(sc->mb[i]); - sc->mb[i] = NULL; - } + s = splimp(); + for (i = 0; i < MAX_MBS; i++) { + if (sc->mb[i]) { + m_freem(sc->mb[i]); + sc->mb[i] = NULL; } - sc->last_mb = sc->next_mb = 0; - untimeout(epmbuffill, sc); - splx(s); + } + sc->last_mb = sc->next_mb = 0; + splx(s); } -#endif /* NEP > 0 */ +#endif /* NEP > 0 */ diff --git a/sys/dev/ep/if_epreg.h b/sys/dev/ep/if_epreg.h index 2b2bcb95744..2dc7a984fa7 100644 --- a/sys/dev/ep/if_epreg.h +++ b/sys/dev/ep/if_epreg.h @@ -1,30 +1,119 @@ /* - * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) - * All rights reserved. - * + * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) 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. The name of the author may not be used to endorse or promote products - * derived from this software withough 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 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. - * - * $Id: if_epreg.h,v 1.2 1994/01/10 19:13:50 ats Exp $ + * 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. The name + * of the author may not be used to endorse or promote products derived from + * this software withough 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 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. + * + * $Id: if_epreg.h,v 1.2 1994/01/10 19:13:50 ats Exp $ Modified by: + * + October 2, 1994 + + Modified by: Andres Vega Garcia + + INRIA - Sophia Antipolis, France + e-mail: avega@sophia.inria.fr + finger: avega@pax.inria.fr + */ + +/* + * Ethernet software status per interface. + */ +struct ep_softc { + struct arpcom arpcom; /* Ethernet common part */ + short ep_io_addr; /* i/o bus address */ +#define MAX_MBS 8 /* # of mbufs we keep around */ + struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */ + int next_mb; /* Which mbuf to use next. */ + int last_mb; /* Last mbuf. */ + struct mbuf *top, *mcur; + short tx_start_thresh; /* Current TX_start_thresh. */ + short tx_rate; + short tx_counter; + short rx_early_thresh; /* Current RX_early_thresh. */ + short rx_latency; + short rx_avg_pkt; + short cur_len; + caddr_t bpf; /* BPF "magic cookie" */ + u_short ep_connectors; /* Connectors on this card. */ + int stat; /* some flags */ +#define F_RX_FIRST 0x1 +#define F_WAIT_TRAIL 0x2 +#define F_RX_TRAILER 0x4 + +#define F_ACCESS_32_BITS 0x100 + +#ifdef EP_LOCAL_STATS + short tx_underrun; + short rx_no_first; + short rx_no_mbuf; + short rx_bpf_disc; + short rx_overrunf; + short rx_overrunl; +#endif +}; + +/* + * Some global constants + */ +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 +#define ETHER_ADDR_LEN 6 + +#define TX_INIT_RATE 16 +#define TX_INIT_MAX_RATE 64 +#define RX_INIT_LATENCY 64 +#define RX_INIT_EARLY_THRESH 64 +#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */ +#define MIN_RX_EARLY_THRESHL 4 + +#define EEPROMSIZE 0x40 +#define MAX_EEPROMBUSY 1000 +#define EP_LAST_TAG 0xd7 +#define EP_MAX_BOARDS 16 +#define EP_ID_PORT 0x100 + +/* + * some macros to acces long named fields + */ +#define IS_BASE (is->id_iobase) +#define BASE (sc->ep_io_addr) + +/* + * Commands to read/write EEPROM trough EEPROM command register (Window 0, + * Offset 0xa) + */ +#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */ +#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */ +#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */ +#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */ + +#define EEPROM_BUSY (1<<15) +#define EEPROM_TST_MODE (1<<14) + +/* + * Some short functions, worth to let them be a macro + */ +#define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY) +#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|(x)) + /************************************************************************** - * * + * * * These define the EEPROM data structure. They are used in the probe * function to verify the existance of the adapter after having sent * the ID_Sequence. @@ -39,7 +128,7 @@ #define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */ #define EEPROM_MFG_ID 0x7 /* 0x6d50 */ #define EEPROM_ADDR_CFG 0x8 /* Base addr */ -#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ +#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ /************************************************************************** * * @@ -50,32 +139,35 @@ * * **************************************************************************/ -#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a command reg. */ -#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status reg. */ -#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window reg. */ +#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a + * command reg. */ +#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status + * reg. */ +#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window + * reg. */ /* * Window 0 registers. Setup. */ - /* Write */ +/* Write */ #define EP_W0_EEPROM_DATA 0x0c #define EP_W0_EEPROM_COMMAND 0x0a #define EP_W0_RESOURCE_CFG 0x08 #define EP_W0_ADDRESS_CFG 0x06 #define EP_W0_CONFIG_CTRL 0x04 - /* Read */ +/* Read */ #define EP_W0_PRODUCT_ID 0x02 #define EP_W0_MFG_ID 0x00 /* * Window 1 registers. Operating Set. */ - /* Write */ +/* Write */ #define EP_W1_TX_PIO_WR_2 0x02 #define EP_W1_TX_PIO_WR_1 0x00 - /* Read */ +/* Read */ #define EP_W1_FREE_TX 0x0c -#define EP_W1_TX_STATUS 0x0b /* byte */ -#define EP_W1_TIMER 0x0a /* byte */ +#define EP_W1_TX_STATUS 0x0b /* byte */ +#define EP_W1_TIMER 0x0a /* byte */ #define EP_W1_RX_STATUS 0x08 #define EP_W1_RX_PIO_RD_2 0x02 #define EP_W1_RX_PIO_RD_1 0x00 @@ -83,7 +175,7 @@ /* * Window 2 registers. Station Address Setup/Read */ - /* Read/Write */ +/* Read/Write */ #define EP_W2_ADDR_5 0x05 #define EP_W2_ADDR_4 0x04 #define EP_W2_ADDR_3 0x03 @@ -91,17 +183,17 @@ #define EP_W2_ADDR_1 0x01 #define EP_W2_ADDR_0 0x00 -/* +/* * Window 3 registers. FIFO Management. */ - /* Read */ +/* Read */ #define EP_W3_FREE_TX 0x0c #define EP_W3_FREE_RX 0x0a /* * Window 4 registers. Diagnostics. */ - /* Read/Write */ +/* Read/Write */ #define EP_W4_MEDIA_TYPE 0x0a #define EP_W4_CTRLR_STATUS 0x08 #define EP_W4_NET_DIAG 0x06 @@ -112,7 +204,7 @@ /* * Window 5 Registers. Results and Internal status. */ - /* Read */ +/* Read */ #define EP_W5_READ_0_MASK 0x0c #define EP_W5_INTR_MASK 0x0a #define EP_W5_RX_FILTER 0x08 @@ -123,7 +215,7 @@ /* * Window 6 registers. Statistics. */ - /* Read/Write */ +/* Read/Write */ #define TX_TOTAL_OK 0x0c #define RX_TOTAL_OK 0x0a #define TX_DEFERRALS 0x08 @@ -150,13 +242,17 @@ * 10-0: 11-bit arg if any. For commands with no args; * this can be set to anything. */ -#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms after issuing */ +#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms + * after issuing */ #define WINDOW_SELECT (u_short) (0x1<<11) -#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to determine - whether this is needed. If so; - wait 800 uSec before using trans- - ceiver. */ -#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on power-up */ +#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to + * determine whether + * this is needed. If + * so; wait 800 uSec + * before using trans- + * ceiver. */ +#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on + * power-up */ #define RX_ENABLE (u_short) (0x4<<11) #define RX_RESET (u_short) (0x5<<11) #define RX_DISCARD_TOP_PACK (u_short) (0x8<<11) @@ -164,32 +260,32 @@ #define TX_DISABLE (u_short) (0xa<<11) #define TX_RESET (u_short) (0xb<<11) #define REQ_INTR (u_short) (0xc<<11) - /* - * The following C_* acknowledge the various interrupts. - * Some of them don't do anything. See the manual. - */ -#define ACK_INTR (u_short) (0x6800) -# define C_INTR_LATCH (u_short) (ACK_INTR|0x1) -# define C_CARD_FAILURE (u_short) (ACK_INTR|0x2) -# define C_TX_COMPLETE (u_short) (ACK_INTR|0x4) -# define C_TX_AVAIL (u_short) (ACK_INTR|0x8) -# define C_RX_COMPLETE (u_short) (ACK_INTR|0x10) -# define C_RX_EARLY (u_short) (ACK_INTR|0x20) -# define C_INT_RQD (u_short) (ACK_INTR|0x40) -# define C_UPD_STATS (u_short) (ACK_INTR|0x80) #define SET_INTR_MASK (u_short) (0xe<<11) #define SET_RD_0_MASK (u_short) (0xf<<11) #define SET_RX_FILTER (u_short) (0x10<<11) -# define FIL_INDIVIDUAL (u_short) (0x1) -# define FIL_MULTICAST (u_short) (0x2) -# define FIL_BRDCST (u_short) (0x4) -# define FIL_PROMISC (u_short) (0x8) +#define FIL_INDIVIDUAL (u_short) (0x1) +#define FIL_GROUP (u_short) (0x2) +#define FIL_BRDCST (u_short) (0x4) +#define FIL_ALL (u_short) (0x8) #define SET_RX_EARLY_THRESH (u_short) (0x11<<11) #define SET_TX_AVAIL_THRESH (u_short) (0x12<<11) #define SET_TX_START_THRESH (u_short) (0x13<<11) #define STATS_ENABLE (u_short) (0x15<<11) #define STATS_DISABLE (u_short) (0x16<<11) #define STOP_TRANSCEIVER (u_short) (0x17<<11) +/* + * The following C_* acknowledge the various interrupts. Some of them don't + * do anything. See the manual. + */ +#define ACK_INTR (u_short) (0x6800) +#define C_INTR_LATCH (u_short) (ACK_INTR|0x1) +#define C_CARD_FAILURE (u_short) (ACK_INTR|0x2) +#define C_TX_COMPLETE (u_short) (ACK_INTR|0x4) +#define C_TX_AVAIL (u_short) (ACK_INTR|0x8) +#define C_RX_COMPLETE (u_short) (ACK_INTR|0x10) +#define C_RX_EARLY (u_short) (ACK_INTR|0x20) +#define C_INT_RQD (u_short) (ACK_INTR|0x40) +#define C_UPD_STATS (u_short) (ACK_INTR|0x80) /* * Status register. All windows. @@ -217,10 +313,13 @@ #define S_RX_EARLY (u_short) (0x20) #define S_INT_RQD (u_short) (0x40) #define S_UPD_STATS (u_short) (0x80) +#define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\ + S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY) #define S_COMMAND_IN_PROGRESS (u_short) (0x1000) /* - * FIFO Registers. RX Status. + * FIFO Registers. + * RX Status. Window 1/Port 08 * * 15: Incomplete or FIFO empty. * 14: 1: Error in RX Packet 0: Incomplete or no error. @@ -235,18 +334,18 @@ * * 10-0: RX Bytes (0-1514) */ -#define ERR_INCOMPLETE (u_short) (0x8000) -#define ERR_RX (u_short) (0x4000) -#define ERR_RX_PACKET (u_short) (0x2000) -#define ERR_OVERRUN (u_short) (0x1000) -#define ERR_RUNT (u_short) (0x1300) -#define ERR_ALIGNMENT (u_short) (0x1400) -#define ERR_CRC (u_short) (0x1500) -#define ERR_OVERSIZE (u_short) (0x1100) -#define ERR_DRIBBLE (u_short) (0x200) +#define ERR_RX_INCOMPLETE (u_short) (0x1<<15) +#define ERR_RX (u_short) (0x1<<14) +#define ERR_RX_OVERRUN (u_short) (0x8<<11) +#define ERR_RX_RUN_PKT (u_short) (0xb<<11) +#define ERR_RX_ALIGN (u_short) (0xc<<11) +#define ERR_RX_CRC (u_short) (0xd<<11) +#define ERR_RX_OVERSIZE (u_short) (0x9<<11) +#define ERR_RX_DRIBBLE (u_short) (0x2<<11) /* - * TX Status + * FIFO Registers. + * TX Status. Window 1/Port 0B * * Reports the transmit status of a completed transmission. Writing this * register pops the transmit completion stack. @@ -263,44 +362,46 @@ * */ #define TXS_COMPLETE 0x80 -#define TXS_INTR_REQ 0x40 +#define TXS_SUCCES_INTR_REQ 0x40 #define TXS_JABBER 0x20 #define TXS_UNDERRUN 0x10 #define TXS_MAX_COLLISION 0x8 #define TXS_STATUS_OVERFLOW 0x4 /* - * Misc defines for various things. + * Configuration control register. + * Window 0/Port 04 */ -#define TAG_ADAPTER_0 0xd0 -#define ACTIVATE_ADAPTER_TO_CONFIG 0xff -#define ENABLE_DRQ_IRQ 0x0001 -#define MFG_ID 0x6d50 -#define PROD_ID 0x9050 -#define BASE sc->ep_iobase -#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|x) -#define AUI 0x1 -#define BNC 0x2 -#define UTP 0x4 +/* Read */ #define IS_AUI (1<<13) #define IS_BNC (1<<12) #define IS_UTP (1<<9) -#define EEPROM_BUSY (1<<15) -#define EEPROM_TST_MODE (1<<14) -#define READ_EEPROM (1<<7) -#define ETHER_ADDR_LEN 6 -#define ETHER_MAX 1536 +/* Write */ +#define ENABLE_DRQ_IRQ 0x0001 +#define W0_P4_CMD_RESET_ADAPTER 0x4 +#define W0_P4_CMD_ENABLE_ADAPTER 0x1 +/* + * Media type and status. + * Window 4/Port 0A + */ #define ENABLE_UTP 0xc0 #define DISABLE_UTP 0x0 -#define RX_BYTES_MASK (u_short) (0x07ff) - /* - * EISA registers (offset from slot base) + * Misc defines for various things. */ -#define EISA_VENDOR 0x0c80 /* vendor ID (2 ports) */ -#define EISA_MODEL 0x0c82 /* model number (2 ports) */ -#define EISA_CONTROL 0x0c84 -#define EISA_RESET 0x04 -#define EISA_ERROR 0x02 -#define EISA_ENABLE 0x01 +#define ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */ +#define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */ +#define PROD_ID 0x9150 + +#define AUI 0x1 +#define BNC 0x2 +#define UTP 0x4 + +#define ETHER_ADDR_LEN 6 +#define ETHER_MAX 1536 +#define RX_BYTES_MASK (u_short) (0x07ff) + + /* EISA support */ +#define EP_EISA_START 0x1000 +#define EP_EISA_W0 0x0c80 diff --git a/sys/i386/isa/if_ep.c b/sys/i386/isa/if_ep.c index a526ca39a09..95341823f31 100644 --- a/sys/i386/isa/if_ep.c +++ b/sys/i386/isa/if_ep.c @@ -1,28 +1,253 @@ /* - * Copyright (c) 1993 Herb Peyerl - * All rights reserved. - * + * Copyright (c) 1993 Herb Peyerl 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. The name of the author may not 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 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. - * - * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp - * $Id: if_ep.c,v 1.13 1994/09/16 13:33:41 davidg Exp $ + * 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. The name + * of the author may not 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 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. + * + * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp $ $Id: if_ep.c,v 1.9 + * 1994/05/02 22:27:33 ats Exp $ + * + * October 26, 1994 + * + * Modified by: Andres Vega Garcia + * INRIA - Sophia Antipolis, France + * e-mail: avega@sophia.inria.fr + * finger: avega@pax.inria.fr + * + * + * What is new: + * + * 1) We can recognize more than 1 board. + * + * 2) The problem which used to happen with high trafic is corrected, + * (No more need to 'down' and 'up' the interface). + * + * 3) In the transmission, we use the TX start threshold in a more dynamic + * fashion (IMO the throughput is higher this way). + * + * 4) In the reception, we use the RX early threshold, that parameter is + * adapted as the packets arrive (IMO the throughput is higher this way). + * + * 5) Supports EISA cards. + * + * NB 0: The 32 bits acces is allowed for the EISA configured cards, thoung I + * wasn't able to test the code added. + * + * NB 1: I added the option EP_LOCAL_STATS, it can be temporary as IMO is just + * used while working on this driver and the program that displays this + * information (epstat). + * + * NB 2: About trailers, I didn't care if this implementation was OK, I just + * adapted it to have the same behaviour as in the original driver (donne + * just for epread()). + * + * + * Some driver statistics can be viewed with the epstat utility. In order to + * use this, you have to compile if_ep.c with + * + * -DEP_LOCAL_STATS + * + * which can be included in your machine config file (e.g. GENERICAH_EP) + * as an option (option EP_LOCAL_STATS). + * + * + * Modifications since FreeBSD 1.1.5.1 Release: + * + * This explanation concerns the epstart(), epread() and epintr() functions. + * + * ========================================================================= + * epstart() + * ========================================================================= + * + * + * Let's see what the idea is: + * + * + * Packet |------------------ LEN ---------------------| + * + * A + * CPU |---------------|----------------------------| + * + * + * Card |----------------------------| + * + * + * + * We suppose the Card is able to *write* bytes (send them to the media) + * at the speed S_CARD (bytes/s), and that is faster than the speed of the CPU, + * S_CPU, to write bytes to the TX FIFO, then, we have to write A bytes to the + * FIFO before enableing the transmision in the card. This way both, the card + * and the CPU must finish their writing at the same time. + * + * + * Let TX_RATE = S_CPU / S_CARD, where TX_RATE <= 1 + * + * We can find that: + * + * (1) A = LEN * (1 - TX_RATE) + * + * + * Let TX_RATE_R be the *very real* value. + * + * If TX_RATE > TX_RATE_R + * We are supposing the CPU is faster than it really is and + * certainly the card will *finish* before the CPU, having a + * TX Underrun Error, then, in such a case, we have to do: + * + * TX_RATE -= STEP, where STEP is the step at which we + * move TX_RATE + * + * If TX_RATE < TX_RATE_R + * We won't have the TX Underrun Error but it is possible that + * we don't use eficiently the TX START THRESH. feature. + * We prevent this by doing: + * + * TX_RATE += STEP every time we have sent succesfuly + * (without Underrun) a certain number + * of packets. + * + * Now, to avoid dealing with reals I used a FACTOR, then (1) will be + * transformed: + * + * Let tx_rate = FACTOR * TX_RATE, tx_rate is the parameter + * really used. + * + * A = (LEN * (FACTOR - tx_rate)) / FACTOR + * + * Actually FACTOR = 64 + * + * (2) A = (LEN * (64 - tx_rate)) >> 6 + * + * As I want to have some margin, and + * as I have to write a number multiple of 4: + * + * A = tx_start_threshold = (((LEN * (64 - tx_rate)) >> 6) & ~3) + 16 + * + * + * ========================================================================= + * epread() + * ========================================================================= + * + * I mantain an estimation of the RX packet's average length, and an + * estimation of the RX latency. + * + * Every time I receive a complete packet I compute the average packet's + * length, rx_avg_pkt: + * + * DELTA = LEN - rx_avg_pkt, where LEN is this packet's length + * + * if DELTA > 0 + * rx_avg_pkt += AVG_UP * DELTA + * else + * rx_avg_pkt += AVG_DOWN *DELTA + * + * + * AVG_UP < AVG_DOWN + * + * In the first case, I'm interested in being conservative about the + * average packet'length, because if I let it go up *too fast*, a shorter packet + * than expected will probably cause an RX Overrun Error. But if I consider + * the next packet will be smaller than it will, I just will have to wait + * for the packet to complete reception. + * + * In the other case, I'm interested in leting the rx_avg_pkt follow + * the real packet's lenght closer, as it is important not to think the average + * packet is bigger than it realy is. If I don't do that, and the rx_avg_pkt + * goes down *too slow*, I'll find myself thinking the packets are big when they + * are really small and I'll have probably Rx Overrun Errors. + * + * Actualy: + * + * AVG_UP = 1/32 + * AVG_DOWN = 1/8 + * + * + * Every time I receive an incomplete packet I recompute the RX latency + * (rx_latency). + * + * I know that if rx_latency = 0, when I go read the bytes from the RX + * FIFO, I'll find as many bytes as I programmed in the RX Early Threshold, but + * if rx_latency > 0, I'll find more bytes. + * + * Let CUR_LAT be the RX latency seen by this packet. + * + * CUR_LAT = LEN - rx_early_threshold, where LEN is the number of + * bytes I have just received. + * + * DELTA = CUR_LAT - rx_latency + * + * + * if DELTA >= 0 + * rx_latency += LAT_UP * DELTA + * else + * rx_latency += LAT_DOWN * DELTA + * + * + * LAT_UP > LAT_DOWN + * + * In a similar way as for rx average packet's length, I try to be more + * conservative in the more critical case. + * + * In the first case, I have to follow closer the incremets of the RX + * latency, because if I don't, I can find myself thinking that we (CPU) are + * *enough fast* and wait up to the last minute to go read data to find we have + * had RX Overrun Error. + * + * In the other case I must be more conservative to avoid falling in + * the situation I have just described, because if I go down *to fast* I'll + * think we are enough fast and we'll wake up later than due. + * + * Actually: + * + * LAT_UP = 1/4 + * LAT_DOWN = 1/32 + * + * Finally, I compute the rx_early_threshold for the next packet as: + * + * rx_early_threshold = rx_avg_pkt - rx_latency + * + * But, as I want to have a margin and + * as I have to write a value multiple of 4. + * + * + * rx_early_threshold = (rx_avg_pkt - rx_latency - 16) & ~3 + * + * + * But if I have to wait for the rest of an incomplete packet + * from which I have already received CUR_LEN bytes: + * + * + * rx_early_threshold = (rx_avg_pkt-CUR_LEN - rx_latency - 16) & ~3 + * + * + * ========================================================================= + * epintr() + * ========================================================================= + * + * For this function I just tryed to do what is stated in the + * Etherlink III Technical Reference. + * + * It was here where I really solved the problem that used to happen with + * high traffic. + * + * + * Andres + * avega@pax.inria.fr */ #include "ep.h" @@ -34,13 +259,13 @@ #if defined(__FreeBSD__) #include #include +#include #endif #include #include #include #include #include -#include #if defined(__NetBSD__) #include #endif @@ -71,359 +296,324 @@ #include #include #include -#include - -/* For backwards compatibility */ -#ifndef IFF_ALTPHYS -#define IFF_ALTPHYS IFF_LINK0 -#endif - - -#define ETHER_MIN_LEN 64 -#define ETHER_MAX_LEN 1518 -#define ETHER_ADDR_LEN 6 - -/* - * Ethernet software status per interface. - */ -struct ep_softc { - struct arpcom arpcom; /* Ethernet common part */ - ushort ep_iobase; /* i/o bus address */ - char ep_connectors; /* Connectors on this card. */ -#define MAX_MBS 8 /* # of mbufs we keep around */ - struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */ - int next_mb; /* Which mbuf to use next. */ - int last_mb; /* Last mbuf. */ - int tx_start_thresh; /* Current TX_start_thresh. */ - int tx_succ_ok; /* # packets sent in sequence w/o underrun */ - caddr_t bpf; /* BPF "magic cookie" */ - char bus32bit; /* 32bit access possible */ -} ep_softc[NEP]; static int epprobe __P((struct isa_device *)); static int epattach __P((struct isa_device *)); static int epioctl __P((struct ifnet * ifp, int, caddr_t)); +static void epmbuffill __P((caddr_t, int)); +static void epmbufempty __P((struct ep_softc *)); void epinit __P((int)); void epintr __P((int)); -void epmbuffill __P((void *)); -static void epmbufempty __P((struct ep_softc *)); void epread __P((struct ep_softc *)); void epreset __P((int)); void epstart __P((struct ifnet *)); void epstop __P((int)); void epwatchdog __P((int)); +static int send_ID_sequence __P((int)); +static int get_eeprom_data __P((int, int)); + +struct ep_softc ep_softc[NEP]; + +#define ep_ftst(f) (sc->stat&(f)) +#define ep_fset(f) (sc->stat|=(f)) +#define ep_frst(f) (sc->stat&=~(f)) + struct isa_driver epdriver = { - epprobe, - epattach, - "ep" + epprobe, + epattach, + "ep" }; static struct kern_devconf kdc_ep[NEP] = { { - 0, 0, 0, /* filled in by dev_attach */ - "ep", 0, { MDDT_ISA, 0, "net" }, - isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, - &kdc_isa0, /* parent */ - 0, /* parentdata */ - DC_BUSY, /* network interfaces are always ``open'' */ - "3Com 3C509 Ethernet adapter" + 0, 0, 0, /* filled in by dev_attach */ + "ep", 0, { MDDT_ISA, 0, "net" }, + isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, + &kdc_isa0, /* parent */ + 0, /* parentdata */ + DC_BUSY, /* network interfaces are always ``open'' */ + "3Com 3C509 Ethernet adapter" } }; static inline void ep_registerdev(struct isa_device *id) { - if(id->id_unit) - kdc_ep[id->id_unit] = kdc_ep[0]; - kdc_ep[id->id_unit].kdc_unit = id->id_unit; - kdc_ep[id->id_unit].kdc_parentdata = id; - dev_attach(&kdc_ep[id->id_unit]); + if(id->id_unit) + kdc_ep[id->id_unit] = kdc_ep[0]; + kdc_ep[id->id_unit].kdc_unit = id->id_unit; + kdc_ep[id->id_unit].kdc_parentdata = id; + dev_attach(&kdc_ep[id->id_unit]); } -static int send_ID_sequence __P((u_short)); -static u_short epreadeeprom __P((int, int)); -static int epbusyeeprom __P((int, ushort)); +int ep_current_tag = EP_LAST_TAG + 1; +int ep_board[EP_MAX_BOARDS + 1]; -#define MAXEPCARDS 20 /* if you have 21 cards in your machine... you lose */ - -static struct epcard { - int iobase; - u_short irq; - char available; - char bus32bit; -} epcards[MAXEPCARDS]; - -static int nepcards; - -static void -epaddcard(p, i, mode) - short p; - u_short i; - char mode; +static int +eeprom_rdy(is) + struct isa_device *is; { - if (nepcards >= sizeof(epcards)/sizeof(epcards[0])) - return; - epcards[nepcards].iobase = p; - epcards[nepcards].irq = 1 << ((i == 2) ? 9 : i); - epcards[nepcards].available = 1; - epcards[nepcards].bus32bit = mode; - nepcards++; -} + int i; - -/* - * 3c579 cards on the EISA bus are probed by their slot number. 3c509 - * cards on the ISA bus are probed in ethernet address order. The probe - * sequence requires careful orchestration, and we'd like like to allow - * the irq and base address to be wildcarded. So, we probe all the cards - * the first time epprobe() is called. On subsequent calls we look for - * matching cards. - */ -int -epprobe(is) - struct isa_device *is; -{ - struct ep_softc *sc = &ep_softc[is->id_unit]; - static int probed; - int slot, iobase, i; - u_short k, k2; - u_short prodid; - - if (probed==0) { - probed = 1; - - /* find all EISA cards */ - for (slot = 1; slot < 16; slot++) { - iobase = 0x1000 * slot; - outw(iobase + EP_COMMAND, GLOBAL_RESET); - DELAY(1000); - if (inw(iobase + EISA_VENDOR) != MFG_ID) - continue; - k = inw(iobase + EISA_MODEL); -#ifdef EP_DEBUG -printf("prod id = %x ", k); -prodid = k; -#endif - if ((k & 0xf0ff) != PROD_ID) - continue; - - k = inw(iobase + EP_W0_CONFIG_CTRL); - /* enable adapter */ - outw(iobase + EP_W0_CONFIG_CTRL, k | 1); -#ifdef EP_DEBUG -printf("config = %x ", k); -#endif - - /* read in eeprom address configuration */ - if (epbusyeeprom(slot - 1, iobase)) - continue; - outw(iobase + EP_W0_EEPROM_COMMAND, READ_EEPROM | EEPROM_ADDR_CFG); - if (epbusyeeprom(slot - 1, iobase)) - continue; - k = inw(iobase + EP_W0_EEPROM_DATA); -#ifdef EP_DEBUG -printf("addr_cfg = %x ", k); -#endif - outw(iobase + EP_W0_ADDRESS_CFG, k); - /* read in eeprom resource configuration */ - if (epbusyeeprom(slot - 1, iobase)) - continue; - outw(iobase + EP_W0_EEPROM_COMMAND, READ_EEPROM | EEPROM_RESOURCE_CFG); - if (epbusyeeprom(slot - 1, iobase)) - continue; - k2 = inw(iobase + EP_W0_EEPROM_DATA); - -#ifdef EP_DEBUG -/** XXXXXXXXXXXXXXXXXXXXX*/ -/* This doesn't give back the actual IRQ number as it should be , ATS */ -/* In the moment simply hardcoded the IRQ's for testing purposes */ -printf("resource config = %x\n", k2); -if (prodid == 0x9150) /* the 3c509 card */ - k2 = 7 << 12; -else - k2 = 3 << 12; /* the eisa 3c579 card set to irq 3 */ -#endif - - outw(iobase + EP_W0_RESOURCE_CFG, k2); - epaddcard(iobase, k2 >> 12, 1); - } - - /* find all isa cards */ -#ifdef 0 - outw(BASE + EP_COMMAND, GLOBAL_RESET); -#endif - DELAY(1000); - elink_reset(); /* global reset to ELINK_ID_PORT */ - DELAY(1000); - - for (slot = 0; slot < 10; slot++) { - outb(ELINK_ID_PORT, 0x00); - elink_idseq(ELINK_509_POLY); - DELAY(1000); - - k = epreadeeprom(ELINK_ID_PORT, EEPROM_MFG_ID); - if (k != MFG_ID) - continue; - k = epreadeeprom(ELINK_ID_PORT, EEPROM_PROD_ID); - if ((k & 0xf0ff) != PROD_ID) - continue; - - k = epreadeeprom(ELINK_ID_PORT, EEPROM_ADDR_CFG); - k = (k & 0x1f) * 0x10 + 0x200; - - k2 = epreadeeprom(ELINK_ID_PORT, EEPROM_RESOURCE_CFG); - k2 >>= 12; - epaddcard(k, k2, 0); - - /* so card will not respond to contention again */ - outb(ELINK_ID_PORT, TAG_ADAPTER_0 + 1); - - /* - * XXX: this should probably not be done here - * because it enables the drq/irq lines from - * the board. Perhaps it should be done after - * we have checked for irq/drq collisions? - */ - outb(ELINK_ID_PORT, ACTIVATE_ADAPTER_TO_CONFIG); - } - /* XXX should we sort by ethernet address? */ - } - - /* - * a very specific search order: - * exact iobase & irq - * exact iobase, wildcard irq - * wildcard iobase, exact irq - * wildcard iobase & irq - * else fail.. - */ - if (is->id_iobase != 0 && is->id_irq != (u_short)0) { - for (i = 0; iid_iobase == epcards[i].iobase && - is->id_irq == epcards[i].irq) - goto good; - } - } - if (is->id_iobase != 0 && is->id_irq == (u_short)0) { - for (i = 0; iid_iobase == epcards[i].iobase) - goto good; - } - } - if (is->id_iobase == 0 && is->id_irq != (u_short)0) { - for (i = 0; iid_irq == epcards[i].irq) - goto good; - } - } - return 0; - -good: - epcards[i].available = 0; - sc->bus32bit = epcards[i].bus32bit; - is->id_iobase = epcards[i].iobase; - - return (0x10); /* 16 bytes of I/O space used. */ + for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++); + if (i >= MAX_EEPROMBUSY) { + printf("ep%d: eeprom failed to come ready.\n", is->id_unit); + return (0); + } + return (1); } static int -epattach(is) - struct isa_device *is; +ep_look_for_board_at(is) + struct isa_device *is; { - struct ep_softc *sc = &ep_softc[is->id_unit]; - struct ifnet *ifp = &sc->arpcom.ac_if; - u_short i; - struct ifaddr *ifa; - struct sockaddr_dl *sdl; + int data, i, j, io_base, id_port = EP_ID_PORT; + int nisa = 0, neisa = 0; - sc->ep_iobase = is->id_iobase; + if (ep_current_tag == (EP_LAST_TAG + 1)) { + /* Come here just one time */ + + /* Look for the EISA boards, leave them activated */ + for(j = 1; j < 16; j++) { + io_base = (j * EP_EISA_START) | EP_EISA_W0; + if (inw(io_base + EP_W0_MFG_ID) != MFG_ID) + continue; - printf("ep%d: ", is->id_unit); + /* we must found 0x1f if the board is EISA configurated */ + if ((inw(io_base + EP_W0_ADDRESS_CFG) & 0x1f) != 0x1f) + continue; - sc->ep_connectors = 0; - i = inw(is->id_iobase + EP_W0_CONFIG_CTRL); - if (i & IS_AUI) { - printf("aui"); - sc->ep_connectors |= AUI; + /* Reset and Enable the card */ + outb(io_base + EP_W0_CONFIG_CTRL, W0_P4_CMD_RESET_ADAPTER); + DELAY(1000); /* we must wait at least 1 ms */ + outb(io_base + EP_W0_CONFIG_CTRL, W0_P4_CMD_ENABLE_ADAPTER); + + /* + * Once activated, all the registers are mapped in the range + * x000 - x00F, where x is the slot number. + */ + ep_board[neisa++] = j * EP_EISA_START; } - if (i & IS_BNC) { - if (sc->ep_connectors) - printf("/"); - printf("bnc"); - sc->ep_connectors |= BNC; + ep_current_tag--; + + /* Look for the ISA boards. Init and leave them actived */ + outb(id_port, 0xc0); /* Global reset */ + DELAY(1000); + for (i = 0; i < EP_MAX_BOARDS; i++) { + outb(id_port, 0); + outb(id_port, 0); + send_ID_sequence(id_port); + + data = get_eeprom_data(id_port, EEPROM_MFG_ID); + if (data != MFG_ID) + break; + + /* resolve contention using the Ethernet address */ + for (j = 0; j < 3; j++) + data = get_eeprom_data(id_port, j); + + ep_board[neisa+nisa++] = + (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x10 + 0x200; + outb(id_port, ep_current_tag); /* tags board */ + outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG); + ep_current_tag--; } - if (i & IS_UTP) { - if (sc->ep_connectors) - printf("/"); - printf("utp"); - sc->ep_connectors |= UTP; + + ep_board[neisa+nisa] = 0; + if (neisa) { + printf("%d 3C5x9 board(s) on EISA found at", neisa); + for (j = 0; ep_board[j]; j++) + if (ep_board[j] >= EP_EISA_START) + printf(" 0x%x", ep_board[j]); + printf("\n"); } - if (!sc->ep_connectors) - printf("no connectors!"); - - /* - * Read the station address from the eeprom - */ - for (i = 0; i < 3; i++) { - u_short *p; - GO_WINDOW(0); - if (epbusyeeprom(is->id_unit, sc->ep_iobase)) - return(0); - outw(BASE + EP_W0_EEPROM_COMMAND, READ_EEPROM | i); - if (epbusyeeprom(is->id_unit, sc->ep_iobase)) - return(0); - p =(u_short *)&sc->arpcom.ac_enaddr[i*2]; - *p = htons(inw(BASE + EP_W0_EEPROM_DATA)); - GO_WINDOW(2); - outw(BASE + EP_W2_ADDR_0 + (i * 2), ntohs(*p)); + if (nisa) { + printf("%d 3C5x9 board(s) on ISA found at", nisa); + for (j = 0; ep_board[j]; j++) + if (ep_board[j] < EP_EISA_START) + printf(" 0x%x", ep_board[j]); + printf("\n"); } - printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr)); + } - ifp->if_unit = is->id_unit; - ifp->if_name = "ep"; - ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | - IFF_MULTICAST ; - ifp->if_init = epinit; - ifp->if_output = ether_output; - ifp->if_start = epstart; - ifp->if_ioctl = epioctl; - ifp->if_watchdog = epwatchdog; + for (i = 0; ep_board[i] && ep_board[i] != IS_BASE; i++); + if (ep_board[i] == IS_BASE) { + if (inw(IS_BASE + EP_W0_EEPROM_COMMAND) & EEPROM_TST_MODE) + printf("ep%d: 3c5x9 at 0x%x in test mode. Erase pencil mark!\n", + is->id_unit, IS_BASE); + return (1); + } + return (0); +} - if_attach(ifp); - ep_registerdev(is); +/* + * get_e: gets a 16 bits word from the EEPROM. we must have set the window + * before + */ +static int +get_e(is, offset) + struct isa_device *is; + int offset; +{ + if (!eeprom_rdy(is)) + return (0xffff); + outw(IS_BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset); + if (!eeprom_rdy(is)) + return (0xffff); + return (inw(IS_BASE + EP_W0_EEPROM_DATA)); +} - /* - * Fill the hardware address into ifa_addr if we find an - * AF_LINK entry. We need to do this so bpf's can get the hardware - * addr of this card. netstat likes this too! - */ - ifa = ifp->if_addrlist; - while ((ifa != 0) && (ifa->ifa_addr != 0) && - (ifa->ifa_addr->sa_family != AF_LINK)) - ifa = ifa->ifa_next; +int +epprobe(is) + struct isa_device *is; +{ + struct ep_softc *sc = &ep_softc[is->id_unit]; + u_short k; + int i; - if ((ifa != 0) && (ifa->ifa_addr != 0)) { - sdl = (struct sockaddr_dl *) ifa->ifa_addr; - sdl->sdl_type = IFT_ETHER; - sdl->sdl_alen = ETHER_ADDR_LEN; - sdl->sdl_slen = 0; - bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); - } -#if NBPFILTER > 0 - bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); + if (!ep_look_for_board_at(is)) + return (0); + /* + * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be + * 0x9[0-f]50 + */ + GO_WINDOW(0); + k = get_e(is, EEPROM_PROD_ID); + if ((k & 0xf0ff) != (PROD_ID & 0xf0ff)) { + printf("epprobe: ignoring model %04x\n", k); + return (0); + } + + k = get_e(is, EEPROM_RESOURCE_CFG); + k >>= 12; + if (is->id_irq != (1 << ((k == 2) ? 9 : k))) { + printf("epprobe: interrupt number %d doesn't match\n",is->id_irq); + return (0); + } + + if (BASE >= EP_EISA_START) /* we have an EISA board, we allow 32 bits access */ + sc->stat = F_ACCESS_32_BITS; + else + sc->stat = 0; + + /* By now, the adapter is already activated */ + + return (0x10); /* 16 bytes of I/O space used. */ +} + +static char *ep_conn_type[] = {"UTP", "AUI", "???", "BNC"}; + +static int +epattach(is) + struct isa_device *is; +{ + struct ep_softc *sc = &ep_softc[is->id_unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + u_short i, j, *p; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + + /* BASE = IS_BASE; */ + sc->ep_io_addr = is->id_iobase; + + printf("ep%d: ", is->id_unit); + + sc->ep_connectors = 0; + i = inw(IS_BASE + EP_W0_CONFIG_CTRL); + j = inw(IS_BASE + EP_W0_ADDRESS_CFG) >> 14; + if (i & IS_AUI) { + printf("aui"); + sc->ep_connectors |= AUI; + } + if (i & IS_BNC) { + if (sc->ep_connectors) + printf("/"); + printf("bnc"); + sc->ep_connectors |= BNC; + } + if (i & IS_UTP) { + if (sc->ep_connectors) + printf("/"); + printf("utp"); + sc->ep_connectors |= UTP; + } + if (!(sc->ep_connectors & 7)) + printf("no connectors!"); + else + printf("[*%s*]", ep_conn_type[j]); + + /* + * Read the station address from the eeprom + */ + p = (u_short *) & sc->arpcom.ac_enaddr; + for (i = 0; i < 3; i++) { + GO_WINDOW(0); + p[i] = htons(get_e(is, i)); + GO_WINDOW(2); + outw(BASE + EP_W2_ADDR_0 + (i * 2), ntohs(p[i])); + } + printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr)); + + ifp->if_unit = is->id_unit; + ifp->if_name = "ep"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; + ifp->if_init = epinit; + ifp->if_output = ether_output; + ifp->if_start = epstart; + ifp->if_ioctl = epioctl; + ifp->if_watchdog = epwatchdog; + + if_attach(ifp); + ep_registerdev(is); + + /* + * Fill the hardware address into ifa_addr if we find an AF_LINK entry. + * We need to do this so bpf's can get the hardware addr of this card. + * netstat likes this too! + */ + ifa = ifp->if_addrlist; + while ((ifa != 0) && (ifa->ifa_addr != 0) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + + if ((ifa != 0) && (ifa->ifa_addr != 0)) { + sdl = (struct sockaddr_dl *) ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); + } + /* we give some initial parameters */ + sc->rx_avg_pkt = 128; + + /* + * NOTE: In all this I multiply everything by 64. + * W_s = the speed the CPU is able to write to the TX FIFO. + * T_s = the speed the board sends the info to the Ether. + * W_s/T_s = 16 (represents 16/64) => W_s = 25 % of T_s. + * This will give us for a packet of 1500 bytes + * tx_start_thresh=1125 and for a pkt of 64 bytes tx_start_threshold=48. + * We prefer to start thinking the CPU is much slower than the Ethernet + * transmission. + */ + sc->tx_rate = TX_INIT_RATE; + sc->tx_counter = 0; + sc->rx_latency = RX_INIT_LATENCY; + sc->rx_early_thresh = RX_INIT_EARLY_THRESH; +#ifdef EP_LOCAL_STATS + sc->rx_no_first = sc->rx_no_mbuf = + sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl = + sc->tx_underrun = 0; #endif + ep_fset(F_RX_FIRST); + sc->top = sc->mcur = 0; - sc->tx_start_thresh = 20; /* probably a good starting point. */ - - return 1; +#if NBPFILTER > 0 + bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + return 1; } @@ -433,752 +623,847 @@ epattach(is) */ void epinit(unit) - int unit; + int unit; { - register struct ep_softc *sc = &ep_softc[unit]; - register struct ifnet *ifp = &sc->arpcom.ac_if; - int s, i; + register struct ep_softc *sc = &ep_softc[unit]; + register struct ifnet *ifp = &sc->arpcom.ac_if; + int s, i; - if (ifp->if_addrlist == (struct ifaddr *) 0) - return; + if (ifp->if_addrlist == (struct ifaddr *) 0) + return; - s = splimp(); - while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) - ; + s = splimp(); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); - GO_WINDOW(0); + GO_WINDOW(0); - /* Disable the card */ - outw(BASE + EP_W0_CONFIG_CTRL, 0); + /* Disable the card */ + outw(BASE + EP_W0_CONFIG_CTRL, 0); - /* Enable the card */ - outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); + /* Enable the card */ + outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); - GO_WINDOW(2); + GO_WINDOW(2); - /* Reload the ether_addr. */ - for (i = 0; i < 6; i++) - outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); + /* Reload the ether_addr. */ + for (i = 0; i < 6; i++) + outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); - outw(BASE + EP_COMMAND, RX_RESET); - outw(BASE + EP_COMMAND, TX_RESET); + outw(BASE + EP_COMMAND, RX_RESET); + outw(BASE + EP_COMMAND, TX_RESET); - /* Window 1 is operating window */ - GO_WINDOW(1); - for (i = 0; i < 31; i++) - inb(BASE + EP_W1_TX_STATUS); + /* Window 1 is operating window */ + GO_WINDOW(1); + for (i = 0; i < 31; i++) + inb(BASE + EP_W1_TX_STATUS); - /* get rid of stray intr's */ - outw(BASE + EP_COMMAND, ACK_INTR | 0xff); + /* get rid of stray intr's */ + outw(BASE + EP_COMMAND, ACK_INTR | 0xff); - outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE | - S_TX_COMPLETE | S_TX_AVAIL); - outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE | - S_TX_COMPLETE | S_TX_AVAIL); + outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS); - outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | - FIL_MULTICAST | FIL_BRDCST); + outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS); + + outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | + FIL_GROUP | FIL_BRDCST); /* - * you can `ifconfig (link0|-link0) ep0' to get the following + * you can `ifconfig ep0 (bnc|aui)' to get the following * behaviour: - * -link0 disable AUI/UTP. enable BNC. - * link0 disable BNC. enable AUI. if the card has a UTP + * bnc disable AUI/UTP. enable BNC. + * aui disable BNC. enable AUI. if the card has a UTP * connector, that is enabled too. not sure, but it * seems you have to be careful to not plug things * into both AUI & UTP. */ #if defined(__NetBSD__) - if (!(ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & BNC)) { + if (!(ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & BNC)) { #else - if (!(ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & BNC)) { + if (!(ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & BNC)) { #endif - outw(BASE + EP_COMMAND, START_TRANSCEIVER); - DELAY(1000); - } + outw(BASE + EP_COMMAND, START_TRANSCEIVER); + DELAY(1000); + } #if defined(__NetBSD__) - if ((ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & UTP)) { + if ((ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & UTP)) { #else - if ((ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & UTP)) { + if ((ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & UTP)) { #endif - GO_WINDOW(4); - outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); - GO_WINDOW(1); - } + GO_WINDOW(4); + outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); + GO_WINDOW(1); + } + outw(BASE + EP_COMMAND, RX_ENABLE); + outw(BASE + EP_COMMAND, TX_ENABLE); - outw(BASE + EP_COMMAND, RX_ENABLE); - outw(BASE + EP_COMMAND, TX_ENABLE); + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; /* just in case */ - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; /* just in case */ - /* - * Store up a bunch of mbuf's for use later. (MAX_MBS). First we - * free up any that we had in case we're being called from intr or - * somewhere else. - */ - sc->last_mb = 0; - sc->next_mb = 0; - epmbuffill((void *)sc); + sc->tx_rate = TX_INIT_RATE; + sc->tx_counter = 0; + sc->rx_latency = RX_INIT_LATENCY; + sc->rx_early_thresh = RX_INIT_EARLY_THRESH; +#ifdef EP_LOCAL_STATS + sc->rx_no_first = sc->rx_no_mbuf = + sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl = + sc->tx_underrun = 0; +#endif + ep_fset(F_RX_FIRST); + ep_frst(F_RX_TRAILER); + if (sc->top) { + m_freem(sc->top); + sc->top = sc->mcur = 0; + } + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | sc->rx_early_thresh); - epstart(ifp); + /* + * Store up a bunch of mbuf's for use later. (MAX_MBS). First we free up + * any that we had in case we're being called from intr or somewhere + * else. + */ + sc->last_mb = 0; + sc->next_mb = 0; + epmbuffill((caddr_t) sc, 0); - splx(s); + epstart(ifp); + + splx(s); } static const char padmap[] = {0, 3, 2, 1}; void epstart(ifp) - struct ifnet *ifp; + struct ifnet *ifp; { - register struct ep_softc *sc = &ep_softc[ifp->if_unit]; - struct mbuf *m, *top; - int s, len, pad; - - s = splimp(); - if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) { - splx(s); - return; - } + register struct ep_softc *sc = &ep_softc[ifp->if_unit]; + register u_int len; + register struct mbuf *m; + struct mbuf *top; + int s, pad; + s = splimp(); + if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) { + splx(s); + return; + } startagain: - /* Sneak a peek at the next packet */ - m = sc->arpcom.ac_if.if_snd.ifq_head; - if (m == 0) { - splx(s); - return; - } + /* Sneak a peek at the next packet */ + m = sc->arpcom.ac_if.if_snd.ifq_head; + if (m == 0) { + splx(s); + return; + } #if 0 - len = m->m_pkthdr.len; + len = m->m_pkthdr.len; #else - for (len = 0, top = m; m; m = m->m_next) - len += m->m_len; + for (len = 0, top = m; m; m = m->m_next) + len += m->m_len; #endif - pad = padmap[len & 3]; - - /* - * The 3c509 automatically pads short packets to minimum ethernet - * length, but we drop packets that are too large. Perhaps we should - * truncate them instead? - */ - if (len + pad > ETHER_MAX_LEN) { - /* packet is obviously too large: toss it */ - ++sc->arpcom.ac_if.if_oerrors; - IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); - m_freem(m); - goto readcheck; - } - - if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { - /* no room in FIFO */ - outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); - sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; - splx(s); - return; - } else { - outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 2044); - } + pad = padmap[len & 3]; + /* + * The 3c509 automatically pads short packets to minimum ethernet length, + * but we drop packets that are too large. Perhaps we should truncate + * them instead? + */ + if (len + pad > ETHER_MAX_LEN) { + /* packet is obviously too large: toss it */ + ++sc->arpcom.ac_if.if_oerrors; IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); - if (m == 0) { /* not really needed */ - splx(s); - return; + m_freem(m); + goto readcheck; + } + if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { + /* no room in FIFO */ + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); + sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; + splx(s); + return; + } + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + + outw(BASE + EP_W1_TX_PIO_WR_1, len); + outw(BASE + EP_W1_TX_PIO_WR_1, 0x0); /* Second dword meaningless */ + + /* compute the Tx start threshold for this packet */ + sc->tx_start_thresh = len = + (((len * (64 - sc->tx_rate)) >> 6) & ~3) + 16; + outw(BASE + EP_COMMAND, SET_TX_START_THRESH | len); + + for (top = m; m != 0; m = m->m_next) + if(ep_ftst(F_ACCESS_32_BITS)) { + outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), + m->m_len / 4); + if (m->m_len & 3) + outsb(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t) + m->m_len / 4, + m->m_len & 3); + } else { + outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 2); + if (m->m_len & 1) + outb(BASE + EP_W1_TX_PIO_WR_1, + *(mtod(m, caddr_t) + m->m_len - 1)); } - outw(BASE + EP_COMMAND, SET_TX_START_THRESH | - (len / 4 + sc->tx_start_thresh)); - outw(BASE + EP_W1_TX_PIO_WR_1, len); - outw(BASE + EP_W1_TX_PIO_WR_1, 0xffff); /* Second dword meaningless */ - - for (top = m; m != 0; m = m->m_next) { - if (sc->bus32bit) { - if(m->m_len > 3) - outsl(BASE + EP_W1_TX_PIO_WR_1, - mtod(m, caddr_t), m->m_len/4); - if(m->m_len & 3) - outsb(BASE + EP_W1_TX_PIO_WR_1, - mtod(m, caddr_t) + (m->m_len & ~3), m->m_len & 3); - } else { - if (m->m_len > 1) - outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), - m->m_len/2); - if (m->m_len & 1) - outb(BASE + EP_W1_TX_PIO_WR_1, - *(mtod(m, caddr_t) + m->m_len - 1)); - - } - } - while (pad--) - outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ + while (pad--) + outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ #if NBPFILTER > 0 - if (sc->bpf) { - u_short etype; - int off, datasize, resid; - struct ether_header *eh; - struct trailer_header { - u_short ether_type; - u_short ether_residual; - } trailer_header; - char ether_packet[ETHER_MAX_LEN]; - char *ep; + if (sc->bpf) { + u_short etype; + int off, datasize, resid; + struct ether_header *eh; + struct trailer_header { + u_short ether_type; + u_short ether_residual; + } trailer_header; + char ether_packet[ETHER_MAX_LEN]; + char *ep; - ep = ether_packet; - - /* - * We handle trailers below: - * Copy ether header first, then residual data, - * then data. Put all this in a temporary buffer - * 'ether_packet' and send off to bpf. Since the - * system has generated this packet, we assume - * that all of the offsets in the packet are - * correct; if they're not, the system will almost - * certainly crash in m_copydata. - * We make no assumptions about how the data is - * arranged in the mbuf chain (i.e. how much - * data is in each mbuf, if mbuf clusters are - * used, etc.), which is why we use m_copydata - * to get the ether header rather than assume - * that this is located in the first mbuf. - */ - /* copy ether header */ - m_copydata(top, 0, sizeof(struct ether_header), ep); - eh = (struct ether_header *) ep; - ep += sizeof(struct ether_header); - eh->ether_type = etype = ntohs(eh->ether_type); - if (etype >= ETHERTYPE_TRAIL && - etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { - datasize = ((etype - ETHERTYPE_TRAIL) << 9); - off = datasize + sizeof(struct ether_header); - - /* copy trailer_header into a data structure */ - m_copydata(top, off, sizeof(struct trailer_header), - (caddr_t)&trailer_header.ether_type); - - /* copy residual data */ - resid = trailer_header.ether_residual - - sizeof(struct trailer_header); - resid = ntohs(resid); - m_copydata(top, off + sizeof(struct trailer_header), - resid, ep); - ep += resid; - - /* copy data */ - m_copydata(top, sizeof(struct ether_header), - datasize, ep); - ep += datasize; - - /* restore original ether packet type */ - eh->ether_type = trailer_header.ether_type; - - bpf_tap(sc->bpf, ether_packet, ep - ether_packet); - } else - bpf_mtap(sc->bpf, top); - } -#endif - - m_freem(top); - ++sc->arpcom.ac_if.if_opackets; + ep = ether_packet; /* - * Is another packet coming in? We don't want to overflow the - * tiny RX fifo. + * We handle trailers below: Copy ether header first, then residual + * data, then data. Put all this in a temporary buffer 'ether_packet' + * and send off to bpf. Since the system has generated this packet, + * we assume that all of the offsets in the packet are correct; if + * they're not, the system will almost certainly crash in m_copydata. + * We make no assumptions about how the data is arranged in the mbuf + * chain (i.e. how much data is in each mbuf, if mbuf clusters are + * used, etc.), which is why we use m_copydata to get the ether + * header rather than assume that this is located in the first mbuf. */ + /* copy ether header */ + m_copydata(top, 0, sizeof(struct ether_header), ep); + eh = (struct ether_header *) ep; + ep += sizeof(struct ether_header); + eh->ether_type = etype = ntohs(eh->ether_type); + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { + datasize = ((etype - ETHERTYPE_TRAIL) << 9); + off = datasize + sizeof(struct ether_header); + + /* copy trailer_header into a data structure */ + m_copydata(top, off, sizeof(struct trailer_header), + (caddr_t) & trailer_header.ether_type); + + /* copy residual data */ + resid = trailer_header.ether_residual - + sizeof(struct trailer_header); + resid = ntohs(resid); + m_copydata(top, off + sizeof(struct trailer_header), + resid, ep); + ep += resid; + + /* copy data */ + m_copydata(top, sizeof(struct ether_header), + datasize, ep); + ep += datasize; + + /* restore original ether packet type */ + eh->ether_type = trailer_header.ether_type; + + bpf_tap(sc->bpf, ether_packet, ep - ether_packet); + } else + bpf_mtap(sc->bpf, top); + } +#endif + + sc->arpcom.ac_if.if_opackets++; + m_freem(top); + /* + * Every 1024*4 packets we increment the tx_rate if we haven't had + * errors, that in the case it has abnormaly goten too low + */ + if (!(++sc->tx_counter & (1024 * 4 - 1)) && + sc->tx_rate < TX_INIT_MAX_RATE) + sc->tx_rate++; + + /* + * Is another packet coming in? We don't want to overflow the tiny RX + * fifo. + */ readcheck: - if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) { - splx(s); - return; + if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) { + /* + * we check if we have packets left, in that case we prepare to come + * back later + */ + if (sc->arpcom.ac_if.if_snd.ifq_head) { + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | + sc->tx_start_thresh); } - goto startagain; + splx(s); + return; + } + goto startagain; } void epintr(unit) - int unit; + int unit; { - int status, i; - register struct ep_softc *sc = &ep_softc[unit]; - struct ifnet *ifp = &sc->arpcom.ac_if; - struct mbuf *m; + int i; + register int status; + register struct ep_softc *sc = &ep_softc[unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; - status = 0; -checkintr: - status = inw(BASE + EP_STATUS) & - (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE); - if (status == 0) { - /* No interrupts. */ - outw(BASE + EP_COMMAND, C_INTR_LATCH); - return; + outw(BASE + EP_COMMAND, SET_INTR_MASK); /* disable all Ints */ + outw(BASE + EP_COMMAND, C_INTR_LATCH); /* ACK int Latch */ + + while ((status = inw(BASE + EP_STATUS)) & S_5_INTS) { + if (status & (S_RX_COMPLETE | S_RX_EARLY)) { + /* we just need ACK for RX_EARLY */ + if (status & S_RX_EARLY) + outw(BASE + EP_COMMAND, C_RX_EARLY); + epread(sc); + continue; } - /* important that we do this first. */ - outw(BASE + EP_COMMAND, ACK_INTR | status); - if (status & S_TX_AVAIL) { - status &= ~S_TX_AVAIL; - inw(BASE + EP_W1_FREE_TX); - sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; - epstart(&sc->arpcom.ac_if); - } - if (status & S_RX_COMPLETE) { - status &= ~S_RX_COMPLETE; - epread(sc); + /* we need ACK */ + outw(BASE + EP_COMMAND, C_TX_AVAIL); + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + epstart(&sc->arpcom.ac_if); } if (status & S_CARD_FAILURE) { - printf("ep%d: reset (status: %x)\n", unit, status); - outw(BASE + EP_COMMAND, C_INTR_LATCH); - epinit(unit); - return; +#ifdef EP_LOCAL_STATS + printf("\nep%d:\n\tStatus: %x\n", unit, status); + GO_WINDOW(4); + printf("\tFIFO Diagnostic: %x\n", inw(BASE + EP_W4_FIFO_DIAG)); + printf("\tStat: %x\n", sc->stat); + printf("\tIpackets=%d, Opackets=%d\n", + sc->arpcom.ac_if.if_ipackets, sc->arpcom.ac_if.if_opackets); + printf("\tNOF=%d, NOMB=%d, BPFD=%d, RXOF=%d, RXOL=%d, TXU=%d\n", + sc->rx_no_first, sc->rx_no_mbuf, sc->rx_bpf_disc, sc->rx_overrunf, + sc->rx_overrunl, sc->tx_underrun); +#else + printf("ep%d: Status: %x\n", unit, status); +#endif + epinit(unit); + return; } if (status & S_TX_COMPLETE) { - status &= ~S_TX_COMPLETE; - /* - * We need to read TX_STATUS until we get a 0 status in - * order to turn off the interrupt flag. - */ - while ((i = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) { - outw(BASE + EP_W1_TX_STATUS, 0x0); - if (i & (TXS_MAX_COLLISION | TXS_JABBER | TXS_UNDERRUN)) { - if (i & TXS_MAX_COLLISION) - ++sc->arpcom.ac_if.if_collisions; - if (i & (TXS_JABBER | TXS_UNDERRUN)) { - outw(BASE + EP_COMMAND, TX_RESET); - if (i & TXS_UNDERRUN) { - if (sc->tx_start_thresh < ETHER_MAX_LEN) { - sc->tx_start_thresh += 20; - outw(BASE + EP_COMMAND, - SET_TX_START_THRESH | - sc->tx_start_thresh); - } - } - } - outw(BASE + EP_COMMAND, TX_ENABLE); - ++sc->arpcom.ac_if.if_oerrors; + /* we need ACK. we do it at the end */ + /* + * We need to read TX_STATUS until we get a 0 status in order to + * turn off the interrupt flag. + */ + while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) { + if (status & TXS_SUCCES_INTR_REQ); + else if (status & (TXS_UNDERRUN | TXS_JABBER | TXS_MAX_COLLISION)) { + outw(BASE + EP_COMMAND, TX_RESET); + if (status & TXS_UNDERRUN) { + if (sc->tx_rate > 1) { + sc->tx_rate--; /* Actually in steps of 1/64 */ + sc->tx_counter = 0; /* We reset it */ } +#ifdef EP_LOCAL_STATS + sc->tx_underrun++; +#endif + } else { + if (status & TXS_JABBER); + else /* TXS_MAX_COLLISION - we shouldn't get here */ + ++sc->arpcom.ac_if.if_collisions; + } + ++sc->arpcom.ac_if.if_oerrors; + outw(BASE + EP_COMMAND, TX_ENABLE); + /* + * To have a tx_avail_int but giving the chance to the + * Reception + */ + if (sc->arpcom.ac_if.if_snd.ifq_head) { + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8); + } } - epstart(ifp); - } - goto checkintr; + outb(BASE + EP_W1_TX_STATUS, 0x0); /* pops up the next + * status */ + } /* while */ + } /* end TX_COMPLETE */ + } + /* re-enable ints */ + outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS); } void epread(sc) - register struct ep_softc *sc; + register struct ep_softc *sc; { - struct ether_header *eh; - struct mbuf *mcur, *m, *m0, *top; - int totlen, lenthisone; - int save_totlen; - u_short etype; - int off, resid; - int count, spinwait; - int i; + struct ether_header *eh; + struct mbuf *top, *mcur, *m; + int lenthisone; - totlen = inw(BASE + EP_W1_RX_STATUS); - off = 0; - top = 0; + short rx_fifo2, status; + register short delta; + register short rx_fifo; + u_short etype; - if (totlen & ERR_RX) { - ++sc->arpcom.ac_if.if_ierrors; + /* + * XXX I have just adapted the code for the protocol trailing processing + * as programed in the original driver. FreeBSD 1.1.5.1 release + */ + status = inw(BASE + EP_W1_RX_STATUS); + +read_again: + + if (status & ERR_RX) { + ++sc->arpcom.ac_if.if_ierrors; + if (status & ERR_RX_OVERRUN) { + /* + * we can think the rx latency is actually greather than we + * expect + */ +#ifdef EP_LOCAL_STATS + if (ep_ftst(F_RX_FIRST)) + sc->rx_overrunf++; + else + sc->rx_overrunl++; +#endif + if (sc->rx_latency < ETHERMTU) + sc->rx_latency += 16; + } + goto out; + } + rx_fifo = rx_fifo2 = status & RX_BYTES_MASK; + + if (ep_ftst(F_RX_FIRST)) { + if (m = sc->mb[sc->next_mb]) { + sc->mb[sc->next_mb] = 0; + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + m->m_data = m->m_pktdat; + m->m_flags = M_PKTHDR; + } else { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (!m) goto out; } - save_totlen = totlen &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */ - - m = sc->mb[sc->next_mb]; - sc->mb[sc->next_mb] = 0; - - if (m == 0) { - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == 0) - goto out; - } else { - /* Convert one of our saved mbuf's */ - sc->next_mb = (sc->next_mb + 1) % MAX_MBS; - m->m_data = m->m_pktdat; - m->m_flags = M_PKTHDR; - } - - top = m0 = m; /* We assign top so we can "goto out" */ + sc->top = sc->mcur = top = m; #define EROUND ((sizeof(struct ether_header) + 3) & ~3) #define EOFF (EROUND - sizeof(struct ether_header)) - m0->m_data += EOFF; + top->m_data += EOFF; + /* Read what should be the header. */ insw(BASE + EP_W1_RX_PIO_RD_1, - mtod(m0, caddr_t), sizeof(struct ether_header) / 2); - m->m_len = sizeof(struct ether_header); - totlen -= sizeof(struct ether_header); + mtod(top, caddr_t), sizeof(struct ether_header) / 2); + top->m_len = sizeof(struct ether_header); + rx_fifo -= sizeof(struct ether_header); + sc->cur_len = rx_fifo2; + /* - * mostly deal with trailer here. (untested) - * We do this in a couple of parts. First we check for a trailer, if - * we have one we convert the mbuf back to a regular mbuf and set the offset and - * subtract sizeof(struct ether_header) from the pktlen. - * After we've read the packet off the interface (all except for the trailer - * header, we then get a header mbuf, read the trailer into it, and fix up - * the mbuf pointer chain. + * Test for trailers. + * I didn't care if this implementation was OK, I just adapted it to + * have the same behaviour as in the original driver */ - eh = mtod(m, struct ether_header *); - eh->ether_type = etype = ntohs((u_short) eh->ether_type); + eh = mtod(top, struct ether_header *); + etype = eh->ether_type = ntohs((u_short) eh->ether_type); if (etype >= ETHERTYPE_TRAIL && etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { - m->m_data = m->m_dat; /* Convert back to regular mbuf. */ - m->m_flags = 0; /* This sucks but non-trailers are the norm */ - off = (etype - ETHERTYPE_TRAIL) * 512; - if (off >= ETHERMTU) { - m_freem(m); - return; /* sanity */ - } - totlen -= sizeof(struct ether_header); /* We don't read the trailer */ - m->m_data += 2 * sizeof(u_short); /* Get rid of type & len */ + if ((etype - ETHERTYPE_TRAIL) * 512 >= ETHERMTU) + goto out; + m->m_data = m->m_dat; /* Convert back to regular mbuf. */ + m->m_flags = 0; /* This sucks but non-trailers are the norm */ + m->m_data += 2 * sizeof(u_short); /* Get rid of type & len */ + + sc->cur_len = sizeof(struct ether_header); + ep_fset(F_RX_TRAILER); + + /* in the case of trailers, we prefer to have the packet complete */ + if (status & ERR_RX_INCOMPLETE) { + ep_frst(F_RX_FIRST); + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | 2032); /* disable */ + return; + } else + /* We don't read the trailer, next we are reading the data */ + rx_fifo -= sizeof(struct ether_header); } - while (totlen > 0) { - lenthisone = min(totlen, M_TRAILINGSPACE(m)); - if (lenthisone == 0) { /* no room in this one */ - mcur = m; - m = sc->mb[sc->next_mb]; - sc->mb[sc->next_mb] = 0; - if (!m) { - MGET(m, M_DONTWAIT, MT_DATA); - if (m == 0) - goto out; - } else { - timeout(epmbuffill, (caddr_t)sc, 0); - sc->next_mb = (sc->next_mb + 1) % MAX_MBS; - } - if (totlen >= MINCLSIZE) - MCLGET(m, M_DONTWAIT); - m->m_len = 0; - mcur->m_next = m; - lenthisone = min(totlen, M_TRAILINGSPACE(m)); - } - if (sc->bus32bit) { - if(totlen > 3) { - lenthisone &= ~3; - insl(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone / 4); - } else - insb(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone); - } else { - if (totlen > 1) { - lenthisone &= ~1; - insw(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone / 2); - } else - *(mtod(m, caddr_t) + m->m_len) = - inb(BASE + EP_W1_RX_PIO_RD_1); - } - m->m_len += lenthisone; - totlen -= lenthisone; - } - if (off) { - top = sc->mb[sc->next_mb]; + } else { + /* come here if we didn't have a complete packet last time */ + top = sc->top; + m = sc->mcur; + sc->cur_len += rx_fifo2; + if (ep_ftst(F_RX_TRAILER)) + /* We don't read the trailer */ + rx_fifo -= sizeof(struct ether_header); + } + + /* Reads what is left in the RX FIFO */ + while (rx_fifo > 0) { + lenthisone = min(rx_fifo, M_TRAILINGSPACE(m)); + if (lenthisone == 0) { /* no room in this one */ + mcur = m; + if (m = sc->mb[sc->next_mb]) { sc->mb[sc->next_mb] = 0; - if (top == 0) { - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (top == 0) { - top = m0; - goto out; - } - } else { - /* Convert one of our saved mbuf's */ - sc->next_mb = (sc->next_mb + 1) % MAX_MBS; - top->m_data = top->m_pktdat; - top->m_flags = M_PKTHDR; - } - insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t), - sizeof(struct ether_header)); - top->m_next = m0; - top->m_len = sizeof(struct ether_header); - /* XXX Accomodate for type and len from beginning of trailer */ - top->m_pkthdr.len = save_totlen - (2 * sizeof(u_short)); + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + } else { + MGET(m, M_DONTWAIT, MT_DATA); + if (!m) + goto out; + } + + if (rx_fifo >= MINCLSIZE) + MCLGET(m, M_DONTWAIT); + m->m_len = 0; + mcur->m_next = m; + lenthisone = min(rx_fifo, M_TRAILINGSPACE(m)); + } + if (ep_ftst(F_ACCESS_32_BITS)) { /* default for EISA configured cards*/ + insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 4); + m->m_len += (lenthisone & ~3); + if (lenthisone & 3) + insb(BASE + EP_W1_RX_PIO_RD_1, + mtod(m, caddr_t) + m->m_len, + lenthisone & 3); + m->m_len += (lenthisone & 3); } else { - top = m0; - top->m_pkthdr.len = save_totlen; + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 2); + m->m_len += lenthisone; + if (lenthisone & 1) + *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); } + rx_fifo -= lenthisone; + } - top->m_pkthdr.rcvif = &sc->arpcom.ac_if; - outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) - ; - ++sc->arpcom.ac_if.if_ipackets; -#if NBPFILTER > 0 - if (sc->bpf) { - bpf_mtap(sc->bpf, top); - - /* - * 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->arpcom.ac_if.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(top); - return; - } + if (ep_ftst(F_RX_TRAILER)) {/* reads the trailer */ + if (m = sc->mb[sc->next_mb]) { + sc->mb[sc->next_mb] = 0; + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + m->m_data = m->m_pktdat; + m->m_flags = M_PKTHDR; + } else { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (!m) + goto out; } + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t), + sizeof(struct ether_header)); + m->m_len = sizeof(struct ether_header); + m->m_next = top; + sc->top = top = m; + /* XXX Accomodate for type and len from beginning of trailer */ + sc->cur_len -= (2 * sizeof(u_short)); + ep_frst(F_RX_TRAILER); + goto all_pkt; + } + + if (status & ERR_RX_INCOMPLETE) { /* we haven't received the complete + * packet */ + sc->mcur = m; +#ifdef EP_LOCAL_STATS + sc->rx_no_first++; /* to know how often we come here */ #endif - m_adj(top, sizeof(struct ether_header)); - ether_input(&sc->arpcom.ac_if, eh, top); + /* + * Re-compute rx_latency, the factor used is 1/4 to go up and 1/32 to + * go down + */ + delta = rx_fifo2 - sc->rx_early_thresh; /* last latency seen LLS */ + delta -= sc->rx_latency;/* LLS - estimated_latency */ + if (delta >= 0) + sc->rx_latency += (delta / 4); + else + sc->rx_latency += (delta / 32); + ep_frst(F_RX_FIRST); + if (!((status = inw(BASE + EP_W1_RX_STATUS)) & ERR_RX_INCOMPLETE)) { + /* we see if by now, the packet has completly arrived */ + goto read_again; + } + /* compute rx_early_threshold */ + delta = (sc->rx_avg_pkt - sc->cur_len - sc->rx_latency - 16) & ~3; + if (delta < MIN_RX_EARLY_THRESHL) + delta = MIN_RX_EARLY_THRESHL; + + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | + (sc->rx_early_thresh = delta)); return; + } +all_pkt: + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + /* + * recompute average packet's length, the factor used is 1/8 to go down + * and 1/32 to go up + */ + delta = sc->cur_len - sc->rx_avg_pkt; + if (delta > 0) + sc->rx_avg_pkt += (delta / 32); + else + sc->rx_avg_pkt += (delta / 8); + delta = (sc->rx_avg_pkt - sc->rx_latency - 16) & ~3; + if (delta < MIN_RX_EARLY_THRESHF) + delta = MIN_RX_EARLY_THRESHF; + sc->rx_early_thresh = delta; + ++sc->arpcom.ac_if.if_ipackets; + ep_fset(F_RX_FIRST); + ep_frst(F_RX_TRAILER); + top->m_pkthdr.rcvif = &sc->arpcom.ac_if; + top->m_pkthdr.len = sc->cur_len; -out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) - ; - if (top) - m_freem(top); +#if NBPFILTER > 0 + if (sc->bpf) { + bpf_mtap(sc->bpf, top); + /* + * 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. + */ + eh = mtod(top, struct ether_header *); + if ((sc->arpcom.ac_if.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) { + if (sc->top) { + m_freem(sc->top); + sc->top = 0; + } + ep_fset(F_RX_FIRST); + ep_frst(F_RX_TRAILER); +#ifdef EP_LOCAL_STATS + sc->rx_bpf_disc++; +#endif + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | delta); + return; + } + } +#endif + + eh = mtod(top, struct ether_header *); + m_adj(top, sizeof(struct ether_header)); + ether_input(&sc->arpcom.ac_if, eh, top); + if (!sc->mb[sc->next_mb]) + epmbuffill((caddr_t) sc, 0); + sc->top = 0; + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | delta); + return; + +out: + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + if (sc->top) { + m_freem(sc->top); + sc->top = 0; +#ifdef EP_LOCAL_STATS + sc->rx_no_mbuf++; +#endif + } + delta = (sc->rx_avg_pkt - sc->rx_latency - 16) & ~3; + if (delta < MIN_RX_EARLY_THRESHF) + delta = MIN_RX_EARLY_THRESHF; + ep_fset(F_RX_FIRST); + ep_frst(F_RX_TRAILER); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | + (sc->rx_early_thresh = delta)); } - /* * Look familiar? */ static int epioctl(ifp, cmd, data) - register struct ifnet *ifp; - int cmd; - caddr_t data; + register struct ifnet *ifp; + int cmd; + caddr_t data; { - register struct ifaddr *ifa = (struct ifaddr *) data; - struct ep_softc *sc = &ep_softc[ifp->if_unit]; - struct ifreq *ifr = (struct ifreq *) data; - int s, error = 0; + register struct ifaddr *ifa = (struct ifaddr *) data; + struct ep_softc *sc = &ep_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *) data; + int s, error = 0; - switch (cmd) { - case SIOCSIFADDR: - ifp->if_flags |= IFF_UP; - switch (ifa->ifa_addr->sa_family) { + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { #ifdef INET - case AF_INET: - epinit(ifp->if_unit); /* before arpwhohas */ - ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; - arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr); - break; + case AF_INET: + epinit(ifp->if_unit); /* before arpwhohas */ + ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr); + break; #endif #ifdef NS - case AF_NS: - { - register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + 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)); - } - epinit(ifp->if_unit); - break; + 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)); } + epinit(ifp->if_unit); + break; + } #endif - default: - epinit(ifp->if_unit); - break; - } - break; - case SIOCSIFFLAGS: - if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { - ifp->if_flags &= ~IFF_RUNNING; - epstop(ifp->if_unit); - epmbufempty(sc); - break; - } - if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0) - epinit(ifp->if_unit); - break; + default: + epinit(ifp->if_unit); + break; + } + break; + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { + ifp->if_flags &= ~IFF_RUNNING; + epstop(ifp->if_unit); + epmbufempty(sc); + break; + } + if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0) + epinit(ifp->if_unit); + break; #ifdef notdef - case SIOCGHWADDR: - bcopy((caddr_t) sc->sc_addr, (caddr_t) &ifr->ifr_data, - sizeof(sc->sc_addr)); - break; + case SIOCGHWADDR: + bcopy((caddr_t) sc->sc_addr, (caddr_t) & ifr->ifr_data, + sizeof(sc->sc_addr)); + break; #endif - case SIOCSIFMTU: + case SIOCSIFMTU: /* * Set the interface MTU. */ - if (ifr->ifr_mtu > ETHERMTU) { - error = EINVAL; + if (ifr->ifr_mtu > ETHERMTU) { + error = EINVAL; } else { - ifp->if_mtu = ifr->ifr_mtu; + ifp->if_mtu = ifr->ifr_mtu; } - break; + break; - default: + default: error = EINVAL; - } - return (error); + } + return (error); } void epreset(unit) - int unit; + int unit; { - int s = splimp(); + int s = splimp(); - epstop(unit); - epinit(unit); - splx(s); + epstop(unit); + epinit(unit); + splx(s); } void epwatchdog(unit) - int unit; + int unit; { - struct ep_softc *sc = &ep_softc[unit]; + struct ep_softc *sc = &ep_softc[unit]; - log(LOG_ERR, "ep%d: watchdog\n", unit); - ++sc->arpcom.ac_if.if_oerrors; + log(LOG_ERR, "ep%d: watchdog\n", unit); + ++sc->arpcom.ac_if.if_oerrors; - epreset(unit); + epreset(unit); } void epstop(unit) - int unit; + int unit; { - struct ep_softc *sc = &ep_softc[unit]; + struct ep_softc *sc = &ep_softc[unit]; - outw(BASE + EP_COMMAND, RX_DISABLE); - outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) - ; - outw(BASE + EP_COMMAND, TX_DISABLE); - outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); - outw(BASE + EP_COMMAND, RX_RESET); - outw(BASE + EP_COMMAND, TX_RESET); - outw(BASE + EP_COMMAND, C_INTR_LATCH); - outw(BASE + EP_COMMAND, SET_RD_0_MASK); - outw(BASE + EP_COMMAND, SET_INTR_MASK); - outw(BASE + EP_COMMAND, SET_RX_FILTER); + outw(BASE + EP_COMMAND, RX_DISABLE); + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + outw(BASE + EP_COMMAND, TX_DISABLE); + outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); + outw(BASE + EP_COMMAND, RX_RESET); + outw(BASE + EP_COMMAND, TX_RESET); + outw(BASE + EP_COMMAND, C_INTR_LATCH); + outw(BASE + EP_COMMAND, SET_RD_0_MASK); + outw(BASE + EP_COMMAND, SET_INTR_MASK); + outw(BASE + EP_COMMAND, SET_RX_FILTER); } -/* - * This is adapted straight from the book. There's probably a better way. - */ static int send_ID_sequence(port) - u_short port; + int port; { - char cx, al; + int cx, al; - cx = 0x0ff; - al = 0x0ff; - - outb(port, 0x0); - DELAY(1000); - outb(port, 0x0); - DELAY(1000); - -loop1: cx--; + for (al = 0xff, cx = 0; cx < 255; cx++) { outb(port, al); - if (!(al & 0x80)) { - al = al << 1; - goto loop1; - } - al = al << 1; - al ^= 0xcf; - if (cx) - goto loop1; - - return(1); + al <<= 1; + if (al & 0x100) + al ^= 0xcf; + } + return (1); } /* - * We get eeprom data from the id_port given an offset into the - * eeprom. Basically; after the ID_sequence is sent to all of - * the cards; they enter the ID_CMD state where they will accept - * command requests. 0x80-0xbf loads the eeprom data. We then - * read the port 16 times and with every read; the cards check - * for contention (ie: if one card writes a 0 bit and another - * writes a 1 bit then the host sees a 0. At the end of the cycle; - * each card compares the data on the bus; if there is a difference - * then that card goes into ID_WAIT state again). In the meantime; - * one bit of data is returned in the AX register which is conveniently - * returned to us by inb(). Hence; we read 16 times getting one - * bit of data with each read. + * We get eeprom data from the id_port given an offset into the eeprom. + * Basically; after the ID_sequence is sent to all of the cards; they enter + * the ID_CMD state where they will accept command requests. 0x80-0xbf loads + * the eeprom data. We then read the port 16 times and with every read; the + * cards check for contention (ie: if one card writes a 0 bit and another + * writes a 1 bit then the host sees a 0. At the end of the cycle; each card + * compares the data on the bus; if there is a difference then that card goes + * into ID_WAIT state again). In the meantime; one bit of data is returned in + * the AX register which is conveniently returned to us by inb(). Hence; we + * read 16 times getting one bit of data with each read. */ -static u_short -epreadeeprom(id_port, offset) - int id_port; - int offset; -{ - int i, data = 0; - outb(id_port, 0x80 + offset); - DELAY(1000); - for (i = 0; i < 16; i++) - data = (data << 1) | (inw(id_port) & 1); - return (data); -} - static int -epbusyeeprom(unit, base) - int unit; ushort base; +get_eeprom_data(id_port, offset) + int id_port; + int offset; { - int i = 0, j; - - while (i++ < 100) { - j = inw(base + EP_W0_EEPROM_COMMAND); - if (j & EEPROM_BUSY) - DELAY(100); - else - break; - } - if (i >= 100) { - printf("\nep%d: eeprom failed to come ready.\n", unit); - return (1); - } - if (j & EEPROM_TST_MODE) { - printf("\nep%d: 3c509 in test mode. Erase pencil mark!\n", unit); - return (1); - } - return (0); + int i, data = 0; + outb(id_port, 0x80 + offset); + DELAY(1000); + for (i = 0; i < 16; i++) + data = (data << 1) | (inw(id_port) & 1); + return (data); } -void -epmbuffill(sp) - void *sp; +/* + * We suppose this is always called inside a splimp(){...}splx() region + */ +static void +epmbuffill(sp, dummy_arg) + caddr_t sp; + int dummy_arg; { - struct ep_softc *sc = (struct ep_softc *)sp; - int s, i; + struct ep_softc *sc = (struct ep_softc *) sp; + int i; - s = splimp(); - i = sc->last_mb; - do { - if(sc->mb[i] == NULL) - MGET(sc->mb[i], M_DONTWAIT, MT_DATA); - if(sc->mb[i] == NULL) - break; - i = (i + 1) % MAX_MBS; - } while (i != sc->next_mb); - sc->last_mb = i; - splx(s); + i = sc->last_mb; + do { + if (sc->mb[i] == NULL) + MGET(sc->mb[i], M_DONTWAIT, MT_DATA); + if (sc->mb[i] == NULL) + break; + i = (i + 1) % MAX_MBS; + } while (i != sc->next_mb); + sc->last_mb = i; } static void epmbufempty(sc) - struct ep_softc *sc; + struct ep_softc *sc; { - int s, i; + int s, i; - s = splimp(); - for (i = 0; imb[i]) { - m_freem(sc->mb[i]); - sc->mb[i] = NULL; - } + s = splimp(); + for (i = 0; i < MAX_MBS; i++) { + if (sc->mb[i]) { + m_freem(sc->mb[i]); + sc->mb[i] = NULL; } - sc->last_mb = sc->next_mb = 0; - untimeout(epmbuffill, sc); - splx(s); + } + sc->last_mb = sc->next_mb = 0; + splx(s); } -#endif /* NEP > 0 */ +#endif /* NEP > 0 */ diff --git a/sys/i386/isa/if_epreg.h b/sys/i386/isa/if_epreg.h index 2b2bcb95744..2dc7a984fa7 100644 --- a/sys/i386/isa/if_epreg.h +++ b/sys/i386/isa/if_epreg.h @@ -1,30 +1,119 @@ /* - * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) - * All rights reserved. - * + * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) 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. The name of the author may not be used to endorse or promote products - * derived from this software withough 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 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. - * - * $Id: if_epreg.h,v 1.2 1994/01/10 19:13:50 ats Exp $ + * 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. The name + * of the author may not be used to endorse or promote products derived from + * this software withough 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 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. + * + * $Id: if_epreg.h,v 1.2 1994/01/10 19:13:50 ats Exp $ Modified by: + * + October 2, 1994 + + Modified by: Andres Vega Garcia + + INRIA - Sophia Antipolis, France + e-mail: avega@sophia.inria.fr + finger: avega@pax.inria.fr + */ + +/* + * Ethernet software status per interface. + */ +struct ep_softc { + struct arpcom arpcom; /* Ethernet common part */ + short ep_io_addr; /* i/o bus address */ +#define MAX_MBS 8 /* # of mbufs we keep around */ + struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */ + int next_mb; /* Which mbuf to use next. */ + int last_mb; /* Last mbuf. */ + struct mbuf *top, *mcur; + short tx_start_thresh; /* Current TX_start_thresh. */ + short tx_rate; + short tx_counter; + short rx_early_thresh; /* Current RX_early_thresh. */ + short rx_latency; + short rx_avg_pkt; + short cur_len; + caddr_t bpf; /* BPF "magic cookie" */ + u_short ep_connectors; /* Connectors on this card. */ + int stat; /* some flags */ +#define F_RX_FIRST 0x1 +#define F_WAIT_TRAIL 0x2 +#define F_RX_TRAILER 0x4 + +#define F_ACCESS_32_BITS 0x100 + +#ifdef EP_LOCAL_STATS + short tx_underrun; + short rx_no_first; + short rx_no_mbuf; + short rx_bpf_disc; + short rx_overrunf; + short rx_overrunl; +#endif +}; + +/* + * Some global constants + */ +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 +#define ETHER_ADDR_LEN 6 + +#define TX_INIT_RATE 16 +#define TX_INIT_MAX_RATE 64 +#define RX_INIT_LATENCY 64 +#define RX_INIT_EARLY_THRESH 64 +#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */ +#define MIN_RX_EARLY_THRESHL 4 + +#define EEPROMSIZE 0x40 +#define MAX_EEPROMBUSY 1000 +#define EP_LAST_TAG 0xd7 +#define EP_MAX_BOARDS 16 +#define EP_ID_PORT 0x100 + +/* + * some macros to acces long named fields + */ +#define IS_BASE (is->id_iobase) +#define BASE (sc->ep_io_addr) + +/* + * Commands to read/write EEPROM trough EEPROM command register (Window 0, + * Offset 0xa) + */ +#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */ +#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */ +#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */ +#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */ + +#define EEPROM_BUSY (1<<15) +#define EEPROM_TST_MODE (1<<14) + +/* + * Some short functions, worth to let them be a macro + */ +#define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY) +#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|(x)) + /************************************************************************** - * * + * * * These define the EEPROM data structure. They are used in the probe * function to verify the existance of the adapter after having sent * the ID_Sequence. @@ -39,7 +128,7 @@ #define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */ #define EEPROM_MFG_ID 0x7 /* 0x6d50 */ #define EEPROM_ADDR_CFG 0x8 /* Base addr */ -#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ +#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ /************************************************************************** * * @@ -50,32 +139,35 @@ * * **************************************************************************/ -#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a command reg. */ -#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status reg. */ -#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window reg. */ +#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a + * command reg. */ +#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status + * reg. */ +#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window + * reg. */ /* * Window 0 registers. Setup. */ - /* Write */ +/* Write */ #define EP_W0_EEPROM_DATA 0x0c #define EP_W0_EEPROM_COMMAND 0x0a #define EP_W0_RESOURCE_CFG 0x08 #define EP_W0_ADDRESS_CFG 0x06 #define EP_W0_CONFIG_CTRL 0x04 - /* Read */ +/* Read */ #define EP_W0_PRODUCT_ID 0x02 #define EP_W0_MFG_ID 0x00 /* * Window 1 registers. Operating Set. */ - /* Write */ +/* Write */ #define EP_W1_TX_PIO_WR_2 0x02 #define EP_W1_TX_PIO_WR_1 0x00 - /* Read */ +/* Read */ #define EP_W1_FREE_TX 0x0c -#define EP_W1_TX_STATUS 0x0b /* byte */ -#define EP_W1_TIMER 0x0a /* byte */ +#define EP_W1_TX_STATUS 0x0b /* byte */ +#define EP_W1_TIMER 0x0a /* byte */ #define EP_W1_RX_STATUS 0x08 #define EP_W1_RX_PIO_RD_2 0x02 #define EP_W1_RX_PIO_RD_1 0x00 @@ -83,7 +175,7 @@ /* * Window 2 registers. Station Address Setup/Read */ - /* Read/Write */ +/* Read/Write */ #define EP_W2_ADDR_5 0x05 #define EP_W2_ADDR_4 0x04 #define EP_W2_ADDR_3 0x03 @@ -91,17 +183,17 @@ #define EP_W2_ADDR_1 0x01 #define EP_W2_ADDR_0 0x00 -/* +/* * Window 3 registers. FIFO Management. */ - /* Read */ +/* Read */ #define EP_W3_FREE_TX 0x0c #define EP_W3_FREE_RX 0x0a /* * Window 4 registers. Diagnostics. */ - /* Read/Write */ +/* Read/Write */ #define EP_W4_MEDIA_TYPE 0x0a #define EP_W4_CTRLR_STATUS 0x08 #define EP_W4_NET_DIAG 0x06 @@ -112,7 +204,7 @@ /* * Window 5 Registers. Results and Internal status. */ - /* Read */ +/* Read */ #define EP_W5_READ_0_MASK 0x0c #define EP_W5_INTR_MASK 0x0a #define EP_W5_RX_FILTER 0x08 @@ -123,7 +215,7 @@ /* * Window 6 registers. Statistics. */ - /* Read/Write */ +/* Read/Write */ #define TX_TOTAL_OK 0x0c #define RX_TOTAL_OK 0x0a #define TX_DEFERRALS 0x08 @@ -150,13 +242,17 @@ * 10-0: 11-bit arg if any. For commands with no args; * this can be set to anything. */ -#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms after issuing */ +#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms + * after issuing */ #define WINDOW_SELECT (u_short) (0x1<<11) -#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to determine - whether this is needed. If so; - wait 800 uSec before using trans- - ceiver. */ -#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on power-up */ +#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to + * determine whether + * this is needed. If + * so; wait 800 uSec + * before using trans- + * ceiver. */ +#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on + * power-up */ #define RX_ENABLE (u_short) (0x4<<11) #define RX_RESET (u_short) (0x5<<11) #define RX_DISCARD_TOP_PACK (u_short) (0x8<<11) @@ -164,32 +260,32 @@ #define TX_DISABLE (u_short) (0xa<<11) #define TX_RESET (u_short) (0xb<<11) #define REQ_INTR (u_short) (0xc<<11) - /* - * The following C_* acknowledge the various interrupts. - * Some of them don't do anything. See the manual. - */ -#define ACK_INTR (u_short) (0x6800) -# define C_INTR_LATCH (u_short) (ACK_INTR|0x1) -# define C_CARD_FAILURE (u_short) (ACK_INTR|0x2) -# define C_TX_COMPLETE (u_short) (ACK_INTR|0x4) -# define C_TX_AVAIL (u_short) (ACK_INTR|0x8) -# define C_RX_COMPLETE (u_short) (ACK_INTR|0x10) -# define C_RX_EARLY (u_short) (ACK_INTR|0x20) -# define C_INT_RQD (u_short) (ACK_INTR|0x40) -# define C_UPD_STATS (u_short) (ACK_INTR|0x80) #define SET_INTR_MASK (u_short) (0xe<<11) #define SET_RD_0_MASK (u_short) (0xf<<11) #define SET_RX_FILTER (u_short) (0x10<<11) -# define FIL_INDIVIDUAL (u_short) (0x1) -# define FIL_MULTICAST (u_short) (0x2) -# define FIL_BRDCST (u_short) (0x4) -# define FIL_PROMISC (u_short) (0x8) +#define FIL_INDIVIDUAL (u_short) (0x1) +#define FIL_GROUP (u_short) (0x2) +#define FIL_BRDCST (u_short) (0x4) +#define FIL_ALL (u_short) (0x8) #define SET_RX_EARLY_THRESH (u_short) (0x11<<11) #define SET_TX_AVAIL_THRESH (u_short) (0x12<<11) #define SET_TX_START_THRESH (u_short) (0x13<<11) #define STATS_ENABLE (u_short) (0x15<<11) #define STATS_DISABLE (u_short) (0x16<<11) #define STOP_TRANSCEIVER (u_short) (0x17<<11) +/* + * The following C_* acknowledge the various interrupts. Some of them don't + * do anything. See the manual. + */ +#define ACK_INTR (u_short) (0x6800) +#define C_INTR_LATCH (u_short) (ACK_INTR|0x1) +#define C_CARD_FAILURE (u_short) (ACK_INTR|0x2) +#define C_TX_COMPLETE (u_short) (ACK_INTR|0x4) +#define C_TX_AVAIL (u_short) (ACK_INTR|0x8) +#define C_RX_COMPLETE (u_short) (ACK_INTR|0x10) +#define C_RX_EARLY (u_short) (ACK_INTR|0x20) +#define C_INT_RQD (u_short) (ACK_INTR|0x40) +#define C_UPD_STATS (u_short) (ACK_INTR|0x80) /* * Status register. All windows. @@ -217,10 +313,13 @@ #define S_RX_EARLY (u_short) (0x20) #define S_INT_RQD (u_short) (0x40) #define S_UPD_STATS (u_short) (0x80) +#define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\ + S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY) #define S_COMMAND_IN_PROGRESS (u_short) (0x1000) /* - * FIFO Registers. RX Status. + * FIFO Registers. + * RX Status. Window 1/Port 08 * * 15: Incomplete or FIFO empty. * 14: 1: Error in RX Packet 0: Incomplete or no error. @@ -235,18 +334,18 @@ * * 10-0: RX Bytes (0-1514) */ -#define ERR_INCOMPLETE (u_short) (0x8000) -#define ERR_RX (u_short) (0x4000) -#define ERR_RX_PACKET (u_short) (0x2000) -#define ERR_OVERRUN (u_short) (0x1000) -#define ERR_RUNT (u_short) (0x1300) -#define ERR_ALIGNMENT (u_short) (0x1400) -#define ERR_CRC (u_short) (0x1500) -#define ERR_OVERSIZE (u_short) (0x1100) -#define ERR_DRIBBLE (u_short) (0x200) +#define ERR_RX_INCOMPLETE (u_short) (0x1<<15) +#define ERR_RX (u_short) (0x1<<14) +#define ERR_RX_OVERRUN (u_short) (0x8<<11) +#define ERR_RX_RUN_PKT (u_short) (0xb<<11) +#define ERR_RX_ALIGN (u_short) (0xc<<11) +#define ERR_RX_CRC (u_short) (0xd<<11) +#define ERR_RX_OVERSIZE (u_short) (0x9<<11) +#define ERR_RX_DRIBBLE (u_short) (0x2<<11) /* - * TX Status + * FIFO Registers. + * TX Status. Window 1/Port 0B * * Reports the transmit status of a completed transmission. Writing this * register pops the transmit completion stack. @@ -263,44 +362,46 @@ * */ #define TXS_COMPLETE 0x80 -#define TXS_INTR_REQ 0x40 +#define TXS_SUCCES_INTR_REQ 0x40 #define TXS_JABBER 0x20 #define TXS_UNDERRUN 0x10 #define TXS_MAX_COLLISION 0x8 #define TXS_STATUS_OVERFLOW 0x4 /* - * Misc defines for various things. + * Configuration control register. + * Window 0/Port 04 */ -#define TAG_ADAPTER_0 0xd0 -#define ACTIVATE_ADAPTER_TO_CONFIG 0xff -#define ENABLE_DRQ_IRQ 0x0001 -#define MFG_ID 0x6d50 -#define PROD_ID 0x9050 -#define BASE sc->ep_iobase -#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|x) -#define AUI 0x1 -#define BNC 0x2 -#define UTP 0x4 +/* Read */ #define IS_AUI (1<<13) #define IS_BNC (1<<12) #define IS_UTP (1<<9) -#define EEPROM_BUSY (1<<15) -#define EEPROM_TST_MODE (1<<14) -#define READ_EEPROM (1<<7) -#define ETHER_ADDR_LEN 6 -#define ETHER_MAX 1536 +/* Write */ +#define ENABLE_DRQ_IRQ 0x0001 +#define W0_P4_CMD_RESET_ADAPTER 0x4 +#define W0_P4_CMD_ENABLE_ADAPTER 0x1 +/* + * Media type and status. + * Window 4/Port 0A + */ #define ENABLE_UTP 0xc0 #define DISABLE_UTP 0x0 -#define RX_BYTES_MASK (u_short) (0x07ff) - /* - * EISA registers (offset from slot base) + * Misc defines for various things. */ -#define EISA_VENDOR 0x0c80 /* vendor ID (2 ports) */ -#define EISA_MODEL 0x0c82 /* model number (2 ports) */ -#define EISA_CONTROL 0x0c84 -#define EISA_RESET 0x04 -#define EISA_ERROR 0x02 -#define EISA_ENABLE 0x01 +#define ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */ +#define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */ +#define PROD_ID 0x9150 + +#define AUI 0x1 +#define BNC 0x2 +#define UTP 0x4 + +#define ETHER_ADDR_LEN 6 +#define ETHER_MAX 1536 +#define RX_BYTES_MASK (u_short) (0x07ff) + + /* EISA support */ +#define EP_EISA_START 0x1000 +#define EP_EISA_W0 0x0c80