if_ex.c revision 52286
121769Sjkh/* 221769Sjkh * Copyright (c) 1996, Javier Mart�n Rueda (jmrueda@diatel.upm.es) 321769Sjkh * All rights reserved. 421769Sjkh * 521769Sjkh * Redistribution and use in source and binary forms, with or without 621769Sjkh * modification, are permitted provided that the following conditions 721769Sjkh * are met: 821769Sjkh * 1. Redistributions of source code must retain the above copyright 921769Sjkh * notice unmodified, this list of conditions, and the following 1021769Sjkh * disclaimer. 1121769Sjkh * 2. Redistributions in binary form must reproduce the above copyright 1221769Sjkh * notice, this list of conditions and the following disclaimer in the 1321769Sjkh * documentation and/or other materials provided with the distribution. 1421769Sjkh * 1521769Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1621769Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1721769Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1821769Sjkh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1921769Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2021769Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2121769Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2221769Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2321769Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2421769Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2521769Sjkh * SUCH DAMAGE. 2629877Smsmith * 2750477Speter * $FreeBSD: head/sys/dev/ex/if_ex.c 52286 1999-10-16 06:26:44Z mdodd $ 2852286Smdodd * 2952286Smdodd * MAINTAINER: Matthew N. Dodd <winter@jurai.net> 3052286Smdodd * <mdodd@FreeBSD.org> 3121769Sjkh */ 3221769Sjkh 3321769Sjkh/* 3429877Smsmith * Intel EtherExpress Pro/10, Pro/10+ Ethernet driver 3521769Sjkh * 3621769Sjkh * Revision history: 3721769Sjkh * 3821769Sjkh * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast. 3921769Sjkh */ 4021769Sjkh 4121769Sjkh#include "ex.h" 4221769Sjkh#if NEX > 0 4321769Sjkh 4421769Sjkh#include <sys/param.h> 4521769Sjkh#include <sys/systm.h> 4652286Smdodd#include <sys/kernel.h> 4721769Sjkh#include <sys/conf.h> 4824204Sbde#include <sys/sockio.h> 4921769Sjkh#include <sys/mbuf.h> 5021769Sjkh#include <sys/socket.h> 5121769Sjkh 5252286Smdodd#include <sys/module.h> 5352286Smdodd#include <sys/bus.h> 5452286Smdodd 5552286Smdodd#include <machine/bus.h> 5652286Smdodd#include <machine/resource.h> 5752286Smdodd#include <sys/rman.h> 5852286Smdodd 5950026Smdodd#include <net/ethernet.h> 6021769Sjkh#include <net/if.h> 6121769Sjkh 6250026Smdodd#include <netinet/in.h> 6350026Smdodd#include <netinet/if_ether.h> 6450026Smdodd 6521769Sjkh#include <net/bpf.h> 6621769Sjkh 6721769Sjkh#include <machine/clock.h> 6821769Sjkh 6952286Smdodd#include <isa/isavar.h> 7052286Smdodd#include <isa/pnpvar.h> 7152286Smdodd 7221769Sjkh#include <i386/isa/if_exreg.h> 7321769Sjkh 7421769Sjkh#ifdef EXDEBUG 7552286Smdodd# define Start_End 1 7652286Smdodd# define Rcvd_Pkts 2 7752286Smdodd# define Sent_Pkts 4 7852286Smdodd# define Status 8 7921769Sjkhstatic int debug_mask = 0; 8021769Sjkhstatic int exintr_count = 0; 8152286Smdodd# define DODEBUG(level, action) if (level & debug_mask) action 8221769Sjkh#else 8352286Smdodd# define DODEBUG(level, action) 8421769Sjkh#endif 8521769Sjkh 8621769Sjkh#define Conn_BNC 1 8721769Sjkh#define Conn_TPE 2 8821769Sjkh#define Conn_AUI 3 8921769Sjkh 9052286Smdodd#define CARD_TYPE_EX_10 1 9152286Smdodd#define CARD_TYPE_EX_10_PLUS 2 9252286Smdodd 9321769Sjkhstruct ex_softc { 9452286Smdodd struct arpcom arpcom; /* Ethernet common data */ 9552286Smdodd 9652286Smdodd device_t dev; 9752286Smdodd struct resource *ioport; 9852286Smdodd struct resource *irq; 9952286Smdodd 10052286Smdodd u_int iobase; /* I/O base address. */ 10152286Smdodd u_short irq_no; /* IRQ number. */ 10252286Smdodd 10352286Smdodd char * irq2ee; /* irq <-> internal */ 10452286Smdodd u_char * ee2irq; /* representation conversion */ 10552286Smdodd 10652286Smdodd u_short connector; /* Connector type. */ 10752286Smdodd 10852286Smdodd u_int mem_size; /* Total memory size, in bytes. */ 10952286Smdodd u_int rx_mem_size; /* Rx memory size (by default, */ 11052286Smdodd /* first 3/4 of total memory). */ 11152286Smdodd 11252286Smdodd u_int rx_lower_limit; /* Lower and upper limits of */ 11352286Smdodd u_int rx_upper_limit; /* receive buffer. */ 11452286Smdodd 11552286Smdodd u_int rx_head; /* Head of receive ring buffer. */ 11652286Smdodd u_int tx_mem_size; /* Tx memory size (by default, */ 11752286Smdodd /* last quarter of total memory).*/ 11852286Smdodd 11952286Smdodd u_int tx_lower_limit; /* Lower and upper limits of */ 12052286Smdodd u_int tx_upper_limit; /* transmit buffer. */ 12152286Smdodd 12252286Smdodd u_int tx_head; /* Head and tail of */ 12352286Smdodd u_int tx_tail; /* transmit ring buffer. */ 12452286Smdodd 12552286Smdodd u_int tx_last; /* Pointer to beginning of last */ 12652286Smdodd /* frame in the chain. */ 12721769Sjkh}; 12821769Sjkh 12952286Smdoddstatic char irq2eemap[] = 13052286Smdodd { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 }; 13152286Smdoddstatic u_char ee2irqmap[] = 13252286Smdodd { 9, 3, 5, 10, 11, 0, 0, 0 }; 13321769Sjkh 13452286Smdoddstatic char plus_irq2eemap[] = 13552286Smdodd { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 }; 13652286Smdoddstatic u_char plus_ee2irqmap[] = 13752286Smdodd { 3, 4, 5, 7, 9, 10, 11, 12 }; 13821769Sjkh 13952286Smdodd/* Bus Front End Functions */ 14052286Smdoddstatic int ex_isa_probe __P((device_t)); 14152286Smdoddstatic int ex_attach __P((device_t)); 14221769Sjkh 14352286Smdodd/* Network Interface Functions */ 14452286Smdoddstatic void ex_init __P((void *)); 14552286Smdoddstatic void ex_start __P((struct ifnet *)); 14652286Smdoddstatic int ex_ioctl __P((struct ifnet *, u_long, caddr_t)); 14752286Smdoddstatic void ex_watchdog __P((struct ifnet *)); 14821769Sjkh 14952286Smdoddstatic void ex_stop __P((struct ex_softc *)); 15052286Smdoddstatic void ex_reset __P((struct ex_softc *)); 15152286Smdodd 15252286Smdoddstatic driver_intr_t exintr; 15352286Smdoddstatic void ex_tx_intr __P((struct ex_softc *)); 15452286Smdoddstatic void ex_rx_intr __P((struct ex_softc *)); 15552286Smdodd 15652286Smdoddstatic u_short eeprom_read __P((int, int)); 15752286Smdodd 15852286Smdoddstatic device_method_t ex_methods[] = { 15952286Smdodd /* Device interface */ 16052286Smdodd DEVMETHOD(device_probe, ex_isa_probe), 16152286Smdodd DEVMETHOD(device_attach, ex_attach), 16252286Smdodd 16352286Smdodd { 0, 0 } 16452286Smdodd}; 16552286Smdodd 16652286Smdoddstatic driver_t ex_driver = { 16721769Sjkh "ex", 16852286Smdodd ex_methods, 16952286Smdodd sizeof(struct ex_softc), 17021769Sjkh}; 17121769Sjkh 17252286Smdoddstatic devclass_t ex_devclass; 17352286Smdodd 17452286SmdoddDRIVER_MODULE(ex, isa, ex_driver, ex_devclass, 0, 0); 17552286Smdodd 17652286Smdoddstatic struct isa_pnp_id ex_ids[] = { 17752286Smdodd { 0x3110d425, NULL }, /* INT1031 */ 17852286Smdodd { 0x3010d425, NULL }, /* INT1030 */ 17952286Smdodd { 0, NULL }, 18052286Smdodd}; 18152286Smdodd 18221769Sjkhstatic int look_for_card(u_int iobase) 18321769Sjkh{ 18421769Sjkh int count1, count2; 18521769Sjkh 18621769Sjkh /* 18721769Sjkh * Check for the i82595 signature, and check that the round robin 18821769Sjkh * counter actually advances. 18921769Sjkh */ 19021769Sjkh if (((count1 = inb(iobase + ID_REG)) & Id_Mask) != Id_Sig) 19121769Sjkh return(0); 19221769Sjkh count2 = inb(iobase + ID_REG); 19321769Sjkh count2 = inb(iobase + ID_REG); 19421769Sjkh count2 = inb(iobase + ID_REG); 19552286Smdodd 19621769Sjkh return((count2 & Counter_bits) == ((count1 + 0xc0) & Counter_bits)); 19721769Sjkh} 19821769Sjkh 19952286Smdoddstatic int 20052286Smdoddex_get_media (u_int32_t iobase) 20152286Smdodd{ 20252286Smdodd int tmp; 20321769Sjkh 20452286Smdodd outb(iobase + CMD_REG, Bank2_Sel); 20552286Smdodd tmp = inb(iobase + REG3); 20652286Smdodd outb(iobase + CMD_REG, Bank0_Sel); 20752286Smdodd 20852286Smdodd if (tmp & TPE_bit) 20952286Smdodd return(Conn_TPE); 21052286Smdodd if (tmp & BNC_bit) 21152286Smdodd return(Conn_BNC); 21252286Smdodd 21352286Smdodd return (Conn_AUI); 21452286Smdodd} 21552286Smdodd 21652286Smdoddstatic void 21752286Smdoddex_get_address (u_int32_t iobase, u_char *enaddr) 21821769Sjkh{ 21952286Smdodd u_int16_t eaddr_tmp; 22021769Sjkh 22152286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Lo); 22252286Smdodd enaddr[5] = eaddr_tmp & 0xff; 22352286Smdodd enaddr[4] = eaddr_tmp >> 8; 22452286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Mid); 22552286Smdodd enaddr[3] = eaddr_tmp & 0xff; 22652286Smdodd enaddr[2] = eaddr_tmp >> 8; 22752286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Hi); 22852286Smdodd enaddr[1] = eaddr_tmp & 0xff; 22952286Smdodd enaddr[0] = eaddr_tmp >> 8; 23052286Smdodd 23152286Smdodd return; 23252286Smdodd} 23321769Sjkh 23452286Smdoddstatic int 23552286Smdoddex_card_type (u_char *enaddr) 23652286Smdodd{ 23752286Smdodd if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9)) 23852286Smdodd return (CARD_TYPE_EX_10_PLUS); 23952286Smdodd 24052286Smdodd return (CARD_TYPE_EX_10); 24152286Smdodd} 24252286Smdodd 24352286Smdoddstatic int 24452286Smdoddex_isa_probe(device_t dev) 24552286Smdodd{ 24652286Smdodd u_int iobase; 24752286Smdodd u_int irq; 24852286Smdodd char * irq2ee; 24952286Smdodd u_char * ee2irq; 25052286Smdodd u_char enaddr[6]; 25152286Smdodd int tmp; 25252286Smdodd int error; 25352286Smdodd 25452286Smdodd DODEBUG(Start_End, printf("ex_probe: start\n");); 25552286Smdodd 25652286Smdodd /* Check isapnp ids */ 25752286Smdodd error = ISA_PNP_PROBE(device_get_parent(dev), dev, ex_ids); 25852286Smdodd 25952286Smdodd /* If the card had a PnP ID that didn't match any we know about */ 26052286Smdodd if (error == ENXIO) { 26152286Smdodd return(error); 26252286Smdodd } 26352286Smdodd 26452286Smdodd /* If we had some other problem. */ 26552286Smdodd if (!(error == 0 || error == ENOENT)) { 26652286Smdodd return(error); 26752286Smdodd } 26852286Smdodd 26921769Sjkh /* 27052286Smdodd * If an I/O address was supplied in the configuration file 27152286Smdodd * or by PnP ,probe only that. Otherwise, cycle through the 27252286Smdodd * predefined set of possible addresses. 27352286Smdodd * This should really be a bus enumerator ala DEVICE_IDENTFY() 27421769Sjkh */ 27552286Smdodd iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0); 27652286Smdodd if (iobase != 0) { 27752286Smdodd if (! look_for_card(iobase)) { 27852286Smdodd printf("ex: no card found at 0x%03x\n", iobase); 27952286Smdodd return(ENXIO); 28052286Smdodd } 28152286Smdodd } else { 28252286Smdodd for (iobase = 0x200; iobase < 0x3a0; iobase += 0x10) { 28321769Sjkh if (look_for_card(iobase)) 28421769Sjkh break; 28552286Smdodd } 28652286Smdodd if (iobase >= 0x3a0) { 28752286Smdodd return(ENXIO); 28852286Smdodd } else { 28952286Smdodd bus_set_resource(dev, SYS_RES_IOPORT, 0, 29052286Smdodd iobase, EX_IOSIZE); 29152286Smdodd } 29221769Sjkh } 29321769Sjkh 29421769Sjkh /* 29521769Sjkh * Reset the card. 29621769Sjkh */ 29721769Sjkh outb(iobase + CMD_REG, Reset_CMD); 29829313Smsmith DELAY(400); 29921769Sjkh 30052286Smdodd ex_get_address(iobase, enaddr); 30152286Smdodd 30252286Smdodd /* work out which set of irq <-> internal tables to use */ 30352286Smdodd if (ex_card_type(enaddr) == CARD_TYPE_EX_10_PLUS) { 30452286Smdodd irq2ee = plus_irq2eemap; 30552286Smdodd ee2irq = plus_ee2irqmap; 30652286Smdodd } else { 30752286Smdodd irq2ee = irq2eemap; 30852286Smdodd ee2irq = ee2irqmap; 30952286Smdodd } 31052286Smdodd 31152286Smdodd tmp = eeprom_read(iobase, EE_IRQ_No) & IRQ_No_Mask; 31252286Smdodd irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0); 31352286Smdodd 31452286Smdodd if (irq > 0) { 31552286Smdodd if (ee2irq[tmp] != irq) { 31652286Smdodd printf("ex: WARNING: board's EEPROM is configured" 31752286Smdodd " for IRQ %d, using %d\n", 31852286Smdodd ee2irq[tmp], irq); 31952286Smdodd } 32052286Smdodd } else { 32152286Smdodd irq = ee2irq[tmp]; 32252286Smdodd bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1); 32352286Smdodd } 32452286Smdodd 32552286Smdodd if (irq == 0) { 32652286Smdodd printf("ex: invalid IRQ.\n"); 32752286Smdodd return(ENXIO); 32852286Smdodd } 32952286Smdodd 33052286Smdodd DODEBUG(Start_End, printf("ex_probe: finish\n");); 33152286Smdodd 33252286Smdodd return(0); 33352286Smdodd} 33452286Smdodd 33552286Smdodd 33652286Smdoddint ex_attach(device_t dev) 33752286Smdodd{ 33852286Smdodd struct ex_softc * sc = device_get_softc(dev); 33952286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 34052286Smdodd int unit = device_get_unit(dev); 34152286Smdodd int error; 34252286Smdodd int rid; 34352286Smdodd void * ih; 34452286Smdodd 34552286Smdodd DODEBUG(Start_End, device_printf(dev, "start\n");); 34652286Smdodd 34752286Smdodd rid = 0; 34852286Smdodd sc->ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 34952286Smdodd 0, ~0, 1, RF_ACTIVE); 35052286Smdodd 35152286Smdodd if (!sc->ioport) { 35252286Smdodd device_printf(dev, "No I/O space?!\n"); 35352286Smdodd goto bad; 35452286Smdodd } 35552286Smdodd 35652286Smdodd rid = 0; 35752286Smdodd sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 35852286Smdodd 0, ~0, 1, RF_ACTIVE); 35952286Smdodd 36052286Smdodd if (!sc->irq) { 36152286Smdodd device_printf(dev, "No IRQ?!\n"); 36252286Smdodd goto bad; 36352286Smdodd } 36452286Smdodd 36552286Smdodd error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, 36652286Smdodd exintr, (void *)unit, &ih); 36752286Smdodd 36852286Smdodd if (error) { 36952286Smdodd device_printf(dev, "bus_setup_intr() failed!\n"); 37052286Smdodd goto bad; 37152286Smdodd } 37252286Smdodd 37321769Sjkh /* 37421769Sjkh * Fill in several fields of the softc structure: 37521769Sjkh * - I/O base address. 37621769Sjkh * - Hardware Ethernet address. 37721769Sjkh * - IRQ number (if not supplied in config file, read it from EEPROM). 37821769Sjkh * - Connector type. 37921769Sjkh */ 38052286Smdodd sc->dev = dev; 38152286Smdodd sc->iobase = rman_get_start(sc->ioport); 38252286Smdodd sc->irq_no = rman_get_start(sc->irq); 38329877Smsmith 38452286Smdodd ex_get_address(sc->iobase, sc->arpcom.ac_enaddr); 38552286Smdodd 38629877Smsmith /* work out which set of irq <-> internal tables to use */ 38752286Smdodd if (ex_card_type(sc->arpcom.ac_enaddr) == CARD_TYPE_EX_10_PLUS) { 38829877Smsmith sc->irq2ee = plus_irq2eemap; 38929877Smsmith sc->ee2irq = plus_ee2irqmap; 39052286Smdodd } else { 39129877Smsmith sc->irq2ee = irq2eemap; 39229877Smsmith sc->ee2irq = ee2irqmap; 39329877Smsmith } 39429877Smsmith 39552286Smdodd sc->connector = ex_get_media(sc->iobase); 39621769Sjkh sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */ 39721769Sjkh 39821769Sjkh /* 39921769Sjkh * Initialize the ifnet structure. 40021769Sjkh */ 40121769Sjkh ifp->if_softc = sc; 40221769Sjkh ifp->if_unit = unit; 40321769Sjkh ifp->if_name = "ex"; 40421769Sjkh ifp->if_init = ex_init; 40521769Sjkh ifp->if_output = ether_output; 40621769Sjkh ifp->if_start = ex_start; 40721769Sjkh ifp->if_ioctl = ex_ioctl; 40821769Sjkh ifp->if_watchdog = ex_watchdog; 40921769Sjkh ifp->if_mtu = ETHERMTU; 41052286Smdodd ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST 41152286Smdodd /* XXX not done yet. | IFF_MULTICAST */; 41221769Sjkh 41321769Sjkh /* 41421769Sjkh * Attach the interface. 41521769Sjkh */ 41621769Sjkh if_attach(ifp); 41721769Sjkh ether_ifattach(ifp); 41821769Sjkh 41952286Smdodd if (ex_card_type(sc->arpcom.ac_enaddr) == CARD_TYPE_EX_10_PLUS) { 42052286Smdodd printf("ex%d: Intel EtherExpress Pro/10+, address %6D, connector ", unit, sc->arpcom.ac_enaddr, ":"); 42129313Smsmith } else { 42252286Smdodd printf("ex%d: Intel EtherExpress Pro/10, address %6D, connector ", unit, sc->arpcom.ac_enaddr, ":"); 42329313Smsmith } 42421769Sjkh switch(sc->connector) { 42521769Sjkh case Conn_TPE: printf("TPE\n"); break; 42621769Sjkh case Conn_BNC: printf("BNC\n"); break; 42721769Sjkh case Conn_AUI: printf("AUI\n"); break; 42821769Sjkh default: printf("???\n"); 42921769Sjkh } 43021769Sjkh 43121769Sjkh /* 43221769Sjkh * If BPF is in the kernel, call the attach for it 43321769Sjkh */ 43421769Sjkh bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 43521769Sjkh DODEBUG(Start_End, printf("ex_attach%d: finish\n", unit);); 43646347Speter sc->arpcom.ac_if.if_snd.ifq_maxlen = ifqmaxlen; 43752286Smdodd 43852286Smdodd return(0); 43952286Smdodd 44052286Smdoddbad: 44152286Smdodd 44252286Smdodd if (sc->ioport) 44352286Smdodd bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->ioport); 44452286Smdodd if (sc->irq) 44552286Smdodd bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 44652286Smdodd 44752286Smdodd return (-1); 44821769Sjkh} 44921769Sjkh 45021769Sjkh 45121769Sjkhvoid ex_init(void *xsc) 45221769Sjkh{ 45352286Smdodd struct ex_softc * sc = (struct ex_softc *) xsc; 45452286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 45552286Smdodd int s; 45652286Smdodd int i; 45752286Smdodd register int iobase = sc->iobase; 45852286Smdodd unsigned short temp_reg; 45921769Sjkh 46021769Sjkh DODEBUG(Start_End, printf("ex_init%d: start\n", ifp->if_unit);); 46121769Sjkh 46252286Smdodd if (ifp->if_addrhead.tqh_first == NULL) { 46352286Smdodd return; 46452286Smdodd } 46521769Sjkh s = splimp(); 46621769Sjkh sc->arpcom.ac_if.if_timer = 0; 46721769Sjkh 46821769Sjkh /* 46921769Sjkh * Load the ethernet address into the card. 47021769Sjkh */ 47121769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 47221769Sjkh temp_reg = inb(iobase + EEPROM_REG); 47352286Smdodd if (temp_reg & Trnoff_Enable) { 47452286Smdodd outb(iobase + EEPROM_REG, temp_reg & ~Trnoff_Enable); 47552286Smdodd } 47652286Smdodd for (i = 0; i < ETHER_ADDR_LEN; i++) { 47752286Smdodd outb(iobase + I_ADDR_REG0 + i, sc->arpcom.ac_enaddr[i]); 47852286Smdodd } 47921769Sjkh /* 48021769Sjkh * - Setup transmit chaining and discard bad received frames. 48121769Sjkh * - Match broadcast. 48221769Sjkh * - Clear test mode. 48321769Sjkh * - Set receiving mode. 48421769Sjkh * - Set IRQ number. 48521769Sjkh */ 48621769Sjkh outb(iobase + REG1, inb(iobase + REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr); 48721769Sjkh outb(iobase + REG2, inb(iobase + REG2) | No_SA_Ins | RX_CRC_InMem); 48826545Sgibbs outb(iobase + REG3, inb(iobase + REG3) & 0x3f /* XXX constants. */ ); 48921769Sjkh outb(iobase + CMD_REG, Bank1_Sel); 49029877Smsmith outb(iobase + INT_NO_REG, (inb(iobase + INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]); 49121769Sjkh 49221769Sjkh /* 49321769Sjkh * Divide the available memory in the card into rcv and xmt buffers. 49421769Sjkh * By default, I use the first 3/4 of the memory for the rcv buffer, 49521769Sjkh * and the remaining 1/4 of the memory for the xmt buffer. 49621769Sjkh */ 49721769Sjkh sc->rx_mem_size = sc->mem_size * 3 / 4; 49821769Sjkh sc->tx_mem_size = sc->mem_size - sc->rx_mem_size; 49921769Sjkh sc->rx_lower_limit = 0x0000; 50021769Sjkh sc->rx_upper_limit = sc->rx_mem_size - 2; 50121769Sjkh sc->tx_lower_limit = sc->rx_mem_size; 50221769Sjkh sc->tx_upper_limit = sc->mem_size - 2; 50321769Sjkh outb(iobase + RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8); 50421769Sjkh outb(iobase + RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8); 50521769Sjkh outb(iobase + XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8); 50621769Sjkh outb(iobase + XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8); 50721769Sjkh 50821769Sjkh /* 50921769Sjkh * Enable receive and transmit interrupts, and clear any pending int. 51021769Sjkh */ 51121769Sjkh outb(iobase + REG1, inb(iobase + REG1) | TriST_INT); 51221769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 51321769Sjkh outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 51421769Sjkh outb(iobase + STATUS_REG, All_Int); 51521769Sjkh 51621769Sjkh /* 51721769Sjkh * Initialize receive and transmit ring buffers. 51821769Sjkh */ 51921769Sjkh outw(iobase + RCV_BAR, sc->rx_lower_limit); 52021769Sjkh sc->rx_head = sc->rx_lower_limit; 52126545Sgibbs outw(iobase + RCV_STOP_REG, sc->rx_upper_limit | 0xfe); 52221769Sjkh outw(iobase + XMT_BAR, sc->tx_lower_limit); 52321769Sjkh sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 52421769Sjkh 52521769Sjkh ifp->if_flags |= IFF_RUNNING; 52621769Sjkh ifp->if_flags &= ~IFF_OACTIVE; 52721769Sjkh DODEBUG(Status, printf("OIDLE init\n");); 52821769Sjkh 52921769Sjkh /* 53021769Sjkh * Final reset of the board, and enable operation. 53121769Sjkh */ 53221769Sjkh outb(iobase + CMD_REG, Sel_Reset_CMD); 53321769Sjkh DELAY(2); 53421769Sjkh outb(iobase + CMD_REG, Rcv_Enable_CMD); 53521769Sjkh 53621769Sjkh ex_start(ifp); 53721769Sjkh splx(s); 53821769Sjkh 53921769Sjkh DODEBUG(Start_End, printf("ex_init%d: finish\n", ifp->if_unit);); 54021769Sjkh} 54121769Sjkh 54221769Sjkh 54352286Smdoddvoid 54452286Smdoddex_start(struct ifnet *ifp) 54521769Sjkh{ 54652286Smdodd struct ex_softc * sc = ifp->if_softc; 54752286Smdodd int iobase = sc->iobase; 54852286Smdodd int i, s, len, data_len, avail, dest, next; 54952286Smdodd unsigned char tmp16[2]; 55052286Smdodd struct mbuf * opkt; 55152286Smdodd struct mbuf * m; 55221769Sjkh 55352286Smdodd DODEBUG(Start_End, printf("ex_start%d: start\n", unit);); 55421769Sjkh 55552286Smdodd s = splimp(); 55621769Sjkh 55752286Smdodd /* 55852286Smdodd * Main loop: send outgoing packets to network card until there are no 55952286Smdodd * more packets left, or the card cannot accept any more yet. 56052286Smdodd */ 56152286Smdodd while (((opkt = ifp->if_snd.ifq_head) != NULL) && 56252286Smdodd !(ifp->if_flags & IFF_OACTIVE)) { 56321769Sjkh 56452286Smdodd /* 56552286Smdodd * Ensure there is enough free transmit buffer space for 56652286Smdodd * this packet, including its header. Note: the header 56752286Smdodd * cannot wrap around the end of the transmit buffer and 56852286Smdodd * must be kept together, so we allow space for twice the 56952286Smdodd * length of the header, just in case. 57052286Smdodd */ 57121769Sjkh 57252286Smdodd for (len = 0, m = opkt; m != NULL; m = m->m_next) { 57352286Smdodd len += m->m_len; 57452286Smdodd } 57552286Smdodd 57652286Smdodd data_len = len; 57752286Smdodd 57852286Smdodd DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len);); 57952286Smdodd 58052286Smdodd if (len & 1) { 58152286Smdodd len += XMT_HEADER_LEN + 1; 58252286Smdodd } else { 58352286Smdodd len += XMT_HEADER_LEN; 58452286Smdodd } 58552286Smdodd 58652286Smdodd if ((i = sc->tx_tail - sc->tx_head) >= 0) { 58752286Smdodd avail = sc->tx_mem_size - i; 58852286Smdodd } else { 58952286Smdodd avail = -i; 59052286Smdodd } 59152286Smdodd 59252286Smdodd DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail);); 59352286Smdodd 59452286Smdodd if (avail >= len + XMT_HEADER_LEN) { 59552286Smdodd IF_DEQUEUE(&ifp->if_snd, opkt); 59652286Smdodd 59721769Sjkh#ifdef EX_PSA_INTR 59852286Smdodd /* 59952286Smdodd * Disable rx and tx interrupts, to avoid corruption 60052286Smdodd * of the host address register by interrupt service 60152286Smdodd * routines. 60252286Smdodd * XXX Is this necessary with splimp() enabled? 60352286Smdodd */ 60452286Smdodd outb(iobase + MASK_REG, All_Int); 60521769Sjkh#endif 60621769Sjkh 60752286Smdodd /* 60852286Smdodd * Compute the start and end addresses of this 60952286Smdodd * frame in the tx buffer. 61052286Smdodd */ 61152286Smdodd dest = sc->tx_tail; 61252286Smdodd next = dest + len; 61321769Sjkh 61452286Smdodd if (next > sc->tx_upper_limit) { 61552286Smdodd if ((sc->tx_upper_limit + 2 - sc->tx_tail) <= 61652286Smdodd XMT_HEADER_LEN) { 61752286Smdodd dest = sc->tx_lower_limit; 61852286Smdodd next = dest + len; 61952286Smdodd } 62052286Smdodd } else { 62152286Smdodd next = sc->tx_lower_limit + 62252286Smdodd next - sc->tx_upper_limit - 2; 62352286Smdodd } 62421769Sjkh 62552286Smdodd /* 62652286Smdodd * Build the packet frame in the card's ring buffer. 62752286Smdodd */ 62852286Smdodd DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next);); 62921769Sjkh 63052286Smdodd outw(iobase + HOST_ADDR_REG, dest); 63152286Smdodd outw(iobase + IO_PORT_REG, Transmit_CMD); 63252286Smdodd outw(iobase + IO_PORT_REG, 0); 63352286Smdodd outw(iobase + IO_PORT_REG, next); 63452286Smdodd outw(iobase + IO_PORT_REG, data_len); 63521769Sjkh 63652286Smdodd /* 63752286Smdodd * Output the packet data to the card. Ensure all 63852286Smdodd * transfers are 16-bit wide, even if individual 63952286Smdodd * mbufs have odd length. 64052286Smdodd */ 64121769Sjkh 64252286Smdodd for (m = opkt, i = 0; m != NULL; m = m->m_next) { 64352286Smdodd DODEBUG(Sent_Pkts, printf("[%d]", m->m_len);); 64452286Smdodd if (i) { 64552286Smdodd tmp16[1] = *(mtod(m, caddr_t)); 64652286Smdodd outsw(iobase + IO_PORT_REG, tmp16, 1); 64752286Smdodd } 64852286Smdodd outsw(iobase + IO_PORT_REG, 64952286Smdodd mtod(m, caddr_t) + i, (m->m_len - i) / 2); 65052286Smdodd 65152286Smdodd if ((i = (m->m_len - i) & 1) != 0) { 65252286Smdodd tmp16[0] = *(mtod(m, caddr_t) + 65352286Smdodd m->m_len - 1); 65452286Smdodd } 65552286Smdodd } 65652286Smdodd if (i) { 65752286Smdodd outsw(iobase + IO_PORT_REG, tmp16, 1); 65852286Smdodd 65952286Smdodd /* 66052286Smdodd * If there were other frames chained, update the 66152286Smdodd * chain in the last one. 66252286Smdodd */ 66352286Smdodd if (sc->tx_head != sc->tx_tail) { 66452286Smdodd if (sc->tx_tail != dest) { 66552286Smdodd outw(iobase + HOST_ADDR_REG, 66652286Smdodd sc->tx_last + XMT_Chain_Point); 66752286Smdodd outw(iobase + IO_PORT_REG, dest); 66852286Smdodd } 66952286Smdodd outw(iobase + HOST_ADDR_REG, 67052286Smdodd sc->tx_last + XMT_Byte_Count); 67152286Smdodd i = inw(iobase + IO_PORT_REG); 67252286Smdodd outw(iobase + HOST_ADDR_REG, 67352286Smdodd sc->tx_last + XMT_Byte_Count); 67452286Smdodd outw(iobase + IO_PORT_REG, i | Ch_bit); 67552286Smdodd } 67652286Smdodd 67752286Smdodd /* 67852286Smdodd * Resume normal operation of the card: 67952286Smdodd * - Make a dummy read to flush the DRAM write 68052286Smdodd * pipeline. 68152286Smdodd * - Enable receive and transmit interrupts. 68252286Smdodd * - Send Transmit or Resume_XMT command, as 68352286Smdodd * appropriate. 68452286Smdodd */ 68552286Smdodd inw(iobase + IO_PORT_REG); 68621769Sjkh#ifdef EX_PSA_INTR 68752286Smdodd outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 68821769Sjkh#endif 68952286Smdodd if (sc->tx_head == sc->tx_tail) { 69052286Smdodd outw(iobase + XMT_BAR, dest); 69152286Smdodd outb(iobase + CMD_REG, Transmit_CMD); 69252286Smdodd sc->tx_head = dest; 69352286Smdodd DODEBUG(Sent_Pkts, printf("Transmit\n");); 69452286Smdodd } else { 69552286Smdodd outb(iobase + CMD_REG, Resume_XMT_List_CMD); 69652286Smdodd DODEBUG(Sent_Pkts, printf("Resume\n");); 69752286Smdodd } 69852286Smdodd 69952286Smdodd sc->tx_last = dest; 70052286Smdodd sc->tx_tail = next; 70152286Smdodd 70252286Smdodd if (ifp->if_bpf != NULL) { 70352286Smdodd bpf_mtap(ifp, opkt); 70452286Smdodd } 70552286Smdodd ifp->if_timer = 2; 70652286Smdodd ifp->if_opackets++; 70752286Smdodd m_freem(opkt); 70852286Smdodd } else { 70952286Smdodd ifp->if_flags |= IFF_OACTIVE; 71052286Smdodd DODEBUG(Status, printf("OACTIVE start\n");); 71152286Smdodd } 71252286Smdodd } 71321769Sjkh } 71421769Sjkh 71552286Smdodd splx(s); 71621769Sjkh 71752286Smdodd DODEBUG(Start_End, printf("ex_start%d: finish\n", unit);); 71821769Sjkh} 71921769Sjkh 72052286Smdoddstatic void 72152286Smdoddex_stop(struct ex_softc *sc) 72221769Sjkh{ 72352286Smdodd int iobase = sc->iobase; 72421769Sjkh 72552286Smdodd DODEBUG(Start_End, printf("ex_stop%d: start\n", unit);); 72621769Sjkh 72752286Smdodd /* 72852286Smdodd * Disable card operation: 72952286Smdodd * - Disable the interrupt line. 73052286Smdodd * - Flush transmission and disable reception. 73152286Smdodd * - Mask and clear all interrupts. 73252286Smdodd * - Reset the 82595. 73352286Smdodd */ 73452286Smdodd outb(iobase + CMD_REG, Bank1_Sel); 73552286Smdodd outb(iobase + REG1, inb(iobase + REG1) & ~TriST_INT); 73652286Smdodd outb(iobase + CMD_REG, Bank0_Sel); 73752286Smdodd outb(iobase + CMD_REG, Rcv_Stop); 73852286Smdodd sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 73952286Smdodd sc->tx_last = 0; /* XXX I think these two lines are not necessary, because ex_init will always be called again to reinit the interface. */ 74052286Smdodd outb(iobase + MASK_REG, All_Int); 74152286Smdodd outb(iobase + STATUS_REG, All_Int); 74252286Smdodd outb(iobase + CMD_REG, Reset_CMD); 74352286Smdodd DELAY(200); 74421769Sjkh 74552286Smdodd DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit);); 74652286Smdodd 74752286Smdodd return; 74821769Sjkh} 74921769Sjkh 75021769Sjkh 75152286Smdoddstatic void 75252286Smdoddexintr(void *arg) 75321769Sjkh{ 75452286Smdodd struct ex_softc * sc = (struct ex_softc *)arg; 75552286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 75652286Smdodd int iobase = sc->iobase; 75752286Smdodd int int_status, send_pkts; 75821769Sjkh 75952286Smdodd DODEBUG(Start_End, printf("exintr%d: start\n", unit);); 76021769Sjkh 76121769Sjkh#ifdef EXDEBUG 76221769Sjkh if (++exintr_count != 1) 76321769Sjkh printf("WARNING: nested interrupt (%d). Mail the author.\n", exintr_count); 76421769Sjkh#endif 76521769Sjkh 76652286Smdodd send_pkts = 0; 76752286Smdodd while ((int_status = inb(iobase + STATUS_REG)) & (Tx_Int | Rx_Int)) { 76852286Smdodd if (int_status & Rx_Int) { 76952286Smdodd outb(iobase + STATUS_REG, Rx_Int); 77021769Sjkh 77152286Smdodd ex_rx_intr(sc); 77252286Smdodd } else if (int_status & Tx_Int) { 77352286Smdodd outb(iobase + STATUS_REG, Tx_Int); 77421769Sjkh 77552286Smdodd ex_tx_intr(sc); 77652286Smdodd send_pkts = 1; 77752286Smdodd } 77852286Smdodd } 77921769Sjkh 78052286Smdodd /* 78152286Smdodd * If any packet has been transmitted, and there are queued packets to 78252286Smdodd * be sent, attempt to send more packets to the network card. 78352286Smdodd */ 78452286Smdodd 78552286Smdodd if (send_pkts && (ifp->if_snd.ifq_head != NULL)) { 78652286Smdodd ex_start(ifp); 78752286Smdodd } 78852286Smdodd 78921769Sjkh#ifdef EXDEBUG 79021769Sjkh exintr_count--; 79121769Sjkh#endif 79221769Sjkh 79352286Smdodd DODEBUG(Start_End, printf("exintr%d: finish\n", unit);); 79452286Smdodd 79552286Smdodd return; 79621769Sjkh} 79721769Sjkh 79852286Smdoddstatic void 79952286Smdoddex_tx_intr(struct ex_softc *sc) 80021769Sjkh{ 80152286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 80252286Smdodd int iobase = sc->iobase; 80352286Smdodd int tx_status; 80421769Sjkh 80552286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit);); 80621769Sjkh 80752286Smdodd /* 80852286Smdodd * - Cancel the watchdog. 80952286Smdodd * For all packets transmitted since last transmit interrupt: 81052286Smdodd * - Advance chain pointer to next queued packet. 81152286Smdodd * - Update statistics. 81252286Smdodd */ 81321769Sjkh 81452286Smdodd ifp->if_timer = 0; 81521769Sjkh 81652286Smdodd while (sc->tx_head != sc->tx_tail) { 81752286Smdodd outw(iobase + HOST_ADDR_REG, sc->tx_head); 81821769Sjkh 81952286Smdodd if (! inw(iobase + IO_PORT_REG) & Done_bit) 82052286Smdodd break; 82121769Sjkh 82252286Smdodd tx_status = inw(iobase + IO_PORT_REG); 82352286Smdodd sc->tx_head = inw(iobase + IO_PORT_REG); 82452286Smdodd 82552286Smdodd if (tx_status & TX_OK_bit) { 82652286Smdodd ifp->if_opackets++; 82752286Smdodd } else { 82852286Smdodd ifp->if_oerrors++; 82952286Smdodd } 83052286Smdodd 83152286Smdodd ifp->if_collisions += tx_status & No_Collisions_bits; 83252286Smdodd } 83352286Smdodd 83452286Smdodd /* 83552286Smdodd * The card should be ready to accept more packets now. 83652286Smdodd */ 83752286Smdodd 83852286Smdodd ifp->if_flags &= ~IFF_OACTIVE; 83952286Smdodd 84052286Smdodd DODEBUG(Status, printf("OIDLE tx_intr\n");); 84152286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit);); 84252286Smdodd 84352286Smdodd return; 84421769Sjkh} 84521769Sjkh 84652286Smdoddstatic void 84752286Smdoddex_rx_intr(struct ex_softc *sc) 84821769Sjkh{ 84952286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 85052286Smdodd int iobase = sc->iobase; 85152286Smdodd int rx_status; 85252286Smdodd int pkt_len; 85352286Smdodd int QQQ; 85452286Smdodd struct mbuf * m; 85552286Smdodd struct mbuf * ipkt; 85652286Smdodd struct ether_header * eh; 85721769Sjkh 85852286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit);); 85921769Sjkh 86052286Smdodd /* 86152286Smdodd * For all packets received since last receive interrupt: 86252286Smdodd * - If packet ok, read it into a new mbuf and queue it to interface, 86352286Smdodd * updating statistics. 86452286Smdodd * - If packet bad, just discard it, and update statistics. 86552286Smdodd * Finally, advance receive stop limit in card's memory to new location. 86652286Smdodd */ 86721769Sjkh 86852286Smdodd outw(iobase + HOST_ADDR_REG, sc->rx_head); 86921769Sjkh 87052286Smdodd while (inw(iobase + IO_PORT_REG) == RCV_Done) { 87152286Smdodd 87252286Smdodd rx_status = inw(iobase + IO_PORT_REG); 87352286Smdodd sc->rx_head = inw(iobase + IO_PORT_REG); 87452286Smdodd QQQ = pkt_len = inw(iobase + IO_PORT_REG); 87552286Smdodd 87652286Smdodd if (rx_status & RCV_OK_bit) { 87752286Smdodd MGETHDR(m, M_DONTWAIT, MT_DATA); 87852286Smdodd ipkt = m; 87952286Smdodd if (ipkt == NULL) { 88052286Smdodd ifp->if_iqdrops++; 88152286Smdodd } else { 88252286Smdodd ipkt->m_pkthdr.rcvif = ifp; 88352286Smdodd ipkt->m_pkthdr.len = pkt_len; 88452286Smdodd ipkt->m_len = MHLEN; 88552286Smdodd 88652286Smdodd while (pkt_len > 0) { 88752286Smdodd if (pkt_len > MINCLSIZE) { 88852286Smdodd MCLGET(m, M_DONTWAIT); 88952286Smdodd if (m->m_flags & M_EXT) { 89052286Smdodd m->m_len = MCLBYTES; 89152286Smdodd } else { 89252286Smdodd m_freem(ipkt); 89352286Smdodd ifp->if_iqdrops++; 89452286Smdodd goto rx_another; 89552286Smdodd } 89652286Smdodd } 89752286Smdodd m->m_len = min(m->m_len, pkt_len); 89852286Smdodd 89921769Sjkh /* 90021769Sjkh * NOTE: I'm assuming that all mbufs allocated are of even length, 90121769Sjkh * except for the last one in an odd-length packet. 90221769Sjkh */ 90352286Smdodd 90452286Smdodd insw(iobase + IO_PORT_REG, 90552286Smdodd mtod(m, caddr_t), m->m_len / 2); 90652286Smdodd 90752286Smdodd if (m->m_len & 1) { 90852286Smdodd *(mtod(m, caddr_t) + m->m_len - 1) = inb(iobase + IO_PORT_REG); 90952286Smdodd } 91052286Smdodd pkt_len -= m->m_len; 91152286Smdodd 91252286Smdodd if (pkt_len > 0) { 91352286Smdodd MGET(m->m_next, M_DONTWAIT, MT_DATA); 91452286Smdodd if (m->m_next == NULL) { 91552286Smdodd m_freem(ipkt); 91652286Smdodd ifp->if_iqdrops++; 91752286Smdodd goto rx_another; 91852286Smdodd } 91952286Smdodd m = m->m_next; 92052286Smdodd m->m_len = MLEN; 92152286Smdodd } 92252286Smdodd } 92352286Smdodd eh = mtod(ipkt, struct ether_header *); 92452286Smdodd#ifdef EXDEBUG 92552286Smdodd if (debug_mask & Rcvd_Pkts) { 92652286Smdodd if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) { 92752286Smdodd printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":"); 92852286Smdodd printf("%6D\n", eh->ether_dhost, ":"); 92952286Smdodd } /* QQQ */ 93021769Sjkh } 93121769Sjkh#endif 93252286Smdodd if (ifp->if_bpf != NULL) { 93352286Smdodd bpf_mtap(ifp, ipkt); 93421769Sjkh 93521769Sjkh /* 93652286Smdodd * Note that the interface cannot be in promiscuous mode 93752286Smdodd * if there are no BPF listeners. And if we are in 93852286Smdodd * promiscuous mode, we have to check if this packet is 93952286Smdodd * really ours. 94021769Sjkh */ 94152286Smdodd if ((ifp->if_flags & IFF_PROMISC) && 94252286Smdodd (eh->ether_dhost[0] & 1) == 0 && 94352286Smdodd bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)) != 0 && 94452286Smdodd bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)) != 0) { 94552286Smdodd m_freem(ipkt); 94652286Smdodd goto rx_another; 94752286Smdodd } 94852286Smdodd } 94952286Smdodd m_adj(ipkt, sizeof(struct ether_header)); 95052286Smdodd ether_input(ifp, eh, ipkt); 95152286Smdodd ifp->if_ipackets++; 95252286Smdodd } 95352286Smdodd } else { 95452286Smdodd ifp->if_ierrors++; 95521769Sjkh } 95652286Smdodd outw(iobase + HOST_ADDR_REG, sc->rx_head); 95752286Smdoddrx_another: ; 95821769Sjkh } 95921769Sjkh 96052286Smdodd if (sc->rx_head < sc->rx_lower_limit + 2) 96152286Smdodd outw(iobase + RCV_STOP_REG, sc->rx_upper_limit); 96252286Smdodd else 96352286Smdodd outw(iobase + RCV_STOP_REG, sc->rx_head - 2); 96452286Smdodd 96552286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit);); 96652286Smdodd 96752286Smdodd return; 96821769Sjkh} 96921769Sjkh 97021769Sjkh 97136735Sdfrint ex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 97221769Sjkh{ 97352286Smdodd struct ex_softc * sc = ifp->if_softc; 97452286Smdodd int s; 97552286Smdodd int error = 0; 97621769Sjkh 97752286Smdodd DODEBUG(Start_End, printf("ex_ioctl%d: start ", ifp->if_unit);); 97821769Sjkh 97952286Smdodd s = splimp(); 98021769Sjkh 98152286Smdodd switch(cmd) { 98252286Smdodd case SIOCSIFADDR: 98352286Smdodd case SIOCGIFADDR: 98452286Smdodd case SIOCSIFMTU: 98552286Smdodd error = ether_ioctl(ifp, cmd, data); 98652286Smdodd break; 98721769Sjkh 98852286Smdodd case SIOCSIFFLAGS: 98952286Smdodd DODEBUG(Start_End, printf("SIOCSIFFLAGS");); 99052286Smdodd if ((ifp->if_flags & IFF_UP) == 0 && 99152286Smdodd (ifp->if_flags & IFF_RUNNING)) { 99252286Smdodd 99352286Smdodd ifp->if_flags &= ~IFF_RUNNING; 99452286Smdodd ex_stop(sc); 99552286Smdodd } else { 99652286Smdodd ex_init(sc); 99752286Smdodd } 99852286Smdodd break; 99921769Sjkh#ifdef NODEF 100052286Smdodd case SIOCGHWADDR: 100152286Smdodd DODEBUG(Start_End, printf("SIOCGHWADDR");); 100252286Smdodd bcopy((caddr_t)sc->sc_addr, (caddr_t)&ifr->ifr_data, 100352286Smdodd sizeof(sc->sc_addr)); 100452286Smdodd break; 100521769Sjkh#endif 100652286Smdodd case SIOCADDMULTI: 100752286Smdodd DODEBUG(Start_End, printf("SIOCADDMULTI");); 100852286Smdodd case SIOCDELMULTI: 100952286Smdodd DODEBUG(Start_End, printf("SIOCDELMULTI");); 101052286Smdodd /* XXX Support not done yet. */ 101152286Smdodd error = EINVAL; 101252286Smdodd break; 101352286Smdodd default: 101452286Smdodd DODEBUG(Start_End, printf("unknown");); 101552286Smdodd error = EINVAL; 101652286Smdodd } 101721769Sjkh 101852286Smdodd splx(s); 101921769Sjkh 102052286Smdodd DODEBUG(Start_End, printf("\nex_ioctl%d: finish\n", ifp->if_unit);); 102152286Smdodd 102252286Smdodd return(error); 102321769Sjkh} 102421769Sjkh 102521769Sjkh 102652286Smdoddstatic void 102752286Smdoddex_reset(struct ex_softc *sc) 102821769Sjkh{ 102952286Smdodd int s; 103021769Sjkh 103152286Smdodd DODEBUG(Start_End, printf("ex_reset%d: start\n", unit);); 103221769Sjkh 103352286Smdodd s = splimp(); 103421769Sjkh 103552286Smdodd ex_stop(sc); 103652286Smdodd ex_init(sc); 103721769Sjkh 103852286Smdodd splx(s); 103921769Sjkh 104052286Smdodd DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit);); 104152286Smdodd 104252286Smdodd return; 104321769Sjkh} 104421769Sjkh 104521769Sjkh 104652286Smdoddstatic void 104752286Smdoddex_watchdog(struct ifnet *ifp) 104821769Sjkh{ 104952286Smdodd struct ex_softc * sc = ifp->if_softc; 105021769Sjkh 105152286Smdodd DODEBUG(Start_End, printf("ex_watchdog%d: start\n", ifp->if_unit);); 105221769Sjkh 105352286Smdodd ifp->if_flags &= ~IFF_OACTIVE; 105421769Sjkh 105552286Smdodd DODEBUG(Status, printf("OIDLE watchdog\n");); 105652286Smdodd 105752286Smdodd ifp->if_oerrors++; 105852286Smdodd ex_reset(sc); 105952286Smdodd ex_start(ifp); 106052286Smdodd 106152286Smdodd DODEBUG(Start_End, printf("ex_watchdog%d: finish\n", ifp->if_unit);); 106252286Smdodd 106352286Smdodd return; 106421769Sjkh} 106521769Sjkh 106621769Sjkh 106752286Smdoddstatic u_short 106852286Smdoddeeprom_read(int iobase, int location) 106921769Sjkh{ 107021769Sjkh int i; 107121769Sjkh u_short data = 0; 107221769Sjkh int ee_addr; 107321769Sjkh int read_cmd = location | EE_READ_CMD; 107421769Sjkh short ctrl_val = EECS; 107521769Sjkh 107621769Sjkh ee_addr = iobase + EEPROM_REG; 107721769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 107821769Sjkh outb(ee_addr, EECS); 107921769Sjkh for (i = 8; i >= 0; i--) { 108021769Sjkh short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; 108121769Sjkh outb(ee_addr, outval); 108221769Sjkh outb(ee_addr, outval | EESK); 108321769Sjkh DELAY(3); 108421769Sjkh outb(ee_addr, outval); 108521769Sjkh DELAY(2); 108621769Sjkh } 108721769Sjkh outb(ee_addr, ctrl_val); 108821769Sjkh 108921769Sjkh for (i = 16; i > 0; i--) { 109021769Sjkh outb(ee_addr, ctrl_val | EESK); 109121769Sjkh DELAY(3); 109221769Sjkh data = (data << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); 109321769Sjkh outb(ee_addr, ctrl_val); 109421769Sjkh DELAY(2); 109521769Sjkh } 109621769Sjkh 109721769Sjkh ctrl_val &= ~EECS; 109821769Sjkh outb(ee_addr, ctrl_val | EESK); 109921769Sjkh DELAY(3); 110021769Sjkh outb(ee_addr, ctrl_val); 110121769Sjkh DELAY(2); 110221769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 110321769Sjkh return(data); 110421769Sjkh} 110521769Sjkh 110621769Sjkh#endif /* NEX > 0 */ 1107