if_ex.c revision 112801
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 112801 2003-03-29 15:38:53Z 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 * 38112731Smdodd * dd-mmm-yyyy: Multicast support ported from NetBSD's if_iy driver. 3921769Sjkh * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast. 4021769Sjkh */ 4121769Sjkh 4221769Sjkh#include <sys/param.h> 4321769Sjkh#include <sys/systm.h> 4452286Smdodd#include <sys/kernel.h> 4524204Sbde#include <sys/sockio.h> 4621769Sjkh#include <sys/mbuf.h> 4721769Sjkh#include <sys/socket.h> 4821769Sjkh 4952286Smdodd#include <sys/module.h> 5052286Smdodd#include <sys/bus.h> 5152286Smdodd 5252286Smdodd#include <machine/bus.h> 5352286Smdodd#include <machine/resource.h> 5452286Smdodd#include <sys/rman.h> 5552286Smdodd 5657987Smdodd#include <net/if.h> 5757987Smdodd#include <net/if_arp.h> 58112731Smdodd#include <net/if_dl.h> 5957987Smdodd#include <net/if_media.h> 6050026Smdodd#include <net/ethernet.h> 6157987Smdodd#include <net/bpf.h> 6221769Sjkh 6350026Smdodd#include <netinet/in.h> 6450026Smdodd#include <netinet/if_ether.h> 6550026Smdodd 6621769Sjkh 6752286Smdodd#include <isa/isavar.h> 6852286Smdodd#include <isa/pnpvar.h> 6952286Smdodd 7055953Speter#include <dev/ex/if_exreg.h> 7159816Smdodd#include <dev/ex/if_exvar.h> 7221769Sjkh 7321769Sjkh#ifdef EXDEBUG 7452286Smdodd# define Start_End 1 7552286Smdodd# define Rcvd_Pkts 2 7652286Smdodd# define Sent_Pkts 4 7752286Smdodd# define Status 8 7821769Sjkhstatic int debug_mask = 0; 7921769Sjkhstatic int exintr_count = 0; 8052286Smdodd# define DODEBUG(level, action) if (level & debug_mask) action 8121769Sjkh#else 8252286Smdodd# define DODEBUG(level, action) 8321769Sjkh#endif 8421769Sjkh 85112801Smdodddevclass_t ex_devclass; 86112801Smdodd 8759816Smdoddchar irq2eemap[] = 8852286Smdodd { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 }; 8959816Smdoddu_char ee2irqmap[] = 9052286Smdodd { 9, 3, 5, 10, 11, 0, 0, 0 }; 9159816Smdodd 9259816Smdoddchar plus_irq2eemap[] = 9352286Smdodd { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 }; 9459816Smdoddu_char plus_ee2irqmap[] = 9552286Smdodd { 3, 4, 5, 7, 9, 10, 11, 12 }; 9621769Sjkh 9752286Smdodd/* Network Interface Functions */ 9892739Salfredstatic void ex_init (void *); 9992739Salfredstatic void ex_start (struct ifnet *); 10092739Salfredstatic int ex_ioctl (struct ifnet *, u_long, caddr_t); 10192739Salfredstatic void ex_watchdog (struct ifnet *); 10221769Sjkh 10357987Smdodd/* ifmedia Functions */ 10492739Salfredstatic int ex_ifmedia_upd (struct ifnet *); 10592739Salfredstatic void ex_ifmedia_sts (struct ifnet *, struct ifmediareq *); 10657987Smdodd 10792739Salfredstatic int ex_get_media (u_int32_t iobase); 10859816Smdodd 10992739Salfredstatic void ex_reset (struct ex_softc *); 110112731Smdoddstatic void ex_setmulti (struct ex_softc *); 11152286Smdodd 11292739Salfredstatic void ex_tx_intr (struct ex_softc *); 11392739Salfredstatic void ex_rx_intr (struct ex_softc *); 11452286Smdodd 11559816Smdoddint 11659816Smdoddlook_for_card (u_int32_t iobase) 11721769Sjkh{ 11821769Sjkh int count1, count2; 11921769Sjkh 12021769Sjkh /* 12121769Sjkh * Check for the i82595 signature, and check that the round robin 12221769Sjkh * counter actually advances. 12321769Sjkh */ 12421769Sjkh if (((count1 = inb(iobase + ID_REG)) & Id_Mask) != Id_Sig) 12521769Sjkh return(0); 12621769Sjkh count2 = inb(iobase + ID_REG); 12721769Sjkh count2 = inb(iobase + ID_REG); 12821769Sjkh count2 = inb(iobase + ID_REG); 12952286Smdodd 13021769Sjkh return((count2 & Counter_bits) == ((count1 + 0xc0) & Counter_bits)); 13121769Sjkh} 13221769Sjkh 13359816Smdoddvoid 13452286Smdoddex_get_address (u_int32_t iobase, u_char *enaddr) 13521769Sjkh{ 13652286Smdodd u_int16_t eaddr_tmp; 13721769Sjkh 13852286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Lo); 13952286Smdodd enaddr[5] = eaddr_tmp & 0xff; 14052286Smdodd enaddr[4] = eaddr_tmp >> 8; 14152286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Mid); 14252286Smdodd enaddr[3] = eaddr_tmp & 0xff; 14352286Smdodd enaddr[2] = eaddr_tmp >> 8; 14452286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Hi); 14552286Smdodd enaddr[1] = eaddr_tmp & 0xff; 14652286Smdodd enaddr[0] = eaddr_tmp >> 8; 14752286Smdodd 14852286Smdodd return; 14952286Smdodd} 15021769Sjkh 15159816Smdoddint 15252286Smdoddex_card_type (u_char *enaddr) 15352286Smdodd{ 15452286Smdodd if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9)) 15552286Smdodd return (CARD_TYPE_EX_10_PLUS); 15652286Smdodd 15752286Smdodd return (CARD_TYPE_EX_10); 15852286Smdodd} 15952286Smdodd 16055882Smdodd/* 16159816Smdodd * Caller is responsible for eventually calling 16259816Smdodd * ex_release_resources() on failure. 16355882Smdodd */ 16459816Smdoddint 16559816Smdoddex_alloc_resources (device_t dev) 16655882Smdodd{ 16759816Smdodd struct ex_softc * sc = device_get_softc(dev); 16859816Smdodd int error = 0; 16955882Smdodd 17059816Smdodd sc->ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->ioport_rid, 17159816Smdodd 0, ~0, 1, RF_ACTIVE); 17259816Smdodd if (!sc->ioport) { 17359816Smdodd device_printf(dev, "No I/O space?!\n"); 17459816Smdodd error = ENOMEM; 17559816Smdodd goto bad; 17659816Smdodd } 17757989Smdodd 17859816Smdodd sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, 17959816Smdodd 0, ~0, 1, RF_ACTIVE); 18055882Smdodd 18159816Smdodd if (!sc->irq) { 18259816Smdodd device_printf(dev, "No IRQ?!\n"); 18359816Smdodd error = ENOMEM; 18459816Smdodd goto bad; 18555882Smdodd } 18655882Smdodd 18759816Smdoddbad: 18859816Smdodd return (error); 18955882Smdodd} 19055882Smdodd 19159816Smdoddvoid 19259816Smdoddex_release_resources (device_t dev) 19352286Smdodd{ 19459816Smdodd struct ex_softc * sc = device_get_softc(dev); 19552286Smdodd 19659816Smdodd if (sc->ih) { 19759816Smdodd bus_teardown_intr(dev, sc->irq, sc->ih); 19859816Smdodd sc->ih = NULL; 19952286Smdodd } 20052286Smdodd 20159816Smdodd if (sc->ioport) { 20259816Smdodd bus_release_resource(dev, SYS_RES_IOPORT, 20359816Smdodd sc->ioport_rid, sc->ioport); 20459816Smdodd sc->ioport = NULL; 20552286Smdodd } 20652286Smdodd 20759816Smdodd if (sc->irq) { 20859816Smdodd bus_release_resource(dev, SYS_RES_IRQ, 20959816Smdodd sc->irq_rid, sc->irq); 21059816Smdodd sc->irq = NULL; 21157989Smdodd } 21257989Smdodd 21359816Smdodd return; 21452286Smdodd} 21552286Smdodd 21659816Smdoddint 21759816Smdoddex_attach(device_t dev) 21852286Smdodd{ 21952286Smdodd struct ex_softc * sc = device_get_softc(dev); 22052286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 22157987Smdodd struct ifmedia * ifm; 22252286Smdodd int unit = device_get_unit(dev); 22357987Smdodd u_int16_t temp; 22452286Smdodd 22529877Smsmith /* work out which set of irq <-> internal tables to use */ 22652286Smdodd if (ex_card_type(sc->arpcom.ac_enaddr) == CARD_TYPE_EX_10_PLUS) { 22729877Smsmith sc->irq2ee = plus_irq2eemap; 22829877Smsmith sc->ee2irq = plus_ee2irqmap; 22952286Smdodd } else { 23029877Smsmith sc->irq2ee = irq2eemap; 23129877Smsmith sc->ee2irq = ee2irqmap; 23229877Smsmith } 23329877Smsmith 23421769Sjkh sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */ 23521769Sjkh 23621769Sjkh /* 23721769Sjkh * Initialize the ifnet structure. 23821769Sjkh */ 23921769Sjkh ifp->if_softc = sc; 24021769Sjkh ifp->if_unit = unit; 24121769Sjkh ifp->if_name = "ex"; 24255883Smdodd ifp->if_mtu = ETHERMTU; 243112731Smdodd ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 24421769Sjkh ifp->if_output = ether_output; 24521769Sjkh ifp->if_start = ex_start; 24621769Sjkh ifp->if_ioctl = ex_ioctl; 24721769Sjkh ifp->if_watchdog = ex_watchdog; 24855883Smdodd ifp->if_init = ex_init; 24955883Smdodd ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 25021769Sjkh 25157987Smdodd ifmedia_init(&sc->ifmedia, 0, ex_ifmedia_upd, ex_ifmedia_sts); 25257987Smdodd 25357987Smdodd temp = eeprom_read(sc->iobase, EE_W5); 25457987Smdodd if (temp & EE_W5_PORT_TPE) 25557987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 25657987Smdodd if (temp & EE_W5_PORT_BNC) 25757987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); 25857987Smdodd if (temp & EE_W5_PORT_AUI) 25957987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 26057987Smdodd 261112764Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); 262112764Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_NONE, 0, NULL); 26357987Smdodd ifmedia_set(&sc->ifmedia, ex_get_media(sc->iobase)); 26457987Smdodd 26557987Smdodd ifm = &sc->ifmedia; 26657987Smdodd ifm->ifm_media = ifm->ifm_cur->ifm_media; 26757987Smdodd ex_ifmedia_upd(ifp); 26857987Smdodd 26921769Sjkh /* 27021769Sjkh * Attach the interface. 27121769Sjkh */ 272106937Ssam ether_ifattach(ifp, sc->arpcom.ac_enaddr); 27321769Sjkh 27455883Smdodd device_printf(sc->dev, "Ethernet address %6D\n", 27555883Smdodd sc->arpcom.ac_enaddr, ":"); 27652286Smdodd 27752286Smdodd return(0); 27821769Sjkh} 27921769Sjkh 280112800Smdoddint 281112800Smdoddex_detach (device_t dev) 282112800Smdodd{ 283112800Smdodd struct ex_softc *sc; 284112800Smdodd struct ifnet *ifp; 285112800Smdodd 286112800Smdodd sc = device_get_softc(dev); 287112800Smdodd ifp = &sc->arpcom.ac_if; 288112800Smdodd 289112800Smdodd ex_stop(sc); 290112800Smdodd 291112800Smdodd ifp->if_flags &= ~IFF_RUNNING; 292112800Smdodd ether_ifdetach(ifp); 293112800Smdodd 294112800Smdodd ex_release_resources(dev); 295112800Smdodd 296112800Smdodd return (0); 297112800Smdodd} 298112800Smdodd 29955883Smdoddstatic void 30055883Smdoddex_init(void *xsc) 30121769Sjkh{ 30252286Smdodd struct ex_softc * sc = (struct ex_softc *) xsc; 30352286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 30452286Smdodd int s; 30552286Smdodd int i; 30652286Smdodd register int iobase = sc->iobase; 30752286Smdodd unsigned short temp_reg; 30821769Sjkh 30921769Sjkh DODEBUG(Start_End, printf("ex_init%d: start\n", ifp->if_unit);); 31021769Sjkh 31171999Sphk if (TAILQ_FIRST(&ifp->if_addrhead) == NULL) { 31252286Smdodd return; 31352286Smdodd } 31421769Sjkh s = splimp(); 31559816Smdodd ifp->if_timer = 0; 31621769Sjkh 31721769Sjkh /* 31821769Sjkh * Load the ethernet address into the card. 31921769Sjkh */ 32021769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 32121769Sjkh temp_reg = inb(iobase + EEPROM_REG); 32252286Smdodd if (temp_reg & Trnoff_Enable) { 32352286Smdodd outb(iobase + EEPROM_REG, temp_reg & ~Trnoff_Enable); 32452286Smdodd } 32552286Smdodd for (i = 0; i < ETHER_ADDR_LEN; i++) { 32652286Smdodd outb(iobase + I_ADDR_REG0 + i, sc->arpcom.ac_enaddr[i]); 32752286Smdodd } 32821769Sjkh /* 32921769Sjkh * - Setup transmit chaining and discard bad received frames. 33021769Sjkh * - Match broadcast. 33121769Sjkh * - Clear test mode. 33221769Sjkh * - Set receiving mode. 33321769Sjkh * - Set IRQ number. 33421769Sjkh */ 33521769Sjkh outb(iobase + REG1, inb(iobase + REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr); 33621769Sjkh outb(iobase + REG2, inb(iobase + REG2) | No_SA_Ins | RX_CRC_InMem); 33726545Sgibbs outb(iobase + REG3, inb(iobase + REG3) & 0x3f /* XXX constants. */ ); 33821769Sjkh outb(iobase + CMD_REG, Bank1_Sel); 33929877Smsmith outb(iobase + INT_NO_REG, (inb(iobase + INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]); 34021769Sjkh 34121769Sjkh /* 34221769Sjkh * Divide the available memory in the card into rcv and xmt buffers. 34321769Sjkh * By default, I use the first 3/4 of the memory for the rcv buffer, 34421769Sjkh * and the remaining 1/4 of the memory for the xmt buffer. 34521769Sjkh */ 34621769Sjkh sc->rx_mem_size = sc->mem_size * 3 / 4; 34721769Sjkh sc->tx_mem_size = sc->mem_size - sc->rx_mem_size; 34821769Sjkh sc->rx_lower_limit = 0x0000; 34921769Sjkh sc->rx_upper_limit = sc->rx_mem_size - 2; 35021769Sjkh sc->tx_lower_limit = sc->rx_mem_size; 35121769Sjkh sc->tx_upper_limit = sc->mem_size - 2; 35221769Sjkh outb(iobase + RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8); 35321769Sjkh outb(iobase + RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8); 35421769Sjkh outb(iobase + XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8); 35521769Sjkh outb(iobase + XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8); 35621769Sjkh 35721769Sjkh /* 35821769Sjkh * Enable receive and transmit interrupts, and clear any pending int. 35921769Sjkh */ 36021769Sjkh outb(iobase + REG1, inb(iobase + REG1) | TriST_INT); 36121769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 36221769Sjkh outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 36321769Sjkh outb(iobase + STATUS_REG, All_Int); 36421769Sjkh 36521769Sjkh /* 36621769Sjkh * Initialize receive and transmit ring buffers. 36721769Sjkh */ 36821769Sjkh outw(iobase + RCV_BAR, sc->rx_lower_limit); 36921769Sjkh sc->rx_head = sc->rx_lower_limit; 37026545Sgibbs outw(iobase + RCV_STOP_REG, sc->rx_upper_limit | 0xfe); 37121769Sjkh outw(iobase + XMT_BAR, sc->tx_lower_limit); 37221769Sjkh sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 37321769Sjkh 37421769Sjkh ifp->if_flags |= IFF_RUNNING; 37521769Sjkh ifp->if_flags &= ~IFF_OACTIVE; 37621769Sjkh DODEBUG(Status, printf("OIDLE init\n");); 37721769Sjkh 378112731Smdodd ex_setmulti(sc); 379112731Smdodd 38021769Sjkh /* 38121769Sjkh * Final reset of the board, and enable operation. 38221769Sjkh */ 38321769Sjkh outb(iobase + CMD_REG, Sel_Reset_CMD); 38421769Sjkh DELAY(2); 38521769Sjkh outb(iobase + CMD_REG, Rcv_Enable_CMD); 38621769Sjkh 38721769Sjkh ex_start(ifp); 38821769Sjkh splx(s); 38921769Sjkh 39021769Sjkh DODEBUG(Start_End, printf("ex_init%d: finish\n", ifp->if_unit);); 39121769Sjkh} 39221769Sjkh 39321769Sjkh 39455883Smdoddstatic void 39552286Smdoddex_start(struct ifnet *ifp) 39621769Sjkh{ 39752286Smdodd struct ex_softc * sc = ifp->if_softc; 39852286Smdodd int iobase = sc->iobase; 39952286Smdodd int i, s, len, data_len, avail, dest, next; 40052286Smdodd unsigned char tmp16[2]; 40152286Smdodd struct mbuf * opkt; 40252286Smdodd struct mbuf * m; 40321769Sjkh 40452286Smdodd DODEBUG(Start_End, printf("ex_start%d: start\n", unit);); 40521769Sjkh 40652286Smdodd s = splimp(); 40721769Sjkh 40852286Smdodd /* 40952286Smdodd * Main loop: send outgoing packets to network card until there are no 41052286Smdodd * more packets left, or the card cannot accept any more yet. 41152286Smdodd */ 41252286Smdodd while (((opkt = ifp->if_snd.ifq_head) != NULL) && 41352286Smdodd !(ifp->if_flags & IFF_OACTIVE)) { 41421769Sjkh 41552286Smdodd /* 41652286Smdodd * Ensure there is enough free transmit buffer space for 41752286Smdodd * this packet, including its header. Note: the header 41852286Smdodd * cannot wrap around the end of the transmit buffer and 41952286Smdodd * must be kept together, so we allow space for twice the 42052286Smdodd * length of the header, just in case. 42152286Smdodd */ 42221769Sjkh 42352286Smdodd for (len = 0, m = opkt; m != NULL; m = m->m_next) { 42452286Smdodd len += m->m_len; 42552286Smdodd } 42652286Smdodd 42752286Smdodd data_len = len; 42852286Smdodd 42952286Smdodd DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len);); 43052286Smdodd 43152286Smdodd if (len & 1) { 43252286Smdodd len += XMT_HEADER_LEN + 1; 43352286Smdodd } else { 43452286Smdodd len += XMT_HEADER_LEN; 43552286Smdodd } 43652286Smdodd 43752286Smdodd if ((i = sc->tx_tail - sc->tx_head) >= 0) { 43852286Smdodd avail = sc->tx_mem_size - i; 43952286Smdodd } else { 44052286Smdodd avail = -i; 44152286Smdodd } 44252286Smdodd 44352286Smdodd DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail);); 44452286Smdodd 44552286Smdodd if (avail >= len + XMT_HEADER_LEN) { 44652286Smdodd IF_DEQUEUE(&ifp->if_snd, opkt); 44752286Smdodd 44821769Sjkh#ifdef EX_PSA_INTR 44952286Smdodd /* 45052286Smdodd * Disable rx and tx interrupts, to avoid corruption 45152286Smdodd * of the host address register by interrupt service 45252286Smdodd * routines. 45352286Smdodd * XXX Is this necessary with splimp() enabled? 45452286Smdodd */ 45552286Smdodd outb(iobase + MASK_REG, All_Int); 45621769Sjkh#endif 45721769Sjkh 45852286Smdodd /* 45952286Smdodd * Compute the start and end addresses of this 46052286Smdodd * frame in the tx buffer. 46152286Smdodd */ 46252286Smdodd dest = sc->tx_tail; 46352286Smdodd next = dest + len; 46421769Sjkh 46552286Smdodd if (next > sc->tx_upper_limit) { 46652286Smdodd if ((sc->tx_upper_limit + 2 - sc->tx_tail) <= 46752286Smdodd XMT_HEADER_LEN) { 46852286Smdodd dest = sc->tx_lower_limit; 46952286Smdodd next = dest + len; 47055881Smdodd } else { 47155881Smdodd next = sc->tx_lower_limit + 47255881Smdodd next - sc->tx_upper_limit - 2; 47352286Smdodd } 47452286Smdodd } 47521769Sjkh 47652286Smdodd /* 47752286Smdodd * Build the packet frame in the card's ring buffer. 47852286Smdodd */ 47952286Smdodd DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next);); 48021769Sjkh 48152286Smdodd outw(iobase + HOST_ADDR_REG, dest); 48252286Smdodd outw(iobase + IO_PORT_REG, Transmit_CMD); 48352286Smdodd outw(iobase + IO_PORT_REG, 0); 48452286Smdodd outw(iobase + IO_PORT_REG, next); 48552286Smdodd outw(iobase + IO_PORT_REG, data_len); 48621769Sjkh 48752286Smdodd /* 48852286Smdodd * Output the packet data to the card. Ensure all 48952286Smdodd * transfers are 16-bit wide, even if individual 49052286Smdodd * mbufs have odd length. 49152286Smdodd */ 49221769Sjkh 49352286Smdodd for (m = opkt, i = 0; m != NULL; m = m->m_next) { 49452286Smdodd DODEBUG(Sent_Pkts, printf("[%d]", m->m_len);); 49552286Smdodd if (i) { 49652286Smdodd tmp16[1] = *(mtod(m, caddr_t)); 49752286Smdodd outsw(iobase + IO_PORT_REG, tmp16, 1); 49852286Smdodd } 49952286Smdodd outsw(iobase + IO_PORT_REG, 50052286Smdodd mtod(m, caddr_t) + i, (m->m_len - i) / 2); 50152286Smdodd 50252286Smdodd if ((i = (m->m_len - i) & 1) != 0) { 50352286Smdodd tmp16[0] = *(mtod(m, caddr_t) + 50452286Smdodd m->m_len - 1); 50552286Smdodd } 50652286Smdodd } 50752286Smdodd if (i) { 50852286Smdodd outsw(iobase + IO_PORT_REG, tmp16, 1); 50955881Smdodd } 51052286Smdodd 51155881Smdodd /* 51255881Smdodd * If there were other frames chained, update the 51355881Smdodd * chain in the last one. 51455881Smdodd */ 51555881Smdodd if (sc->tx_head != sc->tx_tail) { 51655881Smdodd if (sc->tx_tail != dest) { 51752286Smdodd outw(iobase + HOST_ADDR_REG, 51855881Smdodd sc->tx_last + XMT_Chain_Point); 51955881Smdodd outw(iobase + IO_PORT_REG, dest); 52052286Smdodd } 52155881Smdodd outw(iobase + HOST_ADDR_REG, 52255881Smdodd sc->tx_last + XMT_Byte_Count); 52355881Smdodd i = inw(iobase + IO_PORT_REG); 52455881Smdodd outw(iobase + HOST_ADDR_REG, 52555881Smdodd sc->tx_last + XMT_Byte_Count); 52655881Smdodd outw(iobase + IO_PORT_REG, i | Ch_bit); 52755881Smdodd } 52855881Smdodd 52955881Smdodd /* 53055881Smdodd * Resume normal operation of the card: 53155881Smdodd * - Make a dummy read to flush the DRAM write 53255881Smdodd * pipeline. 53355881Smdodd * - Enable receive and transmit interrupts. 53455881Smdodd * - Send Transmit or Resume_XMT command, as 53555881Smdodd * appropriate. 53655881Smdodd */ 53755881Smdodd inw(iobase + IO_PORT_REG); 53821769Sjkh#ifdef EX_PSA_INTR 53955881Smdodd outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 54021769Sjkh#endif 54155881Smdodd if (sc->tx_head == sc->tx_tail) { 54255881Smdodd outw(iobase + XMT_BAR, dest); 54355881Smdodd outb(iobase + CMD_REG, Transmit_CMD); 54455881Smdodd sc->tx_head = dest; 54555881Smdodd DODEBUG(Sent_Pkts, printf("Transmit\n");); 54652286Smdodd } else { 54755881Smdodd outb(iobase + CMD_REG, Resume_XMT_List_CMD); 54855881Smdodd DODEBUG(Sent_Pkts, printf("Resume\n");); 54952286Smdodd } 55055881Smdodd 55155881Smdodd sc->tx_last = dest; 55255881Smdodd sc->tx_tail = next; 55355881Smdodd 554106937Ssam BPF_MTAP(ifp, opkt); 55555881Smdodd 55655881Smdodd ifp->if_timer = 2; 55755881Smdodd ifp->if_opackets++; 55855881Smdodd m_freem(opkt); 55955881Smdodd } else { 56055881Smdodd ifp->if_flags |= IFF_OACTIVE; 56155881Smdodd DODEBUG(Status, printf("OACTIVE start\n");); 56252286Smdodd } 56321769Sjkh } 56421769Sjkh 56552286Smdodd splx(s); 56621769Sjkh 56752286Smdodd DODEBUG(Start_End, printf("ex_start%d: finish\n", unit);); 56821769Sjkh} 56921769Sjkh 57066440Simpvoid 57152286Smdoddex_stop(struct ex_softc *sc) 57221769Sjkh{ 57352286Smdodd int iobase = sc->iobase; 57421769Sjkh 57552286Smdodd DODEBUG(Start_End, printf("ex_stop%d: start\n", unit);); 57621769Sjkh 57752286Smdodd /* 57852286Smdodd * Disable card operation: 57952286Smdodd * - Disable the interrupt line. 58052286Smdodd * - Flush transmission and disable reception. 58152286Smdodd * - Mask and clear all interrupts. 58252286Smdodd * - Reset the 82595. 58352286Smdodd */ 58452286Smdodd outb(iobase + CMD_REG, Bank1_Sel); 58552286Smdodd outb(iobase + REG1, inb(iobase + REG1) & ~TriST_INT); 58652286Smdodd outb(iobase + CMD_REG, Bank0_Sel); 58752286Smdodd outb(iobase + CMD_REG, Rcv_Stop); 58852286Smdodd sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 58952286Smdodd 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. */ 59052286Smdodd outb(iobase + MASK_REG, All_Int); 59152286Smdodd outb(iobase + STATUS_REG, All_Int); 59252286Smdodd outb(iobase + CMD_REG, Reset_CMD); 59352286Smdodd DELAY(200); 59421769Sjkh 59552286Smdodd DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit);); 59652286Smdodd 59752286Smdodd return; 59821769Sjkh} 59921769Sjkh 60059816Smdoddvoid 60155883Smdoddex_intr(void *arg) 60221769Sjkh{ 60352286Smdodd struct ex_softc * sc = (struct ex_softc *)arg; 60452286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 60552286Smdodd int iobase = sc->iobase; 60652286Smdodd int int_status, send_pkts; 60721769Sjkh 60855883Smdodd DODEBUG(Start_End, printf("ex_intr%d: start\n", unit);); 60921769Sjkh 61021769Sjkh#ifdef EXDEBUG 61121769Sjkh if (++exintr_count != 1) 61221769Sjkh printf("WARNING: nested interrupt (%d). Mail the author.\n", exintr_count); 61321769Sjkh#endif 61421769Sjkh 61552286Smdodd send_pkts = 0; 61652286Smdodd while ((int_status = inb(iobase + STATUS_REG)) & (Tx_Int | Rx_Int)) { 61752286Smdodd if (int_status & Rx_Int) { 61852286Smdodd outb(iobase + STATUS_REG, Rx_Int); 61921769Sjkh 62052286Smdodd ex_rx_intr(sc); 62152286Smdodd } else if (int_status & Tx_Int) { 62252286Smdodd outb(iobase + STATUS_REG, Tx_Int); 62321769Sjkh 62452286Smdodd ex_tx_intr(sc); 62552286Smdodd send_pkts = 1; 62652286Smdodd } 62752286Smdodd } 62821769Sjkh 62952286Smdodd /* 63052286Smdodd * If any packet has been transmitted, and there are queued packets to 63152286Smdodd * be sent, attempt to send more packets to the network card. 63252286Smdodd */ 63352286Smdodd 63452286Smdodd if (send_pkts && (ifp->if_snd.ifq_head != NULL)) { 63552286Smdodd ex_start(ifp); 63652286Smdodd } 63752286Smdodd 63821769Sjkh#ifdef EXDEBUG 63921769Sjkh exintr_count--; 64021769Sjkh#endif 64121769Sjkh 64255883Smdodd DODEBUG(Start_End, printf("ex_intr%d: finish\n", unit);); 64352286Smdodd 64452286Smdodd return; 64521769Sjkh} 64621769Sjkh 64752286Smdoddstatic void 64852286Smdoddex_tx_intr(struct ex_softc *sc) 64921769Sjkh{ 65052286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 65152286Smdodd int iobase = sc->iobase; 65252286Smdodd int tx_status; 65321769Sjkh 65452286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit);); 65521769Sjkh 65652286Smdodd /* 65752286Smdodd * - Cancel the watchdog. 65852286Smdodd * For all packets transmitted since last transmit interrupt: 65952286Smdodd * - Advance chain pointer to next queued packet. 66052286Smdodd * - Update statistics. 66152286Smdodd */ 66221769Sjkh 66352286Smdodd ifp->if_timer = 0; 66421769Sjkh 66552286Smdodd while (sc->tx_head != sc->tx_tail) { 66652286Smdodd outw(iobase + HOST_ADDR_REG, sc->tx_head); 66721769Sjkh 66852286Smdodd if (! inw(iobase + IO_PORT_REG) & Done_bit) 66952286Smdodd break; 67021769Sjkh 67152286Smdodd tx_status = inw(iobase + IO_PORT_REG); 67252286Smdodd sc->tx_head = inw(iobase + IO_PORT_REG); 67352286Smdodd 67452286Smdodd if (tx_status & TX_OK_bit) { 67552286Smdodd ifp->if_opackets++; 67652286Smdodd } else { 67752286Smdodd ifp->if_oerrors++; 67852286Smdodd } 67952286Smdodd 68052286Smdodd ifp->if_collisions += tx_status & No_Collisions_bits; 68152286Smdodd } 68252286Smdodd 68352286Smdodd /* 68452286Smdodd * The card should be ready to accept more packets now. 68552286Smdodd */ 68652286Smdodd 68752286Smdodd ifp->if_flags &= ~IFF_OACTIVE; 68852286Smdodd 68952286Smdodd DODEBUG(Status, printf("OIDLE tx_intr\n");); 69052286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit);); 69152286Smdodd 69252286Smdodd return; 69321769Sjkh} 69421769Sjkh 69552286Smdoddstatic void 69652286Smdoddex_rx_intr(struct ex_softc *sc) 69721769Sjkh{ 69852286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 69952286Smdodd int iobase = sc->iobase; 70052286Smdodd int rx_status; 70152286Smdodd int pkt_len; 70252286Smdodd int QQQ; 70352286Smdodd struct mbuf * m; 70452286Smdodd struct mbuf * ipkt; 70552286Smdodd struct ether_header * eh; 70621769Sjkh 70752286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit);); 70821769Sjkh 70952286Smdodd /* 71052286Smdodd * For all packets received since last receive interrupt: 71152286Smdodd * - If packet ok, read it into a new mbuf and queue it to interface, 71252286Smdodd * updating statistics. 71352286Smdodd * - If packet bad, just discard it, and update statistics. 71452286Smdodd * Finally, advance receive stop limit in card's memory to new location. 71552286Smdodd */ 71621769Sjkh 71752286Smdodd outw(iobase + HOST_ADDR_REG, sc->rx_head); 71821769Sjkh 71952286Smdodd while (inw(iobase + IO_PORT_REG) == RCV_Done) { 72052286Smdodd 72152286Smdodd rx_status = inw(iobase + IO_PORT_REG); 72252286Smdodd sc->rx_head = inw(iobase + IO_PORT_REG); 72352286Smdodd QQQ = pkt_len = inw(iobase + IO_PORT_REG); 72452286Smdodd 72552286Smdodd if (rx_status & RCV_OK_bit) { 726111119Simp MGETHDR(m, M_DONTWAIT, MT_DATA); 72752286Smdodd ipkt = m; 72852286Smdodd if (ipkt == NULL) { 72952286Smdodd ifp->if_iqdrops++; 73052286Smdodd } else { 73152286Smdodd ipkt->m_pkthdr.rcvif = ifp; 73252286Smdodd ipkt->m_pkthdr.len = pkt_len; 73352286Smdodd ipkt->m_len = MHLEN; 73452286Smdodd 73552286Smdodd while (pkt_len > 0) { 73652286Smdodd if (pkt_len > MINCLSIZE) { 737111119Simp MCLGET(m, M_DONTWAIT); 73852286Smdodd if (m->m_flags & M_EXT) { 73952286Smdodd m->m_len = MCLBYTES; 74052286Smdodd } else { 74152286Smdodd m_freem(ipkt); 74252286Smdodd ifp->if_iqdrops++; 74352286Smdodd goto rx_another; 74452286Smdodd } 74552286Smdodd } 74652286Smdodd m->m_len = min(m->m_len, pkt_len); 74752286Smdodd 74821769Sjkh /* 74921769Sjkh * NOTE: I'm assuming that all mbufs allocated are of even length, 75021769Sjkh * except for the last one in an odd-length packet. 75121769Sjkh */ 75252286Smdodd 75352286Smdodd insw(iobase + IO_PORT_REG, 75452286Smdodd mtod(m, caddr_t), m->m_len / 2); 75552286Smdodd 75652286Smdodd if (m->m_len & 1) { 75752286Smdodd *(mtod(m, caddr_t) + m->m_len - 1) = inb(iobase + IO_PORT_REG); 75852286Smdodd } 75952286Smdodd pkt_len -= m->m_len; 76052286Smdodd 76152286Smdodd if (pkt_len > 0) { 762111119Simp MGET(m->m_next, M_DONTWAIT, MT_DATA); 76352286Smdodd if (m->m_next == NULL) { 76452286Smdodd m_freem(ipkt); 76552286Smdodd ifp->if_iqdrops++; 76652286Smdodd goto rx_another; 76752286Smdodd } 76852286Smdodd m = m->m_next; 76952286Smdodd m->m_len = MLEN; 77052286Smdodd } 77152286Smdodd } 77252286Smdodd eh = mtod(ipkt, struct ether_header *); 77352286Smdodd#ifdef EXDEBUG 77452286Smdodd if (debug_mask & Rcvd_Pkts) { 77552286Smdodd if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) { 77652286Smdodd printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":"); 77752286Smdodd printf("%6D\n", eh->ether_dhost, ":"); 77852286Smdodd } /* QQQ */ 77921769Sjkh } 78021769Sjkh#endif 781106937Ssam (*ifp->if_input)(ifp, ipkt); 78252286Smdodd ifp->if_ipackets++; 78352286Smdodd } 78452286Smdodd } else { 78552286Smdodd ifp->if_ierrors++; 78621769Sjkh } 78752286Smdodd outw(iobase + HOST_ADDR_REG, sc->rx_head); 78852286Smdoddrx_another: ; 78921769Sjkh } 79021769Sjkh 79152286Smdodd if (sc->rx_head < sc->rx_lower_limit + 2) 79252286Smdodd outw(iobase + RCV_STOP_REG, sc->rx_upper_limit); 79352286Smdodd else 79452286Smdodd outw(iobase + RCV_STOP_REG, sc->rx_head - 2); 79552286Smdodd 79652286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit);); 79752286Smdodd 79852286Smdodd return; 79921769Sjkh} 80021769Sjkh 80121769Sjkh 80255883Smdoddstatic int 80355883Smdoddex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 80421769Sjkh{ 80552286Smdodd struct ex_softc * sc = ifp->if_softc; 80657987Smdodd struct ifreq * ifr = (struct ifreq *)data; 80752286Smdodd int s; 80852286Smdodd int error = 0; 80921769Sjkh 81052286Smdodd DODEBUG(Start_End, printf("ex_ioctl%d: start ", ifp->if_unit);); 81121769Sjkh 81252286Smdodd s = splimp(); 81321769Sjkh 81452286Smdodd switch(cmd) { 81552286Smdodd case SIOCSIFADDR: 81652286Smdodd case SIOCGIFADDR: 81752286Smdodd case SIOCSIFMTU: 81852286Smdodd error = ether_ioctl(ifp, cmd, data); 81952286Smdodd break; 82021769Sjkh 82152286Smdodd case SIOCSIFFLAGS: 82252286Smdodd DODEBUG(Start_End, printf("SIOCSIFFLAGS");); 82352286Smdodd if ((ifp->if_flags & IFF_UP) == 0 && 82452286Smdodd (ifp->if_flags & IFF_RUNNING)) { 82552286Smdodd 82652286Smdodd ifp->if_flags &= ~IFF_RUNNING; 82752286Smdodd ex_stop(sc); 82852286Smdodd } else { 82952286Smdodd ex_init(sc); 83052286Smdodd } 83152286Smdodd break; 83221769Sjkh#ifdef NODEF 83352286Smdodd case SIOCGHWADDR: 83452286Smdodd DODEBUG(Start_End, printf("SIOCGHWADDR");); 83552286Smdodd bcopy((caddr_t)sc->sc_addr, (caddr_t)&ifr->ifr_data, 83652286Smdodd sizeof(sc->sc_addr)); 83752286Smdodd break; 83821769Sjkh#endif 83952286Smdodd case SIOCADDMULTI: 84052286Smdodd case SIOCDELMULTI: 841112731Smdodd ex_init(sc); 842112731Smdodd error = 0; 84352286Smdodd break; 84457987Smdodd case SIOCSIFMEDIA: 84557987Smdodd case SIOCGIFMEDIA: 84657987Smdodd error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd); 84757987Smdodd break; 84852286Smdodd default: 84952286Smdodd DODEBUG(Start_End, printf("unknown");); 85052286Smdodd error = EINVAL; 85152286Smdodd } 85221769Sjkh 85352286Smdodd splx(s); 85421769Sjkh 85552286Smdodd DODEBUG(Start_End, printf("\nex_ioctl%d: finish\n", ifp->if_unit);); 85652286Smdodd 85752286Smdodd return(error); 85821769Sjkh} 85921769Sjkh 860112731Smdoddstatic void 861112731Smdoddex_setmulti(struct ex_softc *sc) 862112731Smdodd{ 863112731Smdodd struct ifnet *ifp; 864112731Smdodd struct ifmultiaddr *maddr; 865112731Smdodd u_int16_t *addr; 866112731Smdodd int iobase = sc->iobase; 867112731Smdodd int count; 868112731Smdodd int timeout, status; 869112731Smdodd 870112731Smdodd ifp = &sc->arpcom.ac_if; 87121769Sjkh 872112731Smdodd count = 0; 873112731Smdodd TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { 874112731Smdodd if (maddr->ifma_addr->sa_family != AF_LINK) 875112731Smdodd continue; 876112731Smdodd count++; 877112731Smdodd } 878112731Smdodd 879112731Smdodd if ((ifp->if_flags & IFF_PROMISC) || (ifp->if_flags & IFF_ALLMULTI) 880112731Smdodd || count > 63) { 881112731Smdodd /* Interface is in promiscuous mode or there are too many 882112731Smdodd * multicast addresses for the card to handle */ 883112731Smdodd outb(iobase + CMD_REG, Bank2_Sel); 884112731Smdodd outb(iobase + REG2, inb(iobase + REG2) | Promisc_Mode); 885112731Smdodd outb(iobase + REG3, inb(iobase + REG3)); 886112731Smdodd outb(iobase + CMD_REG, Bank0_Sel); 887112731Smdodd } 888112731Smdodd else if ((ifp->if_flags & IFF_MULTICAST) && (count > 0)) { 889112731Smdodd /* Program multicast addresses plus our MAC address 890112731Smdodd * into the filter */ 891112731Smdodd outb(iobase + CMD_REG, Bank2_Sel); 892112731Smdodd outb(iobase + REG2, inb(iobase + REG2) | Multi_IA); 893112731Smdodd outb(iobase + REG3, inb(iobase + REG3)); 894112731Smdodd outb(iobase + CMD_REG, Bank0_Sel); 895112731Smdodd 896112731Smdodd /* Borrow space from TX buffer; this should be safe 897112731Smdodd * as this is only called from ex_init */ 898112731Smdodd 899112731Smdodd outw(iobase + HOST_ADDR_REG, sc->tx_lower_limit); 900112731Smdodd outw(iobase + IO_PORT_REG, MC_Setup_CMD); 901112731Smdodd outw(iobase + IO_PORT_REG, 0); 902112731Smdodd outw(iobase + IO_PORT_REG, 0); 903112731Smdodd outw(iobase + IO_PORT_REG, (count + 1) * 6); 904112731Smdodd 905112731Smdodd TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { 906112731Smdodd if (maddr->ifma_addr->sa_family != AF_LINK) 907112731Smdodd continue; 908112731Smdodd 909112731Smdodd addr = (u_int16_t*)LLADDR((struct sockaddr_dl *) 910112731Smdodd maddr->ifma_addr); 911112731Smdodd outw(iobase + IO_PORT_REG, *addr++); 912112731Smdodd outw(iobase + IO_PORT_REG, *addr++); 913112731Smdodd outw(iobase + IO_PORT_REG, *addr++); 914112731Smdodd } 915112731Smdodd 916112731Smdodd /* Program our MAC address as well */ 917112731Smdodd /* XXX: Is this necessary? The Linux driver does this 918112731Smdodd * but the NetBSD driver does not */ 919112731Smdodd addr = (u_int16_t*)(&sc->arpcom.ac_enaddr); 920112731Smdodd outw(iobase + IO_PORT_REG, *addr++); 921112731Smdodd outw(iobase + IO_PORT_REG, *addr++); 922112731Smdodd outw(iobase + IO_PORT_REG, *addr++); 923112731Smdodd 924112731Smdodd inw(iobase + IO_PORT_REG); 925112731Smdodd outw(iobase + XMT_BAR, sc->tx_lower_limit); 926112731Smdodd outb(iobase + CMD_REG, MC_Setup_CMD); 927112731Smdodd 928112731Smdodd sc->tx_head = sc->tx_lower_limit; 929112731Smdodd sc->tx_tail = sc->tx_head + XMT_HEADER_LEN + (count + 1) * 6; 930112731Smdodd 931112731Smdodd for (timeout=0; timeout<100; timeout++) { 932112731Smdodd DELAY(2); 933112731Smdodd if ((inb(iobase + STATUS_REG) & Exec_Int) == 0) 934112731Smdodd continue; 935112731Smdodd 936112731Smdodd status = inb(iobase + CMD_REG); 937112731Smdodd outb(iobase + STATUS_REG, Exec_Int); 938112731Smdodd break; 939112731Smdodd } 940112731Smdodd 941112731Smdodd sc->tx_head = sc->tx_tail; 942112731Smdodd } 943112731Smdodd else 944112731Smdodd { 945112731Smdodd /* No multicast or promiscuous mode */ 946112731Smdodd outb(iobase + CMD_REG, Bank2_Sel); 947112731Smdodd outb(iobase + REG2, inb(iobase + REG2) & 0xDE); 948112731Smdodd /* ~(Multi_IA | Promisc_Mode) */ 949112731Smdodd outb(iobase + REG3, inb(iobase + REG3)); 950112731Smdodd outb(iobase + CMD_REG, Bank0_Sel); 951112731Smdodd } 952112731Smdodd} 953112731Smdodd 95452286Smdoddstatic void 95552286Smdoddex_reset(struct ex_softc *sc) 95621769Sjkh{ 95752286Smdodd int s; 95821769Sjkh 95952286Smdodd DODEBUG(Start_End, printf("ex_reset%d: start\n", unit);); 96021769Sjkh 96152286Smdodd s = splimp(); 96221769Sjkh 96352286Smdodd ex_stop(sc); 96452286Smdodd ex_init(sc); 96521769Sjkh 96652286Smdodd splx(s); 96721769Sjkh 96852286Smdodd DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit);); 96952286Smdodd 97052286Smdodd return; 97121769Sjkh} 97221769Sjkh 97352286Smdoddstatic void 97452286Smdoddex_watchdog(struct ifnet *ifp) 97521769Sjkh{ 97652286Smdodd struct ex_softc * sc = ifp->if_softc; 97721769Sjkh 97852286Smdodd DODEBUG(Start_End, printf("ex_watchdog%d: start\n", ifp->if_unit);); 97921769Sjkh 98052286Smdodd ifp->if_flags &= ~IFF_OACTIVE; 98121769Sjkh 98252286Smdodd DODEBUG(Status, printf("OIDLE watchdog\n");); 98352286Smdodd 98452286Smdodd ifp->if_oerrors++; 98552286Smdodd ex_reset(sc); 98652286Smdodd ex_start(ifp); 98752286Smdodd 98852286Smdodd DODEBUG(Start_End, printf("ex_watchdog%d: finish\n", ifp->if_unit);); 98952286Smdodd 99052286Smdodd return; 99121769Sjkh} 99221769Sjkh 99357987Smdoddstatic int 99459816Smdoddex_get_media (u_int32_t iobase) 99559816Smdodd{ 996112764Smdodd int current; 997112764Smdodd int media; 99859816Smdodd 999112764Smdodd media = eeprom_read(iobase, EE_W5); 1000112764Smdodd 100159816Smdodd outb(iobase + CMD_REG, Bank2_Sel); 1002112764Smdodd current = inb(iobase + REG3); 100359816Smdodd outb(iobase + CMD_REG, Bank0_Sel); 100459816Smdodd 1005112764Smdodd if ((current & TPE_bit) && (media & EE_W5_PORT_TPE)) 100659816Smdodd return(IFM_ETHER|IFM_10_T); 1007112764Smdodd if ((current & BNC_bit) && (media & EE_W5_PORT_BNC)) 100859816Smdodd return(IFM_ETHER|IFM_10_2); 100959816Smdodd 1010112764Smdodd if (media & EE_W5_PORT_AUI) 1011112764Smdodd return (IFM_ETHER|IFM_10_5); 1012112764Smdodd 1013112764Smdodd return (IFM_ETHER|IFM_AUTO); 101459816Smdodd} 101559816Smdodd 101659816Smdoddstatic int 101757987Smdoddex_ifmedia_upd (ifp) 101857987Smdodd struct ifnet * ifp; 101957987Smdodd{ 1020112764Smdodd struct ex_softc * sc = ifp->if_softc; 102121769Sjkh 1022112764Smdodd if (IFM_TYPE(sc->ifmedia.ifm_media) != IFM_ETHER) 1023112764Smdodd return EINVAL; 1024112764Smdodd 102557987Smdodd return (0); 102657987Smdodd} 102757987Smdodd 102857987Smdoddstatic void 102957987Smdoddex_ifmedia_sts(ifp, ifmr) 103057987Smdodd struct ifnet * ifp; 103157987Smdodd struct ifmediareq * ifmr; 103257987Smdodd{ 103357987Smdodd struct ex_softc * sc = ifp->if_softc; 103457987Smdodd 103557987Smdodd ifmr->ifm_active = ex_get_media(sc->iobase); 1036112764Smdodd ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; 103757987Smdodd 103857987Smdodd return; 103957987Smdodd} 104057987Smdodd 104159816Smdoddu_short 104259816Smdoddeeprom_read(u_int32_t iobase, int location) 104321769Sjkh{ 104421769Sjkh int i; 104521769Sjkh u_short data = 0; 104621769Sjkh int ee_addr; 104721769Sjkh int read_cmd = location | EE_READ_CMD; 104821769Sjkh short ctrl_val = EECS; 104921769Sjkh 105021769Sjkh ee_addr = iobase + EEPROM_REG; 105121769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 105221769Sjkh outb(ee_addr, EECS); 105321769Sjkh for (i = 8; i >= 0; i--) { 105421769Sjkh short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; 105521769Sjkh outb(ee_addr, outval); 105621769Sjkh outb(ee_addr, outval | EESK); 105721769Sjkh DELAY(3); 105821769Sjkh outb(ee_addr, outval); 105921769Sjkh DELAY(2); 106021769Sjkh } 106121769Sjkh outb(ee_addr, ctrl_val); 106221769Sjkh 106321769Sjkh for (i = 16; i > 0; i--) { 106421769Sjkh outb(ee_addr, ctrl_val | EESK); 106521769Sjkh DELAY(3); 106621769Sjkh data = (data << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); 106721769Sjkh outb(ee_addr, ctrl_val); 106821769Sjkh DELAY(2); 106921769Sjkh } 107021769Sjkh 107121769Sjkh ctrl_val &= ~EECS; 107221769Sjkh outb(ee_addr, ctrl_val | EESK); 107321769Sjkh DELAY(3); 107421769Sjkh outb(ee_addr, ctrl_val); 107521769Sjkh DELAY(2); 107621769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 107721769Sjkh return(data); 107821769Sjkh} 1079