if_ex.c revision 57987
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 57987 2000-03-13 12:23:32Z 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 4321769Sjkh#include <sys/param.h> 4421769Sjkh#include <sys/systm.h> 4552286Smdodd#include <sys/kernel.h> 4621769Sjkh#include <sys/conf.h> 4724204Sbde#include <sys/sockio.h> 4821769Sjkh#include <sys/mbuf.h> 4921769Sjkh#include <sys/socket.h> 5021769Sjkh 5152286Smdodd#include <sys/module.h> 5252286Smdodd#include <sys/bus.h> 5352286Smdodd 5452286Smdodd#include <machine/bus.h> 5552286Smdodd#include <machine/resource.h> 5652286Smdodd#include <sys/rman.h> 5752286Smdodd 5857987Smdodd#include <net/if.h> 5957987Smdodd#include <net/if_arp.h> 6057987Smdodd#include <net/if_media.h> 6150026Smdodd#include <net/ethernet.h> 6257987Smdodd#include <net/bpf.h> 6321769Sjkh 6450026Smdodd#include <netinet/in.h> 6550026Smdodd#include <netinet/if_ether.h> 6650026Smdodd 6721769Sjkh#include <machine/clock.h> 6821769Sjkh 6952286Smdodd#include <isa/isavar.h> 7052286Smdodd#include <isa/pnpvar.h> 7152286Smdodd 7255953Speter#include <dev/ex/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 8652286Smdodd#define CARD_TYPE_EX_10 1 8752286Smdodd#define CARD_TYPE_EX_10_PLUS 2 8852286Smdodd 8921769Sjkhstruct ex_softc { 9052286Smdodd struct arpcom arpcom; /* Ethernet common data */ 9157987Smdodd struct ifmedia ifmedia; 9252286Smdodd 9352286Smdodd device_t dev; 9452286Smdodd struct resource *ioport; 9552286Smdodd struct resource *irq; 9652286Smdodd 9752286Smdodd u_int iobase; /* I/O base address. */ 9852286Smdodd u_short irq_no; /* IRQ number. */ 9952286Smdodd 10052286Smdodd char * irq2ee; /* irq <-> internal */ 10152286Smdodd u_char * ee2irq; /* representation conversion */ 10252286Smdodd 10352286Smdodd u_int mem_size; /* Total memory size, in bytes. */ 10452286Smdodd u_int rx_mem_size; /* Rx memory size (by default, */ 10552286Smdodd /* first 3/4 of total memory). */ 10652286Smdodd 10752286Smdodd u_int rx_lower_limit; /* Lower and upper limits of */ 10852286Smdodd u_int rx_upper_limit; /* receive buffer. */ 10952286Smdodd 11052286Smdodd u_int rx_head; /* Head of receive ring buffer. */ 11152286Smdodd u_int tx_mem_size; /* Tx memory size (by default, */ 11252286Smdodd /* last quarter of total memory).*/ 11352286Smdodd 11452286Smdodd u_int tx_lower_limit; /* Lower and upper limits of */ 11552286Smdodd u_int tx_upper_limit; /* transmit buffer. */ 11652286Smdodd 11752286Smdodd u_int tx_head; /* Head and tail of */ 11852286Smdodd u_int tx_tail; /* transmit ring buffer. */ 11952286Smdodd 12052286Smdodd u_int tx_last; /* Pointer to beginning of last */ 12152286Smdodd /* frame in the chain. */ 12221769Sjkh}; 12321769Sjkh 12452286Smdoddstatic char irq2eemap[] = 12552286Smdodd { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 }; 12652286Smdoddstatic u_char ee2irqmap[] = 12752286Smdodd { 9, 3, 5, 10, 11, 0, 0, 0 }; 12821769Sjkh 12952286Smdoddstatic char plus_irq2eemap[] = 13052286Smdodd { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 }; 13152286Smdoddstatic u_char plus_ee2irqmap[] = 13252286Smdodd { 3, 4, 5, 7, 9, 10, 11, 12 }; 13321769Sjkh 13452286Smdodd/* Bus Front End Functions */ 13555882Smdoddstatic void ex_isa_identify __P((driver_t *, device_t)); 13652286Smdoddstatic int ex_isa_probe __P((device_t)); 13755883Smdoddstatic int ex_isa_attach __P((device_t)); 13821769Sjkh 13952286Smdodd/* Network Interface Functions */ 14052286Smdoddstatic void ex_init __P((void *)); 14152286Smdoddstatic void ex_start __P((struct ifnet *)); 14252286Smdoddstatic int ex_ioctl __P((struct ifnet *, u_long, caddr_t)); 14352286Smdoddstatic void ex_watchdog __P((struct ifnet *)); 14421769Sjkh 14557987Smdodd/* ifmedia Functions */ 14657987Smdoddstatic int ex_ifmedia_upd __P((struct ifnet *)); 14757987Smdoddstatic void ex_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 14857987Smdodd 14952286Smdoddstatic void ex_stop __P((struct ex_softc *)); 15052286Smdoddstatic void ex_reset __P((struct ex_softc *)); 15152286Smdodd 15255883Smdoddstatic driver_intr_t ex_intr; 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 */ 16055882Smdodd DEVMETHOD(device_identify, ex_isa_identify), 16152286Smdodd DEVMETHOD(device_probe, ex_isa_probe), 16255883Smdodd DEVMETHOD(device_attach, ex_isa_attach), 16352286Smdodd 16452286Smdodd { 0, 0 } 16552286Smdodd}; 16652286Smdodd 16752286Smdoddstatic driver_t ex_driver = { 16821769Sjkh "ex", 16952286Smdodd ex_methods, 17052286Smdodd sizeof(struct ex_softc), 17121769Sjkh}; 17221769Sjkh 17352286Smdoddstatic devclass_t ex_devclass; 17452286Smdodd 17552286SmdoddDRIVER_MODULE(ex, isa, ex_driver, ex_devclass, 0, 0); 17652286Smdodd 17752286Smdoddstatic struct isa_pnp_id ex_ids[] = { 17852286Smdodd { 0x3110d425, NULL }, /* INT1031 */ 17952286Smdodd { 0x3010d425, NULL }, /* INT1030 */ 18052286Smdodd { 0, NULL }, 18152286Smdodd}; 18252286Smdodd 18355883Smdoddstatic int 18455883Smdoddlook_for_card (u_int iobase) 18521769Sjkh{ 18621769Sjkh int count1, count2; 18721769Sjkh 18821769Sjkh /* 18921769Sjkh * Check for the i82595 signature, and check that the round robin 19021769Sjkh * counter actually advances. 19121769Sjkh */ 19221769Sjkh if (((count1 = inb(iobase + ID_REG)) & Id_Mask) != Id_Sig) 19321769Sjkh return(0); 19421769Sjkh count2 = inb(iobase + ID_REG); 19521769Sjkh count2 = inb(iobase + ID_REG); 19621769Sjkh count2 = inb(iobase + ID_REG); 19752286Smdodd 19821769Sjkh return((count2 & Counter_bits) == ((count1 + 0xc0) & Counter_bits)); 19921769Sjkh} 20021769Sjkh 20152286Smdoddstatic int 20252286Smdoddex_get_media (u_int32_t iobase) 20352286Smdodd{ 20452286Smdodd int tmp; 20521769Sjkh 20652286Smdodd outb(iobase + CMD_REG, Bank2_Sel); 20752286Smdodd tmp = inb(iobase + REG3); 20852286Smdodd outb(iobase + CMD_REG, Bank0_Sel); 20952286Smdodd 21052286Smdodd if (tmp & TPE_bit) 21157987Smdodd return(IFM_ETHER|IFM_10_T); 21252286Smdodd if (tmp & BNC_bit) 21357987Smdodd return(IFM_ETHER|IFM_10_2); 21452286Smdodd 21557987Smdodd return (IFM_ETHER|IFM_10_5); 21652286Smdodd} 21752286Smdodd 21852286Smdoddstatic void 21952286Smdoddex_get_address (u_int32_t iobase, u_char *enaddr) 22021769Sjkh{ 22152286Smdodd u_int16_t eaddr_tmp; 22221769Sjkh 22352286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Lo); 22452286Smdodd enaddr[5] = eaddr_tmp & 0xff; 22552286Smdodd enaddr[4] = eaddr_tmp >> 8; 22652286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Mid); 22752286Smdodd enaddr[3] = eaddr_tmp & 0xff; 22852286Smdodd enaddr[2] = eaddr_tmp >> 8; 22952286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Hi); 23052286Smdodd enaddr[1] = eaddr_tmp & 0xff; 23152286Smdodd enaddr[0] = eaddr_tmp >> 8; 23252286Smdodd 23352286Smdodd return; 23452286Smdodd} 23521769Sjkh 23652286Smdoddstatic int 23752286Smdoddex_card_type (u_char *enaddr) 23852286Smdodd{ 23952286Smdodd if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9)) 24052286Smdodd return (CARD_TYPE_EX_10_PLUS); 24152286Smdodd 24252286Smdodd return (CARD_TYPE_EX_10); 24352286Smdodd} 24452286Smdodd 24555882Smdodd/* 24655882Smdodd * Non-destructive identify. 24755882Smdodd */ 24855882Smdoddstatic void 24955882Smdoddex_isa_identify (driver_t *driver, device_t parent) 25055882Smdodd{ 25155882Smdodd device_t child; 25255882Smdodd u_int32_t ioport; 25355882Smdodd u_char enaddr[6]; 25455882Smdodd u_int irq; 25555882Smdodd int tmp; 25655882Smdodd const char * desc; 25755882Smdodd 25855882Smdodd for (ioport = 0x200; ioport < 0x3a0; ioport += 0x10) { 25955882Smdodd 26055882Smdodd /* No board found at address */ 26155882Smdodd if (!look_for_card(ioport)) { 26255882Smdodd continue; 26355882Smdodd } 26455882Smdodd 26555882Smdodd /* Board in PnP mode */ 26655882Smdodd if (eeprom_read(ioport, 0) & 0x01) { 26755882Smdodd continue; 26855882Smdodd } 26955882Smdodd 27055882Smdodd bzero(enaddr, sizeof(enaddr)); 27155882Smdodd 27255882Smdodd /* Reset the card. */ 27355882Smdodd outb(ioport + CMD_REG, Reset_CMD); 27455882Smdodd DELAY(400); 27555882Smdodd 27655882Smdodd ex_get_address(ioport, enaddr); 27755882Smdodd tmp = eeprom_read(ioport, EE_IRQ_No) & IRQ_No_Mask; 27855882Smdodd 27955882Smdodd /* work out which set of irq <-> internal tables to use */ 28055882Smdodd if (ex_card_type(enaddr) == CARD_TYPE_EX_10_PLUS) { 28155882Smdodd irq = plus_ee2irqmap[tmp]; 28255882Smdodd desc = "Intel Pro/10+"; 28355882Smdodd } else { 28455882Smdodd irq = ee2irqmap[tmp]; 28555882Smdodd desc = "Intel Pro/10"; 28655882Smdodd } 28755882Smdodd 28855882Smdodd child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ex", -1); 28955882Smdodd device_set_desc_copy(child, desc); 29055882Smdodd device_set_driver(child, driver); 29155882Smdodd bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); 29255882Smdodd bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, EX_IOSIZE); 29355882Smdodd } 29455882Smdodd 29555882Smdodd return; 29655882Smdodd} 29755882Smdodd 29852286Smdoddstatic int 29952286Smdoddex_isa_probe(device_t dev) 30052286Smdodd{ 30152286Smdodd u_int iobase; 30252286Smdodd u_int irq; 30352286Smdodd char * irq2ee; 30452286Smdodd u_char * ee2irq; 30552286Smdodd u_char enaddr[6]; 30652286Smdodd int tmp; 30752286Smdodd int error; 30852286Smdodd 30952286Smdodd DODEBUG(Start_End, printf("ex_probe: start\n");); 31052286Smdodd 31152286Smdodd /* Check isapnp ids */ 31252286Smdodd error = ISA_PNP_PROBE(device_get_parent(dev), dev, ex_ids); 31352286Smdodd 31452286Smdodd /* If the card had a PnP ID that didn't match any we know about */ 31552286Smdodd if (error == ENXIO) { 31652286Smdodd return(error); 31752286Smdodd } 31852286Smdodd 31952286Smdodd /* If we had some other problem. */ 32052286Smdodd if (!(error == 0 || error == ENOENT)) { 32152286Smdodd return(error); 32252286Smdodd } 32352286Smdodd 32452286Smdodd iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0); 32555882Smdodd if (iobase && !look_for_card(iobase)) { 32655882Smdodd printf("ex: no card found at 0x%03x\n", iobase); 32755882Smdodd return(ENXIO); 32821769Sjkh } 32921769Sjkh 33021769Sjkh /* 33121769Sjkh * Reset the card. 33221769Sjkh */ 33321769Sjkh outb(iobase + CMD_REG, Reset_CMD); 33429313Smsmith DELAY(400); 33521769Sjkh 33652286Smdodd ex_get_address(iobase, enaddr); 33752286Smdodd 33852286Smdodd /* work out which set of irq <-> internal tables to use */ 33952286Smdodd if (ex_card_type(enaddr) == CARD_TYPE_EX_10_PLUS) { 34052286Smdodd irq2ee = plus_irq2eemap; 34152286Smdodd ee2irq = plus_ee2irqmap; 34252286Smdodd } else { 34352286Smdodd irq2ee = irq2eemap; 34452286Smdodd ee2irq = ee2irqmap; 34552286Smdodd } 34652286Smdodd 34752286Smdodd tmp = eeprom_read(iobase, EE_IRQ_No) & IRQ_No_Mask; 34852286Smdodd irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0); 34952286Smdodd 35052286Smdodd if (irq > 0) { 35155882Smdodd /* This will happen if board is in PnP mode. */ 35252286Smdodd if (ee2irq[tmp] != irq) { 35352286Smdodd printf("ex: WARNING: board's EEPROM is configured" 35452286Smdodd " for IRQ %d, using %d\n", 35552286Smdodd ee2irq[tmp], irq); 35652286Smdodd } 35752286Smdodd } else { 35852286Smdodd irq = ee2irq[tmp]; 35952286Smdodd bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1); 36052286Smdodd } 36152286Smdodd 36252286Smdodd if (irq == 0) { 36352286Smdodd printf("ex: invalid IRQ.\n"); 36452286Smdodd return(ENXIO); 36552286Smdodd } 36652286Smdodd 36752286Smdodd DODEBUG(Start_End, printf("ex_probe: finish\n");); 36852286Smdodd 36952286Smdodd return(0); 37052286Smdodd} 37152286Smdodd 37255883Smdoddstatic int 37355883Smdoddex_isa_attach(device_t dev) 37452286Smdodd{ 37552286Smdodd struct ex_softc * sc = device_get_softc(dev); 37652286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 37757987Smdodd struct ifmedia * ifm; 37852286Smdodd int unit = device_get_unit(dev); 37952286Smdodd int error; 38052286Smdodd int rid; 38152286Smdodd void * ih; 38257987Smdodd u_int16_t temp; 38352286Smdodd 38452286Smdodd DODEBUG(Start_End, device_printf(dev, "start\n");); 38552286Smdodd 38652286Smdodd rid = 0; 38752286Smdodd sc->ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 38852286Smdodd 0, ~0, 1, RF_ACTIVE); 38952286Smdodd 39052286Smdodd if (!sc->ioport) { 39152286Smdodd device_printf(dev, "No I/O space?!\n"); 39252286Smdodd goto bad; 39352286Smdodd } 39452286Smdodd 39552286Smdodd rid = 0; 39652286Smdodd sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 39752286Smdodd 0, ~0, 1, RF_ACTIVE); 39852286Smdodd 39952286Smdodd if (!sc->irq) { 40052286Smdodd device_printf(dev, "No IRQ?!\n"); 40152286Smdodd goto bad; 40252286Smdodd } 40352286Smdodd 40452286Smdodd error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, 40555883Smdodd ex_intr, (void *)sc, &ih); 40652286Smdodd 40752286Smdodd if (error) { 40852286Smdodd device_printf(dev, "bus_setup_intr() failed!\n"); 40952286Smdodd goto bad; 41052286Smdodd } 41152286Smdodd 41221769Sjkh /* 41321769Sjkh * Fill in several fields of the softc structure: 41421769Sjkh * - I/O base address. 41521769Sjkh * - Hardware Ethernet address. 41621769Sjkh * - IRQ number (if not supplied in config file, read it from EEPROM). 41721769Sjkh * - Connector type. 41821769Sjkh */ 41952286Smdodd sc->dev = dev; 42052286Smdodd sc->iobase = rman_get_start(sc->ioport); 42152286Smdodd sc->irq_no = rman_get_start(sc->irq); 42229877Smsmith 42352286Smdodd ex_get_address(sc->iobase, sc->arpcom.ac_enaddr); 42452286Smdodd 42529877Smsmith /* work out which set of irq <-> internal tables to use */ 42652286Smdodd if (ex_card_type(sc->arpcom.ac_enaddr) == CARD_TYPE_EX_10_PLUS) { 42729877Smsmith sc->irq2ee = plus_irq2eemap; 42829877Smsmith sc->ee2irq = plus_ee2irqmap; 42952286Smdodd } else { 43029877Smsmith sc->irq2ee = irq2eemap; 43129877Smsmith sc->ee2irq = ee2irqmap; 43229877Smsmith } 43329877Smsmith 43421769Sjkh sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */ 43521769Sjkh 43621769Sjkh /* 43721769Sjkh * Initialize the ifnet structure. 43821769Sjkh */ 43921769Sjkh ifp->if_softc = sc; 44021769Sjkh ifp->if_unit = unit; 44121769Sjkh ifp->if_name = "ex"; 44255883Smdodd ifp->if_mtu = ETHERMTU; 44355883Smdodd ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST /* XXX not done yet. | IFF_MULTICAST */; 44421769Sjkh ifp->if_output = ether_output; 44521769Sjkh ifp->if_start = ex_start; 44621769Sjkh ifp->if_ioctl = ex_ioctl; 44721769Sjkh ifp->if_watchdog = ex_watchdog; 44855883Smdodd ifp->if_init = ex_init; 44955883Smdodd ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 45021769Sjkh 45157987Smdodd ifmedia_init(&sc->ifmedia, 0, ex_ifmedia_upd, ex_ifmedia_sts); 45257987Smdodd 45357987Smdodd temp = eeprom_read(sc->iobase, EE_W5); 45457987Smdodd if (temp & EE_W5_PORT_TPE) 45557987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 45657987Smdodd if (temp & EE_W5_PORT_BNC) 45757987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); 45857987Smdodd if (temp & EE_W5_PORT_AUI) 45957987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 46057987Smdodd 46157987Smdodd ifmedia_set(&sc->ifmedia, ex_get_media(sc->iobase)); 46257987Smdodd 46357987Smdodd ifm = &sc->ifmedia; 46457987Smdodd ifm->ifm_media = ifm->ifm_cur->ifm_media; 46557987Smdodd ex_ifmedia_upd(ifp); 46657987Smdodd 46721769Sjkh /* 46821769Sjkh * Attach the interface. 46921769Sjkh */ 47021769Sjkh if_attach(ifp); 47121769Sjkh ether_ifattach(ifp); 47221769Sjkh 47357987Smdodd temp = eeprom_read(sc->iobase, EE_W0); 47457987Smdodd device_printf(sc->dev, "%s config, %s bus, ", 47557987Smdodd (temp & EE_W0_PNP) ? "PnP" : "Manual", 47657987Smdodd (temp & EE_W0_BUS16) ? "16-bit" : "8-bit"); 47757987Smdodd 47857987Smdodd temp = eeprom_read(sc->iobase, EE_W6); 47957987Smdodd printf("board id 0x%03x, stepping 0x%01x\n", 48057987Smdodd (temp & EE_W6_BOARD_MASK) >> EE_W6_BOARD_SHIFT, 48157987Smdodd temp & EE_W6_STEP_MASK); 48257987Smdodd 48355883Smdodd device_printf(sc->dev, "Ethernet address %6D\n", 48455883Smdodd sc->arpcom.ac_enaddr, ":"); 48521769Sjkh /* 48621769Sjkh * If BPF is in the kernel, call the attach for it 48721769Sjkh */ 48821769Sjkh bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 48955883Smdodd DODEBUG(Start_End, printf("ex_isa_attach%d: finish\n", unit);); 49052286Smdodd 49152286Smdodd return(0); 49252286Smdoddbad: 49352286Smdodd 49452286Smdodd if (sc->ioport) 49552286Smdodd bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->ioport); 49652286Smdodd if (sc->irq) 49752286Smdodd bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 49852286Smdodd 49952286Smdodd return (-1); 50021769Sjkh} 50121769Sjkh 50221769Sjkh 50355883Smdoddstatic void 50455883Smdoddex_init(void *xsc) 50521769Sjkh{ 50652286Smdodd struct ex_softc * sc = (struct ex_softc *) xsc; 50752286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 50852286Smdodd int s; 50952286Smdodd int i; 51052286Smdodd register int iobase = sc->iobase; 51152286Smdodd unsigned short temp_reg; 51221769Sjkh 51321769Sjkh DODEBUG(Start_End, printf("ex_init%d: start\n", ifp->if_unit);); 51421769Sjkh 51552286Smdodd if (ifp->if_addrhead.tqh_first == NULL) { 51652286Smdodd return; 51752286Smdodd } 51821769Sjkh s = splimp(); 51921769Sjkh sc->arpcom.ac_if.if_timer = 0; 52021769Sjkh 52121769Sjkh /* 52221769Sjkh * Load the ethernet address into the card. 52321769Sjkh */ 52421769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 52521769Sjkh temp_reg = inb(iobase + EEPROM_REG); 52652286Smdodd if (temp_reg & Trnoff_Enable) { 52752286Smdodd outb(iobase + EEPROM_REG, temp_reg & ~Trnoff_Enable); 52852286Smdodd } 52952286Smdodd for (i = 0; i < ETHER_ADDR_LEN; i++) { 53052286Smdodd outb(iobase + I_ADDR_REG0 + i, sc->arpcom.ac_enaddr[i]); 53152286Smdodd } 53221769Sjkh /* 53321769Sjkh * - Setup transmit chaining and discard bad received frames. 53421769Sjkh * - Match broadcast. 53521769Sjkh * - Clear test mode. 53621769Sjkh * - Set receiving mode. 53721769Sjkh * - Set IRQ number. 53821769Sjkh */ 53921769Sjkh outb(iobase + REG1, inb(iobase + REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr); 54021769Sjkh outb(iobase + REG2, inb(iobase + REG2) | No_SA_Ins | RX_CRC_InMem); 54126545Sgibbs outb(iobase + REG3, inb(iobase + REG3) & 0x3f /* XXX constants. */ ); 54221769Sjkh outb(iobase + CMD_REG, Bank1_Sel); 54329877Smsmith outb(iobase + INT_NO_REG, (inb(iobase + INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]); 54421769Sjkh 54521769Sjkh /* 54621769Sjkh * Divide the available memory in the card into rcv and xmt buffers. 54721769Sjkh * By default, I use the first 3/4 of the memory for the rcv buffer, 54821769Sjkh * and the remaining 1/4 of the memory for the xmt buffer. 54921769Sjkh */ 55021769Sjkh sc->rx_mem_size = sc->mem_size * 3 / 4; 55121769Sjkh sc->tx_mem_size = sc->mem_size - sc->rx_mem_size; 55221769Sjkh sc->rx_lower_limit = 0x0000; 55321769Sjkh sc->rx_upper_limit = sc->rx_mem_size - 2; 55421769Sjkh sc->tx_lower_limit = sc->rx_mem_size; 55521769Sjkh sc->tx_upper_limit = sc->mem_size - 2; 55621769Sjkh outb(iobase + RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8); 55721769Sjkh outb(iobase + RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8); 55821769Sjkh outb(iobase + XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8); 55921769Sjkh outb(iobase + XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8); 56021769Sjkh 56121769Sjkh /* 56221769Sjkh * Enable receive and transmit interrupts, and clear any pending int. 56321769Sjkh */ 56421769Sjkh outb(iobase + REG1, inb(iobase + REG1) | TriST_INT); 56521769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 56621769Sjkh outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 56721769Sjkh outb(iobase + STATUS_REG, All_Int); 56821769Sjkh 56921769Sjkh /* 57021769Sjkh * Initialize receive and transmit ring buffers. 57121769Sjkh */ 57221769Sjkh outw(iobase + RCV_BAR, sc->rx_lower_limit); 57321769Sjkh sc->rx_head = sc->rx_lower_limit; 57426545Sgibbs outw(iobase + RCV_STOP_REG, sc->rx_upper_limit | 0xfe); 57521769Sjkh outw(iobase + XMT_BAR, sc->tx_lower_limit); 57621769Sjkh sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 57721769Sjkh 57821769Sjkh ifp->if_flags |= IFF_RUNNING; 57921769Sjkh ifp->if_flags &= ~IFF_OACTIVE; 58021769Sjkh DODEBUG(Status, printf("OIDLE init\n");); 58121769Sjkh 58221769Sjkh /* 58321769Sjkh * Final reset of the board, and enable operation. 58421769Sjkh */ 58521769Sjkh outb(iobase + CMD_REG, Sel_Reset_CMD); 58621769Sjkh DELAY(2); 58721769Sjkh outb(iobase + CMD_REG, Rcv_Enable_CMD); 58821769Sjkh 58921769Sjkh ex_start(ifp); 59021769Sjkh splx(s); 59121769Sjkh 59221769Sjkh DODEBUG(Start_End, printf("ex_init%d: finish\n", ifp->if_unit);); 59321769Sjkh} 59421769Sjkh 59521769Sjkh 59655883Smdoddstatic void 59752286Smdoddex_start(struct ifnet *ifp) 59821769Sjkh{ 59952286Smdodd struct ex_softc * sc = ifp->if_softc; 60052286Smdodd int iobase = sc->iobase; 60152286Smdodd int i, s, len, data_len, avail, dest, next; 60252286Smdodd unsigned char tmp16[2]; 60352286Smdodd struct mbuf * opkt; 60452286Smdodd struct mbuf * m; 60521769Sjkh 60652286Smdodd DODEBUG(Start_End, printf("ex_start%d: start\n", unit);); 60721769Sjkh 60852286Smdodd s = splimp(); 60921769Sjkh 61052286Smdodd /* 61152286Smdodd * Main loop: send outgoing packets to network card until there are no 61252286Smdodd * more packets left, or the card cannot accept any more yet. 61352286Smdodd */ 61452286Smdodd while (((opkt = ifp->if_snd.ifq_head) != NULL) && 61552286Smdodd !(ifp->if_flags & IFF_OACTIVE)) { 61621769Sjkh 61752286Smdodd /* 61852286Smdodd * Ensure there is enough free transmit buffer space for 61952286Smdodd * this packet, including its header. Note: the header 62052286Smdodd * cannot wrap around the end of the transmit buffer and 62152286Smdodd * must be kept together, so we allow space for twice the 62252286Smdodd * length of the header, just in case. 62352286Smdodd */ 62421769Sjkh 62552286Smdodd for (len = 0, m = opkt; m != NULL; m = m->m_next) { 62652286Smdodd len += m->m_len; 62752286Smdodd } 62852286Smdodd 62952286Smdodd data_len = len; 63052286Smdodd 63152286Smdodd DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len);); 63252286Smdodd 63352286Smdodd if (len & 1) { 63452286Smdodd len += XMT_HEADER_LEN + 1; 63552286Smdodd } else { 63652286Smdodd len += XMT_HEADER_LEN; 63752286Smdodd } 63852286Smdodd 63952286Smdodd if ((i = sc->tx_tail - sc->tx_head) >= 0) { 64052286Smdodd avail = sc->tx_mem_size - i; 64152286Smdodd } else { 64252286Smdodd avail = -i; 64352286Smdodd } 64452286Smdodd 64552286Smdodd DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail);); 64652286Smdodd 64752286Smdodd if (avail >= len + XMT_HEADER_LEN) { 64852286Smdodd IF_DEQUEUE(&ifp->if_snd, opkt); 64952286Smdodd 65021769Sjkh#ifdef EX_PSA_INTR 65152286Smdodd /* 65252286Smdodd * Disable rx and tx interrupts, to avoid corruption 65352286Smdodd * of the host address register by interrupt service 65452286Smdodd * routines. 65552286Smdodd * XXX Is this necessary with splimp() enabled? 65652286Smdodd */ 65752286Smdodd outb(iobase + MASK_REG, All_Int); 65821769Sjkh#endif 65921769Sjkh 66052286Smdodd /* 66152286Smdodd * Compute the start and end addresses of this 66252286Smdodd * frame in the tx buffer. 66352286Smdodd */ 66452286Smdodd dest = sc->tx_tail; 66552286Smdodd next = dest + len; 66621769Sjkh 66752286Smdodd if (next > sc->tx_upper_limit) { 66852286Smdodd if ((sc->tx_upper_limit + 2 - sc->tx_tail) <= 66952286Smdodd XMT_HEADER_LEN) { 67052286Smdodd dest = sc->tx_lower_limit; 67152286Smdodd next = dest + len; 67255881Smdodd } else { 67355881Smdodd next = sc->tx_lower_limit + 67455881Smdodd next - sc->tx_upper_limit - 2; 67552286Smdodd } 67652286Smdodd } 67721769Sjkh 67852286Smdodd /* 67952286Smdodd * Build the packet frame in the card's ring buffer. 68052286Smdodd */ 68152286Smdodd DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next);); 68221769Sjkh 68352286Smdodd outw(iobase + HOST_ADDR_REG, dest); 68452286Smdodd outw(iobase + IO_PORT_REG, Transmit_CMD); 68552286Smdodd outw(iobase + IO_PORT_REG, 0); 68652286Smdodd outw(iobase + IO_PORT_REG, next); 68752286Smdodd outw(iobase + IO_PORT_REG, data_len); 68821769Sjkh 68952286Smdodd /* 69052286Smdodd * Output the packet data to the card. Ensure all 69152286Smdodd * transfers are 16-bit wide, even if individual 69252286Smdodd * mbufs have odd length. 69352286Smdodd */ 69421769Sjkh 69552286Smdodd for (m = opkt, i = 0; m != NULL; m = m->m_next) { 69652286Smdodd DODEBUG(Sent_Pkts, printf("[%d]", m->m_len);); 69752286Smdodd if (i) { 69852286Smdodd tmp16[1] = *(mtod(m, caddr_t)); 69952286Smdodd outsw(iobase + IO_PORT_REG, tmp16, 1); 70052286Smdodd } 70152286Smdodd outsw(iobase + IO_PORT_REG, 70252286Smdodd mtod(m, caddr_t) + i, (m->m_len - i) / 2); 70352286Smdodd 70452286Smdodd if ((i = (m->m_len - i) & 1) != 0) { 70552286Smdodd tmp16[0] = *(mtod(m, caddr_t) + 70652286Smdodd m->m_len - 1); 70752286Smdodd } 70852286Smdodd } 70952286Smdodd if (i) { 71052286Smdodd outsw(iobase + IO_PORT_REG, tmp16, 1); 71155881Smdodd } 71252286Smdodd 71355881Smdodd /* 71455881Smdodd * If there were other frames chained, update the 71555881Smdodd * chain in the last one. 71655881Smdodd */ 71755881Smdodd if (sc->tx_head != sc->tx_tail) { 71855881Smdodd if (sc->tx_tail != dest) { 71952286Smdodd outw(iobase + HOST_ADDR_REG, 72055881Smdodd sc->tx_last + XMT_Chain_Point); 72155881Smdodd outw(iobase + IO_PORT_REG, dest); 72252286Smdodd } 72355881Smdodd outw(iobase + HOST_ADDR_REG, 72455881Smdodd sc->tx_last + XMT_Byte_Count); 72555881Smdodd i = inw(iobase + IO_PORT_REG); 72655881Smdodd outw(iobase + HOST_ADDR_REG, 72755881Smdodd sc->tx_last + XMT_Byte_Count); 72855881Smdodd outw(iobase + IO_PORT_REG, i | Ch_bit); 72955881Smdodd } 73055881Smdodd 73155881Smdodd /* 73255881Smdodd * Resume normal operation of the card: 73355881Smdodd * - Make a dummy read to flush the DRAM write 73455881Smdodd * pipeline. 73555881Smdodd * - Enable receive and transmit interrupts. 73655881Smdodd * - Send Transmit or Resume_XMT command, as 73755881Smdodd * appropriate. 73855881Smdodd */ 73955881Smdodd inw(iobase + IO_PORT_REG); 74021769Sjkh#ifdef EX_PSA_INTR 74155881Smdodd outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 74221769Sjkh#endif 74355881Smdodd if (sc->tx_head == sc->tx_tail) { 74455881Smdodd outw(iobase + XMT_BAR, dest); 74555881Smdodd outb(iobase + CMD_REG, Transmit_CMD); 74655881Smdodd sc->tx_head = dest; 74755881Smdodd DODEBUG(Sent_Pkts, printf("Transmit\n");); 74852286Smdodd } else { 74955881Smdodd outb(iobase + CMD_REG, Resume_XMT_List_CMD); 75055881Smdodd DODEBUG(Sent_Pkts, printf("Resume\n");); 75152286Smdodd } 75255881Smdodd 75355881Smdodd sc->tx_last = dest; 75455881Smdodd sc->tx_tail = next; 75555881Smdodd 75655881Smdodd if (ifp->if_bpf != NULL) { 75755881Smdodd bpf_mtap(ifp, opkt); 75855881Smdodd } 75955881Smdodd 76055881Smdodd ifp->if_timer = 2; 76155881Smdodd ifp->if_opackets++; 76255881Smdodd m_freem(opkt); 76355881Smdodd } else { 76455881Smdodd ifp->if_flags |= IFF_OACTIVE; 76555881Smdodd DODEBUG(Status, printf("OACTIVE start\n");); 76652286Smdodd } 76721769Sjkh } 76821769Sjkh 76952286Smdodd splx(s); 77021769Sjkh 77152286Smdodd DODEBUG(Start_End, printf("ex_start%d: finish\n", unit);); 77221769Sjkh} 77321769Sjkh 77452286Smdoddstatic void 77552286Smdoddex_stop(struct ex_softc *sc) 77621769Sjkh{ 77752286Smdodd int iobase = sc->iobase; 77821769Sjkh 77952286Smdodd DODEBUG(Start_End, printf("ex_stop%d: start\n", unit);); 78021769Sjkh 78152286Smdodd /* 78252286Smdodd * Disable card operation: 78352286Smdodd * - Disable the interrupt line. 78452286Smdodd * - Flush transmission and disable reception. 78552286Smdodd * - Mask and clear all interrupts. 78652286Smdodd * - Reset the 82595. 78752286Smdodd */ 78852286Smdodd outb(iobase + CMD_REG, Bank1_Sel); 78952286Smdodd outb(iobase + REG1, inb(iobase + REG1) & ~TriST_INT); 79052286Smdodd outb(iobase + CMD_REG, Bank0_Sel); 79152286Smdodd outb(iobase + CMD_REG, Rcv_Stop); 79252286Smdodd sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 79352286Smdodd 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. */ 79452286Smdodd outb(iobase + MASK_REG, All_Int); 79552286Smdodd outb(iobase + STATUS_REG, All_Int); 79652286Smdodd outb(iobase + CMD_REG, Reset_CMD); 79752286Smdodd DELAY(200); 79821769Sjkh 79952286Smdodd DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit);); 80052286Smdodd 80152286Smdodd return; 80221769Sjkh} 80321769Sjkh 80421769Sjkh 80552286Smdoddstatic void 80655883Smdoddex_intr(void *arg) 80721769Sjkh{ 80852286Smdodd struct ex_softc * sc = (struct ex_softc *)arg; 80952286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 81052286Smdodd int iobase = sc->iobase; 81152286Smdodd int int_status, send_pkts; 81221769Sjkh 81355883Smdodd DODEBUG(Start_End, printf("ex_intr%d: start\n", unit);); 81421769Sjkh 81521769Sjkh#ifdef EXDEBUG 81621769Sjkh if (++exintr_count != 1) 81721769Sjkh printf("WARNING: nested interrupt (%d). Mail the author.\n", exintr_count); 81821769Sjkh#endif 81921769Sjkh 82052286Smdodd send_pkts = 0; 82152286Smdodd while ((int_status = inb(iobase + STATUS_REG)) & (Tx_Int | Rx_Int)) { 82252286Smdodd if (int_status & Rx_Int) { 82352286Smdodd outb(iobase + STATUS_REG, Rx_Int); 82421769Sjkh 82552286Smdodd ex_rx_intr(sc); 82652286Smdodd } else if (int_status & Tx_Int) { 82752286Smdodd outb(iobase + STATUS_REG, Tx_Int); 82821769Sjkh 82952286Smdodd ex_tx_intr(sc); 83052286Smdodd send_pkts = 1; 83152286Smdodd } 83252286Smdodd } 83321769Sjkh 83452286Smdodd /* 83552286Smdodd * If any packet has been transmitted, and there are queued packets to 83652286Smdodd * be sent, attempt to send more packets to the network card. 83752286Smdodd */ 83852286Smdodd 83952286Smdodd if (send_pkts && (ifp->if_snd.ifq_head != NULL)) { 84052286Smdodd ex_start(ifp); 84152286Smdodd } 84252286Smdodd 84321769Sjkh#ifdef EXDEBUG 84421769Sjkh exintr_count--; 84521769Sjkh#endif 84621769Sjkh 84755883Smdodd DODEBUG(Start_End, printf("ex_intr%d: finish\n", unit);); 84852286Smdodd 84952286Smdodd return; 85021769Sjkh} 85121769Sjkh 85252286Smdoddstatic void 85352286Smdoddex_tx_intr(struct ex_softc *sc) 85421769Sjkh{ 85552286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 85652286Smdodd int iobase = sc->iobase; 85752286Smdodd int tx_status; 85821769Sjkh 85952286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit);); 86021769Sjkh 86152286Smdodd /* 86252286Smdodd * - Cancel the watchdog. 86352286Smdodd * For all packets transmitted since last transmit interrupt: 86452286Smdodd * - Advance chain pointer to next queued packet. 86552286Smdodd * - Update statistics. 86652286Smdodd */ 86721769Sjkh 86852286Smdodd ifp->if_timer = 0; 86921769Sjkh 87052286Smdodd while (sc->tx_head != sc->tx_tail) { 87152286Smdodd outw(iobase + HOST_ADDR_REG, sc->tx_head); 87221769Sjkh 87352286Smdodd if (! inw(iobase + IO_PORT_REG) & Done_bit) 87452286Smdodd break; 87521769Sjkh 87652286Smdodd tx_status = inw(iobase + IO_PORT_REG); 87752286Smdodd sc->tx_head = inw(iobase + IO_PORT_REG); 87852286Smdodd 87952286Smdodd if (tx_status & TX_OK_bit) { 88052286Smdodd ifp->if_opackets++; 88152286Smdodd } else { 88252286Smdodd ifp->if_oerrors++; 88352286Smdodd } 88452286Smdodd 88552286Smdodd ifp->if_collisions += tx_status & No_Collisions_bits; 88652286Smdodd } 88752286Smdodd 88852286Smdodd /* 88952286Smdodd * The card should be ready to accept more packets now. 89052286Smdodd */ 89152286Smdodd 89252286Smdodd ifp->if_flags &= ~IFF_OACTIVE; 89352286Smdodd 89452286Smdodd DODEBUG(Status, printf("OIDLE tx_intr\n");); 89552286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit);); 89652286Smdodd 89752286Smdodd return; 89821769Sjkh} 89921769Sjkh 90052286Smdoddstatic void 90152286Smdoddex_rx_intr(struct ex_softc *sc) 90221769Sjkh{ 90352286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 90452286Smdodd int iobase = sc->iobase; 90552286Smdodd int rx_status; 90652286Smdodd int pkt_len; 90752286Smdodd int QQQ; 90852286Smdodd struct mbuf * m; 90952286Smdodd struct mbuf * ipkt; 91052286Smdodd struct ether_header * eh; 91121769Sjkh 91252286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit);); 91321769Sjkh 91452286Smdodd /* 91552286Smdodd * For all packets received since last receive interrupt: 91652286Smdodd * - If packet ok, read it into a new mbuf and queue it to interface, 91752286Smdodd * updating statistics. 91852286Smdodd * - If packet bad, just discard it, and update statistics. 91952286Smdodd * Finally, advance receive stop limit in card's memory to new location. 92052286Smdodd */ 92121769Sjkh 92252286Smdodd outw(iobase + HOST_ADDR_REG, sc->rx_head); 92321769Sjkh 92452286Smdodd while (inw(iobase + IO_PORT_REG) == RCV_Done) { 92552286Smdodd 92652286Smdodd rx_status = inw(iobase + IO_PORT_REG); 92752286Smdodd sc->rx_head = inw(iobase + IO_PORT_REG); 92852286Smdodd QQQ = pkt_len = inw(iobase + IO_PORT_REG); 92952286Smdodd 93052286Smdodd if (rx_status & RCV_OK_bit) { 93152286Smdodd MGETHDR(m, M_DONTWAIT, MT_DATA); 93252286Smdodd ipkt = m; 93352286Smdodd if (ipkt == NULL) { 93452286Smdodd ifp->if_iqdrops++; 93552286Smdodd } else { 93652286Smdodd ipkt->m_pkthdr.rcvif = ifp; 93752286Smdodd ipkt->m_pkthdr.len = pkt_len; 93852286Smdodd ipkt->m_len = MHLEN; 93952286Smdodd 94052286Smdodd while (pkt_len > 0) { 94152286Smdodd if (pkt_len > MINCLSIZE) { 94252286Smdodd MCLGET(m, M_DONTWAIT); 94352286Smdodd if (m->m_flags & M_EXT) { 94452286Smdodd m->m_len = MCLBYTES; 94552286Smdodd } else { 94652286Smdodd m_freem(ipkt); 94752286Smdodd ifp->if_iqdrops++; 94852286Smdodd goto rx_another; 94952286Smdodd } 95052286Smdodd } 95152286Smdodd m->m_len = min(m->m_len, pkt_len); 95252286Smdodd 95321769Sjkh /* 95421769Sjkh * NOTE: I'm assuming that all mbufs allocated are of even length, 95521769Sjkh * except for the last one in an odd-length packet. 95621769Sjkh */ 95752286Smdodd 95852286Smdodd insw(iobase + IO_PORT_REG, 95952286Smdodd mtod(m, caddr_t), m->m_len / 2); 96052286Smdodd 96152286Smdodd if (m->m_len & 1) { 96252286Smdodd *(mtod(m, caddr_t) + m->m_len - 1) = inb(iobase + IO_PORT_REG); 96352286Smdodd } 96452286Smdodd pkt_len -= m->m_len; 96552286Smdodd 96652286Smdodd if (pkt_len > 0) { 96752286Smdodd MGET(m->m_next, M_DONTWAIT, MT_DATA); 96852286Smdodd if (m->m_next == NULL) { 96952286Smdodd m_freem(ipkt); 97052286Smdodd ifp->if_iqdrops++; 97152286Smdodd goto rx_another; 97252286Smdodd } 97352286Smdodd m = m->m_next; 97452286Smdodd m->m_len = MLEN; 97552286Smdodd } 97652286Smdodd } 97752286Smdodd eh = mtod(ipkt, struct ether_header *); 97852286Smdodd#ifdef EXDEBUG 97952286Smdodd if (debug_mask & Rcvd_Pkts) { 98052286Smdodd if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) { 98152286Smdodd printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":"); 98252286Smdodd printf("%6D\n", eh->ether_dhost, ":"); 98352286Smdodd } /* QQQ */ 98421769Sjkh } 98521769Sjkh#endif 98652286Smdodd if (ifp->if_bpf != NULL) { 98752286Smdodd bpf_mtap(ifp, ipkt); 98821769Sjkh 98921769Sjkh /* 99052286Smdodd * Note that the interface cannot be in promiscuous mode 99152286Smdodd * if there are no BPF listeners. And if we are in 99252286Smdodd * promiscuous mode, we have to check if this packet is 99352286Smdodd * really ours. 99421769Sjkh */ 99552286Smdodd if ((ifp->if_flags & IFF_PROMISC) && 99652286Smdodd (eh->ether_dhost[0] & 1) == 0 && 99752286Smdodd bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)) != 0 && 99852286Smdodd bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)) != 0) { 99952286Smdodd m_freem(ipkt); 100052286Smdodd goto rx_another; 100152286Smdodd } 100252286Smdodd } 100352286Smdodd m_adj(ipkt, sizeof(struct ether_header)); 100452286Smdodd ether_input(ifp, eh, ipkt); 100552286Smdodd ifp->if_ipackets++; 100652286Smdodd } 100752286Smdodd } else { 100852286Smdodd ifp->if_ierrors++; 100921769Sjkh } 101052286Smdodd outw(iobase + HOST_ADDR_REG, sc->rx_head); 101152286Smdoddrx_another: ; 101221769Sjkh } 101321769Sjkh 101452286Smdodd if (sc->rx_head < sc->rx_lower_limit + 2) 101552286Smdodd outw(iobase + RCV_STOP_REG, sc->rx_upper_limit); 101652286Smdodd else 101752286Smdodd outw(iobase + RCV_STOP_REG, sc->rx_head - 2); 101852286Smdodd 101952286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit);); 102052286Smdodd 102152286Smdodd return; 102221769Sjkh} 102321769Sjkh 102421769Sjkh 102555883Smdoddstatic int 102655883Smdoddex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 102721769Sjkh{ 102852286Smdodd struct ex_softc * sc = ifp->if_softc; 102957987Smdodd struct ifreq * ifr = (struct ifreq *)data; 103052286Smdodd int s; 103152286Smdodd int error = 0; 103221769Sjkh 103352286Smdodd DODEBUG(Start_End, printf("ex_ioctl%d: start ", ifp->if_unit);); 103421769Sjkh 103552286Smdodd s = splimp(); 103621769Sjkh 103752286Smdodd switch(cmd) { 103852286Smdodd case SIOCSIFADDR: 103952286Smdodd case SIOCGIFADDR: 104052286Smdodd case SIOCSIFMTU: 104152286Smdodd error = ether_ioctl(ifp, cmd, data); 104252286Smdodd break; 104321769Sjkh 104452286Smdodd case SIOCSIFFLAGS: 104552286Smdodd DODEBUG(Start_End, printf("SIOCSIFFLAGS");); 104652286Smdodd if ((ifp->if_flags & IFF_UP) == 0 && 104752286Smdodd (ifp->if_flags & IFF_RUNNING)) { 104852286Smdodd 104952286Smdodd ifp->if_flags &= ~IFF_RUNNING; 105052286Smdodd ex_stop(sc); 105152286Smdodd } else { 105252286Smdodd ex_init(sc); 105352286Smdodd } 105452286Smdodd break; 105521769Sjkh#ifdef NODEF 105652286Smdodd case SIOCGHWADDR: 105752286Smdodd DODEBUG(Start_End, printf("SIOCGHWADDR");); 105852286Smdodd bcopy((caddr_t)sc->sc_addr, (caddr_t)&ifr->ifr_data, 105952286Smdodd sizeof(sc->sc_addr)); 106052286Smdodd break; 106121769Sjkh#endif 106252286Smdodd case SIOCADDMULTI: 106352286Smdodd DODEBUG(Start_End, printf("SIOCADDMULTI");); 106452286Smdodd case SIOCDELMULTI: 106552286Smdodd DODEBUG(Start_End, printf("SIOCDELMULTI");); 106652286Smdodd /* XXX Support not done yet. */ 106752286Smdodd error = EINVAL; 106852286Smdodd break; 106957987Smdodd case SIOCSIFMEDIA: 107057987Smdodd case SIOCGIFMEDIA: 107157987Smdodd error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd); 107257987Smdodd break; 107352286Smdodd default: 107452286Smdodd DODEBUG(Start_End, printf("unknown");); 107552286Smdodd error = EINVAL; 107652286Smdodd } 107721769Sjkh 107852286Smdodd splx(s); 107921769Sjkh 108052286Smdodd DODEBUG(Start_End, printf("\nex_ioctl%d: finish\n", ifp->if_unit);); 108152286Smdodd 108252286Smdodd return(error); 108321769Sjkh} 108421769Sjkh 108521769Sjkh 108652286Smdoddstatic void 108752286Smdoddex_reset(struct ex_softc *sc) 108821769Sjkh{ 108952286Smdodd int s; 109021769Sjkh 109152286Smdodd DODEBUG(Start_End, printf("ex_reset%d: start\n", unit);); 109221769Sjkh 109352286Smdodd s = splimp(); 109421769Sjkh 109552286Smdodd ex_stop(sc); 109652286Smdodd ex_init(sc); 109721769Sjkh 109852286Smdodd splx(s); 109921769Sjkh 110052286Smdodd DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit);); 110152286Smdodd 110252286Smdodd return; 110321769Sjkh} 110421769Sjkh 110552286Smdoddstatic void 110652286Smdoddex_watchdog(struct ifnet *ifp) 110721769Sjkh{ 110852286Smdodd struct ex_softc * sc = ifp->if_softc; 110921769Sjkh 111052286Smdodd DODEBUG(Start_End, printf("ex_watchdog%d: start\n", ifp->if_unit);); 111121769Sjkh 111252286Smdodd ifp->if_flags &= ~IFF_OACTIVE; 111321769Sjkh 111452286Smdodd DODEBUG(Status, printf("OIDLE watchdog\n");); 111552286Smdodd 111652286Smdodd ifp->if_oerrors++; 111752286Smdodd ex_reset(sc); 111852286Smdodd ex_start(ifp); 111952286Smdodd 112052286Smdodd DODEBUG(Start_End, printf("ex_watchdog%d: finish\n", ifp->if_unit);); 112152286Smdodd 112252286Smdodd return; 112321769Sjkh} 112421769Sjkh 112557987Smdoddstatic int 112657987Smdoddex_ifmedia_upd (ifp) 112757987Smdodd struct ifnet * ifp; 112857987Smdodd{ 112957987Smdodd struct ex_softc * sc = ifp->if_softc; 113021769Sjkh 113157987Smdodd return (0); 113257987Smdodd} 113357987Smdodd 113457987Smdoddstatic void 113557987Smdoddex_ifmedia_sts(ifp, ifmr) 113657987Smdodd struct ifnet * ifp; 113757987Smdodd struct ifmediareq * ifmr; 113857987Smdodd{ 113957987Smdodd struct ex_softc * sc = ifp->if_softc; 114057987Smdodd 114157987Smdodd ifmr->ifm_active = ex_get_media(sc->iobase); 114257987Smdodd 114357987Smdodd return; 114457987Smdodd} 114557987Smdodd 114652286Smdoddstatic u_short 114752286Smdoddeeprom_read(int iobase, int location) 114821769Sjkh{ 114921769Sjkh int i; 115021769Sjkh u_short data = 0; 115121769Sjkh int ee_addr; 115221769Sjkh int read_cmd = location | EE_READ_CMD; 115321769Sjkh short ctrl_val = EECS; 115421769Sjkh 115521769Sjkh ee_addr = iobase + EEPROM_REG; 115621769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 115721769Sjkh outb(ee_addr, EECS); 115821769Sjkh for (i = 8; i >= 0; i--) { 115921769Sjkh short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; 116021769Sjkh outb(ee_addr, outval); 116121769Sjkh outb(ee_addr, outval | EESK); 116221769Sjkh DELAY(3); 116321769Sjkh outb(ee_addr, outval); 116421769Sjkh DELAY(2); 116521769Sjkh } 116621769Sjkh outb(ee_addr, ctrl_val); 116721769Sjkh 116821769Sjkh for (i = 16; i > 0; i--) { 116921769Sjkh outb(ee_addr, ctrl_val | EESK); 117021769Sjkh DELAY(3); 117121769Sjkh data = (data << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); 117221769Sjkh outb(ee_addr, ctrl_val); 117321769Sjkh DELAY(2); 117421769Sjkh } 117521769Sjkh 117621769Sjkh ctrl_val &= ~EECS; 117721769Sjkh outb(ee_addr, ctrl_val | EESK); 117821769Sjkh DELAY(3); 117921769Sjkh outb(ee_addr, ctrl_val); 118021769Sjkh DELAY(2); 118121769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 118221769Sjkh return(data); 118321769Sjkh} 1184