if_ex.c revision 150215
1139749Simp/*- 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 * 2752286Smdodd * 2852286Smdodd * MAINTAINER: Matthew N. Dodd <winter@jurai.net> 2952286Smdodd * <mdodd@FreeBSD.org> 3021769Sjkh */ 3121769Sjkh 32119418Sobrien#include <sys/cdefs.h> 33119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/ex/if_ex.c 150215 2005-09-16 11:25:19Z ru $"); 34119418Sobrien 3521769Sjkh/* 3629877Smsmith * Intel EtherExpress Pro/10, Pro/10+ Ethernet driver 3721769Sjkh * 3821769Sjkh * Revision history: 3921769Sjkh * 40112731Smdodd * dd-mmm-yyyy: Multicast support ported from NetBSD's if_iy driver. 4121769Sjkh * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast. 4221769Sjkh */ 4321769Sjkh 4421769Sjkh#include <sys/param.h> 4521769Sjkh#include <sys/systm.h> 4652286Smdodd#include <sys/kernel.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> 60112731Smdodd#include <net/if_dl.h> 6157987Smdodd#include <net/if_media.h> 62147256Sbrooks#include <net/if_types.h> 6350026Smdodd#include <net/ethernet.h> 6457987Smdodd#include <net/bpf.h> 6521769Sjkh 6650026Smdodd#include <netinet/in.h> 6750026Smdodd#include <netinet/if_ether.h> 6850026Smdodd 6921769Sjkh 7052286Smdodd#include <isa/isavar.h> 7152286Smdodd#include <isa/pnpvar.h> 7252286Smdodd 7355953Speter#include <dev/ex/if_exreg.h> 7459816Smdodd#include <dev/ex/if_exvar.h> 7521769Sjkh 7621769Sjkh#ifdef EXDEBUG 7752286Smdodd# define Start_End 1 7852286Smdodd# define Rcvd_Pkts 2 7952286Smdodd# define Sent_Pkts 4 8052286Smdodd# define Status 8 8121769Sjkhstatic int debug_mask = 0; 8252286Smdodd# define DODEBUG(level, action) if (level & debug_mask) action 8321769Sjkh#else 8452286Smdodd# define DODEBUG(level, action) 8521769Sjkh#endif 8621769Sjkh 87112801Smdodddevclass_t ex_devclass; 88112801Smdodd 8959816Smdoddchar irq2eemap[] = 9052286Smdodd { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 }; 9159816Smdoddu_char ee2irqmap[] = 9252286Smdodd { 9, 3, 5, 10, 11, 0, 0, 0 }; 9359816Smdodd 9459816Smdoddchar plus_irq2eemap[] = 9552286Smdodd { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 }; 9659816Smdoddu_char plus_ee2irqmap[] = 9752286Smdodd { 3, 4, 5, 7, 9, 10, 11, 12 }; 9821769Sjkh 9952286Smdodd/* Network Interface Functions */ 100131192Simpstatic void ex_init(void *); 101131192Simpstatic void ex_start(struct ifnet *); 102131192Simpstatic int ex_ioctl(struct ifnet *, u_long, caddr_t); 103131192Simpstatic void ex_watchdog(struct ifnet *); 10421769Sjkh 10557987Smdodd/* ifmedia Functions */ 106131192Simpstatic int ex_ifmedia_upd(struct ifnet *); 107131192Simpstatic void ex_ifmedia_sts(struct ifnet *, struct ifmediareq *); 10857987Smdodd 109131192Simpstatic int ex_get_media(struct ex_softc *); 11059816Smdodd 111131192Simpstatic void ex_reset(struct ex_softc *); 112131192Simpstatic void ex_setmulti(struct ex_softc *); 11352286Smdodd 114131192Simpstatic void ex_tx_intr(struct ex_softc *); 115131192Simpstatic void ex_rx_intr(struct ex_softc *); 11652286Smdodd 11759816Smdoddvoid 118131192Simpex_get_address(struct ex_softc *sc, u_char *enaddr) 11921769Sjkh{ 120131192Simp uint16_t eaddr_tmp; 12121769Sjkh 122131192Simp eaddr_tmp = ex_eeprom_read(sc, EE_Eth_Addr_Lo); 12352286Smdodd enaddr[5] = eaddr_tmp & 0xff; 12452286Smdodd enaddr[4] = eaddr_tmp >> 8; 125131192Simp eaddr_tmp = ex_eeprom_read(sc, EE_Eth_Addr_Mid); 12652286Smdodd enaddr[3] = eaddr_tmp & 0xff; 12752286Smdodd enaddr[2] = eaddr_tmp >> 8; 128131192Simp eaddr_tmp = ex_eeprom_read(sc, EE_Eth_Addr_Hi); 12952286Smdodd enaddr[1] = eaddr_tmp & 0xff; 13052286Smdodd enaddr[0] = eaddr_tmp >> 8; 13152286Smdodd 13252286Smdodd return; 13352286Smdodd} 13421769Sjkh 13559816Smdoddint 136131192Simpex_card_type(u_char *enaddr) 13752286Smdodd{ 13852286Smdodd if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9)) 13952286Smdodd return (CARD_TYPE_EX_10_PLUS); 14052286Smdodd 14152286Smdodd return (CARD_TYPE_EX_10); 14252286Smdodd} 14352286Smdodd 14455882Smdodd/* 14559816Smdodd * Caller is responsible for eventually calling 14659816Smdodd * ex_release_resources() on failure. 14755882Smdodd */ 14859816Smdoddint 149131192Simpex_alloc_resources(device_t dev) 15055882Smdodd{ 15159816Smdodd struct ex_softc * sc = device_get_softc(dev); 15259816Smdodd int error = 0; 15355882Smdodd 154127135Snjl sc->ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 155127135Snjl &sc->ioport_rid, RF_ACTIVE); 15659816Smdodd if (!sc->ioport) { 15759816Smdodd device_printf(dev, "No I/O space?!\n"); 15859816Smdodd error = ENOMEM; 15959816Smdodd goto bad; 16059816Smdodd } 161131192Simp sc->bst = rman_get_bustag(sc->ioport); 162131192Simp sc->bsh = rman_get_bushandle(sc->ioport); 16357989Smdodd 164127135Snjl sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, 165127135Snjl RF_ACTIVE); 16655882Smdodd 16759816Smdodd if (!sc->irq) { 16859816Smdodd device_printf(dev, "No IRQ?!\n"); 16959816Smdodd error = ENOMEM; 17059816Smdodd goto bad; 17155882Smdodd } 17255882Smdodd 17359816Smdoddbad: 17459816Smdodd return (error); 17555882Smdodd} 17655882Smdodd 17759816Smdoddvoid 178131192Simpex_release_resources(device_t dev) 17952286Smdodd{ 18059816Smdodd struct ex_softc * sc = device_get_softc(dev); 18152286Smdodd 18259816Smdodd if (sc->ih) { 18359816Smdodd bus_teardown_intr(dev, sc->irq, sc->ih); 18459816Smdodd sc->ih = NULL; 18552286Smdodd } 18652286Smdodd 18759816Smdodd if (sc->ioport) { 18859816Smdodd bus_release_resource(dev, SYS_RES_IOPORT, 18959816Smdodd sc->ioport_rid, sc->ioport); 19059816Smdodd sc->ioport = NULL; 19152286Smdodd } 19252286Smdodd 19359816Smdodd if (sc->irq) { 19459816Smdodd bus_release_resource(dev, SYS_RES_IRQ, 19559816Smdodd sc->irq_rid, sc->irq); 19659816Smdodd sc->irq = NULL; 19757989Smdodd } 19857989Smdodd 199150215Sru if (sc->ifp) 200150215Sru if_free(sc->ifp); 201150215Sru 20259816Smdodd return; 20352286Smdodd} 20452286Smdodd 20559816Smdoddint 20659816Smdoddex_attach(device_t dev) 20752286Smdodd{ 20852286Smdodd struct ex_softc * sc = device_get_softc(dev); 209147256Sbrooks struct ifnet * ifp; 21057987Smdodd struct ifmedia * ifm; 211131192Simp uint16_t temp; 21252286Smdodd 213147256Sbrooks ifp = sc->ifp = if_alloc(IFT_ETHER); 214147256Sbrooks if (ifp == NULL) { 215147256Sbrooks device_printf(dev, "can not if_alloc()\n"); 216147256Sbrooks return (ENOSPC); 217147256Sbrooks } 21829877Smsmith /* work out which set of irq <-> internal tables to use */ 219147256Sbrooks if (ex_card_type(sc->enaddr) == CARD_TYPE_EX_10_PLUS) { 22029877Smsmith sc->irq2ee = plus_irq2eemap; 22129877Smsmith sc->ee2irq = plus_ee2irqmap; 22252286Smdodd } else { 22329877Smsmith sc->irq2ee = irq2eemap; 22429877Smsmith sc->ee2irq = ee2irqmap; 22529877Smsmith } 22629877Smsmith 22721769Sjkh sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */ 22821769Sjkh 22921769Sjkh /* 23021769Sjkh * Initialize the ifnet structure. 23121769Sjkh */ 23221769Sjkh ifp->if_softc = sc; 233121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 23455883Smdodd ifp->if_mtu = ETHERMTU; 235133684Srwatson ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST | 236133684Srwatson IFF_NEEDSGIANT; 23721769Sjkh ifp->if_start = ex_start; 23821769Sjkh ifp->if_ioctl = ex_ioctl; 23921769Sjkh ifp->if_watchdog = ex_watchdog; 24055883Smdodd ifp->if_init = ex_init; 24155883Smdodd ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 24221769Sjkh 24357987Smdodd ifmedia_init(&sc->ifmedia, 0, ex_ifmedia_upd, ex_ifmedia_sts); 24457987Smdodd 245131192Simp temp = ex_eeprom_read(sc, EE_W5); 24657987Smdodd if (temp & EE_W5_PORT_TPE) 24757987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 24857987Smdodd if (temp & EE_W5_PORT_BNC) 24957987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); 25057987Smdodd if (temp & EE_W5_PORT_AUI) 25157987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 25257987Smdodd 253112764Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); 254112764Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_NONE, 0, NULL); 255131192Simp ifmedia_set(&sc->ifmedia, ex_get_media(sc)); 25657987Smdodd 25757987Smdodd ifm = &sc->ifmedia; 25857987Smdodd ifm->ifm_media = ifm->ifm_cur->ifm_media; 25957987Smdodd ex_ifmedia_upd(ifp); 26057987Smdodd 26121769Sjkh /* 26221769Sjkh * Attach the interface. 26321769Sjkh */ 264147256Sbrooks ether_ifattach(ifp, sc->enaddr); 26521769Sjkh 26652286Smdodd return(0); 26721769Sjkh} 26821769Sjkh 269112800Smdoddint 270131192Simpex_detach(device_t dev) 271112800Smdodd{ 272112800Smdodd struct ex_softc *sc; 273112800Smdodd struct ifnet *ifp; 274112800Smdodd 275112800Smdodd sc = device_get_softc(dev); 276147256Sbrooks ifp = sc->ifp; 277112800Smdodd 278112800Smdodd ex_stop(sc); 279112800Smdodd 280148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 281112800Smdodd ether_ifdetach(ifp); 282112800Smdodd 283112800Smdodd ex_release_resources(dev); 284112800Smdodd 285112800Smdodd return (0); 286112800Smdodd} 287112800Smdodd 28855883Smdoddstatic void 28955883Smdoddex_init(void *xsc) 29021769Sjkh{ 29152286Smdodd struct ex_softc * sc = (struct ex_softc *) xsc; 292147256Sbrooks struct ifnet * ifp = sc->ifp; 29352286Smdodd int s; 29452286Smdodd int i; 29552286Smdodd unsigned short temp_reg; 29621769Sjkh 297121816Sbrooks DODEBUG(Start_End, printf("%s: ex_init: start\n", ifp->if_xname);); 29821769Sjkh 29921769Sjkh s = splimp(); 30059816Smdodd ifp->if_timer = 0; 30121769Sjkh 30221769Sjkh /* 30321769Sjkh * Load the ethernet address into the card. 30421769Sjkh */ 305131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 306131192Simp temp_reg = CSR_READ_1(sc, EEPROM_REG); 30752286Smdodd if (temp_reg & Trnoff_Enable) { 308131192Simp CSR_WRITE_1(sc, EEPROM_REG, temp_reg & ~Trnoff_Enable); 30952286Smdodd } 31052286Smdodd for (i = 0; i < ETHER_ADDR_LEN; i++) { 311147256Sbrooks CSR_WRITE_1(sc, I_ADDR_REG0 + i, IFP2ENADDR(sc->ifp)[i]); 31252286Smdodd } 31321769Sjkh /* 31421769Sjkh * - Setup transmit chaining and discard bad received frames. 31521769Sjkh * - Match broadcast. 31621769Sjkh * - Clear test mode. 31721769Sjkh * - Set receiving mode. 31821769Sjkh * - Set IRQ number. 31921769Sjkh */ 320131192Simp CSR_WRITE_1(sc, REG1, CSR_READ_1(sc, REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr); 321131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) | No_SA_Ins | RX_CRC_InMem); 322131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3) & 0x3f /* XXX constants. */ ); 323131192Simp CSR_WRITE_1(sc, CMD_REG, Bank1_Sel); 324131192Simp CSR_WRITE_1(sc, INT_NO_REG, (CSR_READ_1(sc, INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]); 32521769Sjkh 32621769Sjkh /* 32721769Sjkh * Divide the available memory in the card into rcv and xmt buffers. 32821769Sjkh * By default, I use the first 3/4 of the memory for the rcv buffer, 32921769Sjkh * and the remaining 1/4 of the memory for the xmt buffer. 33021769Sjkh */ 33121769Sjkh sc->rx_mem_size = sc->mem_size * 3 / 4; 33221769Sjkh sc->tx_mem_size = sc->mem_size - sc->rx_mem_size; 33321769Sjkh sc->rx_lower_limit = 0x0000; 33421769Sjkh sc->rx_upper_limit = sc->rx_mem_size - 2; 33521769Sjkh sc->tx_lower_limit = sc->rx_mem_size; 33621769Sjkh sc->tx_upper_limit = sc->mem_size - 2; 337131192Simp CSR_WRITE_1(sc, RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8); 338131192Simp CSR_WRITE_1(sc, RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8); 339131192Simp CSR_WRITE_1(sc, XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8); 340131192Simp CSR_WRITE_1(sc, XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8); 34121769Sjkh 34221769Sjkh /* 34321769Sjkh * Enable receive and transmit interrupts, and clear any pending int. 34421769Sjkh */ 345131192Simp CSR_WRITE_1(sc, REG1, CSR_READ_1(sc, REG1) | TriST_INT); 346131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 347131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 348131192Simp CSR_WRITE_1(sc, STATUS_REG, All_Int); 34921769Sjkh 35021769Sjkh /* 35121769Sjkh * Initialize receive and transmit ring buffers. 35221769Sjkh */ 353131192Simp CSR_WRITE_2(sc, RCV_BAR, sc->rx_lower_limit); 35421769Sjkh sc->rx_head = sc->rx_lower_limit; 355131192Simp CSR_WRITE_2(sc, RCV_STOP_REG, sc->rx_upper_limit | 0xfe); 356131192Simp CSR_WRITE_2(sc, XMT_BAR, sc->tx_lower_limit); 35721769Sjkh sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 35821769Sjkh 359148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 360148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 36121769Sjkh DODEBUG(Status, printf("OIDLE init\n");); 36221769Sjkh 363112731Smdodd ex_setmulti(sc); 364112731Smdodd 36521769Sjkh /* 36621769Sjkh * Final reset of the board, and enable operation. 36721769Sjkh */ 368131192Simp CSR_WRITE_1(sc, CMD_REG, Sel_Reset_CMD); 36921769Sjkh DELAY(2); 370131192Simp CSR_WRITE_1(sc, CMD_REG, Rcv_Enable_CMD); 37121769Sjkh 37221769Sjkh ex_start(ifp); 37321769Sjkh splx(s); 37421769Sjkh 375121816Sbrooks DODEBUG(Start_End, printf("%s: ex_init: finish\n", ifp->if_xname);); 37621769Sjkh} 37721769Sjkh 37821769Sjkh 37955883Smdoddstatic void 38052286Smdoddex_start(struct ifnet *ifp) 38121769Sjkh{ 38252286Smdodd struct ex_softc * sc = ifp->if_softc; 38352286Smdodd int i, s, len, data_len, avail, dest, next; 38452286Smdodd unsigned char tmp16[2]; 38552286Smdodd struct mbuf * opkt; 38652286Smdodd struct mbuf * m; 38721769Sjkh 38852286Smdodd DODEBUG(Start_End, printf("ex_start%d: start\n", unit);); 38921769Sjkh 39052286Smdodd s = splimp(); 39121769Sjkh 39252286Smdodd /* 39352286Smdodd * Main loop: send outgoing packets to network card until there are no 39452286Smdodd * more packets left, or the card cannot accept any more yet. 39552286Smdodd */ 39652286Smdodd while (((opkt = ifp->if_snd.ifq_head) != NULL) && 397148887Srwatson !(ifp->if_drv_flags & IFF_DRV_OACTIVE)) { 39821769Sjkh 39952286Smdodd /* 40052286Smdodd * Ensure there is enough free transmit buffer space for 40152286Smdodd * this packet, including its header. Note: the header 40252286Smdodd * cannot wrap around the end of the transmit buffer and 40352286Smdodd * must be kept together, so we allow space for twice the 40452286Smdodd * length of the header, just in case. 40552286Smdodd */ 40621769Sjkh 40752286Smdodd for (len = 0, m = opkt; m != NULL; m = m->m_next) { 40852286Smdodd len += m->m_len; 40952286Smdodd } 41052286Smdodd 41152286Smdodd data_len = len; 41252286Smdodd 41352286Smdodd DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len);); 41452286Smdodd 41552286Smdodd if (len & 1) { 41652286Smdodd len += XMT_HEADER_LEN + 1; 41752286Smdodd } else { 41852286Smdodd len += XMT_HEADER_LEN; 41952286Smdodd } 42052286Smdodd 42152286Smdodd if ((i = sc->tx_tail - sc->tx_head) >= 0) { 42252286Smdodd avail = sc->tx_mem_size - i; 42352286Smdodd } else { 42452286Smdodd avail = -i; 42552286Smdodd } 42652286Smdodd 42752286Smdodd DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail);); 42852286Smdodd 42952286Smdodd if (avail >= len + XMT_HEADER_LEN) { 43052286Smdodd IF_DEQUEUE(&ifp->if_snd, opkt); 43152286Smdodd 43221769Sjkh#ifdef EX_PSA_INTR 43352286Smdodd /* 43452286Smdodd * Disable rx and tx interrupts, to avoid corruption 43552286Smdodd * of the host address register by interrupt service 43652286Smdodd * routines. 43752286Smdodd * XXX Is this necessary with splimp() enabled? 43852286Smdodd */ 439131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int); 44021769Sjkh#endif 44121769Sjkh 44252286Smdodd /* 44352286Smdodd * Compute the start and end addresses of this 44452286Smdodd * frame in the tx buffer. 44552286Smdodd */ 44652286Smdodd dest = sc->tx_tail; 44752286Smdodd next = dest + len; 44821769Sjkh 44952286Smdodd if (next > sc->tx_upper_limit) { 45052286Smdodd if ((sc->tx_upper_limit + 2 - sc->tx_tail) <= 45152286Smdodd XMT_HEADER_LEN) { 45252286Smdodd dest = sc->tx_lower_limit; 45352286Smdodd next = dest + len; 45455881Smdodd } else { 45555881Smdodd next = sc->tx_lower_limit + 45655881Smdodd next - sc->tx_upper_limit - 2; 45752286Smdodd } 45852286Smdodd } 45921769Sjkh 46052286Smdodd /* 46152286Smdodd * Build the packet frame in the card's ring buffer. 46252286Smdodd */ 46352286Smdodd DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next);); 46421769Sjkh 465131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, dest); 466131192Simp CSR_WRITE_2(sc, IO_PORT_REG, Transmit_CMD); 467131192Simp CSR_WRITE_2(sc, IO_PORT_REG, 0); 468131192Simp CSR_WRITE_2(sc, IO_PORT_REG, next); 469131192Simp CSR_WRITE_2(sc, IO_PORT_REG, data_len); 47021769Sjkh 47152286Smdodd /* 47252286Smdodd * Output the packet data to the card. Ensure all 47352286Smdodd * transfers are 16-bit wide, even if individual 47452286Smdodd * mbufs have odd length. 47552286Smdodd */ 47652286Smdodd for (m = opkt, i = 0; m != NULL; m = m->m_next) { 47752286Smdodd DODEBUG(Sent_Pkts, printf("[%d]", m->m_len);); 47852286Smdodd if (i) { 47952286Smdodd tmp16[1] = *(mtod(m, caddr_t)); 480131192Simp CSR_WRITE_MULTI_2(sc, IO_PORT_REG, 481131192Simp (uint16_t *) tmp16, 1); 48252286Smdodd } 483131192Simp CSR_WRITE_MULTI_2(sc, IO_PORT_REG, 484131192Simp (uint16_t *) (mtod(m, caddr_t) + i), 485131192Simp (m->m_len - i) / 2); 48652286Smdodd if ((i = (m->m_len - i) & 1) != 0) { 48752286Smdodd tmp16[0] = *(mtod(m, caddr_t) + 48852286Smdodd m->m_len - 1); 48952286Smdodd } 49052286Smdodd } 491131192Simp if (i) 492131192Simp CSR_WRITE_MULTI_2(sc, IO_PORT_REG, 493131192Simp (uint16_t *) tmp16, 1); 49455881Smdodd /* 49555881Smdodd * If there were other frames chained, update the 49655881Smdodd * chain in the last one. 49755881Smdodd */ 49855881Smdodd if (sc->tx_head != sc->tx_tail) { 49955881Smdodd if (sc->tx_tail != dest) { 500131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, 50155881Smdodd sc->tx_last + XMT_Chain_Point); 502131192Simp CSR_WRITE_2(sc, IO_PORT_REG, dest); 50352286Smdodd } 504131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, 50555881Smdodd sc->tx_last + XMT_Byte_Count); 506131192Simp i = CSR_READ_2(sc, IO_PORT_REG); 507131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, 50855881Smdodd sc->tx_last + XMT_Byte_Count); 509131192Simp CSR_WRITE_2(sc, IO_PORT_REG, i | Ch_bit); 51055881Smdodd } 51155881Smdodd 51255881Smdodd /* 51355881Smdodd * Resume normal operation of the card: 51455881Smdodd * - Make a dummy read to flush the DRAM write 51555881Smdodd * pipeline. 51655881Smdodd * - Enable receive and transmit interrupts. 51755881Smdodd * - Send Transmit or Resume_XMT command, as 51855881Smdodd * appropriate. 51955881Smdodd */ 520131192Simp CSR_READ_2(sc, IO_PORT_REG); 52121769Sjkh#ifdef EX_PSA_INTR 522131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 52321769Sjkh#endif 52455881Smdodd if (sc->tx_head == sc->tx_tail) { 525131192Simp CSR_WRITE_2(sc, XMT_BAR, dest); 526131192Simp CSR_WRITE_1(sc, CMD_REG, Transmit_CMD); 52755881Smdodd sc->tx_head = dest; 52855881Smdodd DODEBUG(Sent_Pkts, printf("Transmit\n");); 52952286Smdodd } else { 530131192Simp CSR_WRITE_1(sc, CMD_REG, Resume_XMT_List_CMD); 53155881Smdodd DODEBUG(Sent_Pkts, printf("Resume\n");); 53252286Smdodd } 53355881Smdodd 53455881Smdodd sc->tx_last = dest; 53555881Smdodd sc->tx_tail = next; 53655881Smdodd 537106937Ssam BPF_MTAP(ifp, opkt); 53855881Smdodd 53955881Smdodd ifp->if_timer = 2; 54055881Smdodd ifp->if_opackets++; 54155881Smdodd m_freem(opkt); 54255881Smdodd } else { 543148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 54455881Smdodd DODEBUG(Status, printf("OACTIVE start\n");); 54552286Smdodd } 54621769Sjkh } 54721769Sjkh 54852286Smdodd splx(s); 54921769Sjkh 55052286Smdodd DODEBUG(Start_End, printf("ex_start%d: finish\n", unit);); 55121769Sjkh} 55221769Sjkh 55366440Simpvoid 55452286Smdoddex_stop(struct ex_softc *sc) 55521769Sjkh{ 556131192Simp 55752286Smdodd DODEBUG(Start_End, printf("ex_stop%d: start\n", unit);); 55821769Sjkh 55952286Smdodd /* 56052286Smdodd * Disable card operation: 56152286Smdodd * - Disable the interrupt line. 56252286Smdodd * - Flush transmission and disable reception. 56352286Smdodd * - Mask and clear all interrupts. 56452286Smdodd * - Reset the 82595. 56552286Smdodd */ 566131192Simp CSR_WRITE_1(sc, CMD_REG, Bank1_Sel); 567131192Simp CSR_WRITE_1(sc, REG1, CSR_READ_1(sc, REG1) & ~TriST_INT); 568131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 569131192Simp CSR_WRITE_1(sc, CMD_REG, Rcv_Stop); 57052286Smdodd sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 57152286Smdodd 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. */ 572131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int); 573131192Simp CSR_WRITE_1(sc, STATUS_REG, All_Int); 574131192Simp CSR_WRITE_1(sc, CMD_REG, Reset_CMD); 57552286Smdodd DELAY(200); 57621769Sjkh 57752286Smdodd DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit);); 57852286Smdodd 57952286Smdodd return; 58021769Sjkh} 58121769Sjkh 58259816Smdoddvoid 58355883Smdoddex_intr(void *arg) 58421769Sjkh{ 585131192Simp struct ex_softc *sc = (struct ex_softc *)arg; 586147256Sbrooks struct ifnet *ifp = sc->ifp; 587131192Simp int int_status, send_pkts; 588131192Simp int loops = 100; 58921769Sjkh 59055883Smdodd DODEBUG(Start_End, printf("ex_intr%d: start\n", unit);); 59121769Sjkh 59252286Smdodd send_pkts = 0; 593131192Simp while (loops-- > 0 && 594131192Simp (int_status = CSR_READ_1(sc, STATUS_REG)) & (Tx_Int | Rx_Int)) { 595131192Simp /* don't loop forever */ 596131192Simp if (int_status == 0xff) 597131192Simp break; 59852286Smdodd if (int_status & Rx_Int) { 599131192Simp CSR_WRITE_1(sc, STATUS_REG, Rx_Int); 60052286Smdodd ex_rx_intr(sc); 60152286Smdodd } else if (int_status & Tx_Int) { 602131192Simp CSR_WRITE_1(sc, STATUS_REG, Tx_Int); 60352286Smdodd ex_tx_intr(sc); 60452286Smdodd send_pkts = 1; 60552286Smdodd } 60652286Smdodd } 607131192Simp if (loops == 0) 608131192Simp printf("100 loops are not enough\n"); 60921769Sjkh 61052286Smdodd /* 61152286Smdodd * If any packet has been transmitted, and there are queued packets to 61252286Smdodd * be sent, attempt to send more packets to the network card. 61352286Smdodd */ 614131192Simp if (send_pkts && (ifp->if_snd.ifq_head != NULL)) 61552286Smdodd ex_start(ifp); 61652286Smdodd 61755883Smdodd DODEBUG(Start_End, printf("ex_intr%d: finish\n", unit);); 61852286Smdodd 61952286Smdodd return; 62021769Sjkh} 62121769Sjkh 62252286Smdoddstatic void 62352286Smdoddex_tx_intr(struct ex_softc *sc) 62421769Sjkh{ 625147256Sbrooks struct ifnet * ifp = sc->ifp; 62652286Smdodd int tx_status; 62721769Sjkh 62852286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit);); 62921769Sjkh 63052286Smdodd /* 63152286Smdodd * - Cancel the watchdog. 63252286Smdodd * For all packets transmitted since last transmit interrupt: 63352286Smdodd * - Advance chain pointer to next queued packet. 63452286Smdodd * - Update statistics. 63552286Smdodd */ 63621769Sjkh 63752286Smdodd ifp->if_timer = 0; 63821769Sjkh 63952286Smdodd while (sc->tx_head != sc->tx_tail) { 640131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->tx_head); 64121769Sjkh 642131192Simp if (! CSR_READ_2(sc, IO_PORT_REG) & Done_bit) 64352286Smdodd break; 64421769Sjkh 645131192Simp tx_status = CSR_READ_2(sc, IO_PORT_REG); 646131192Simp sc->tx_head = CSR_READ_2(sc, IO_PORT_REG); 64752286Smdodd 64852286Smdodd if (tx_status & TX_OK_bit) { 64952286Smdodd ifp->if_opackets++; 65052286Smdodd } else { 65152286Smdodd ifp->if_oerrors++; 65252286Smdodd } 65352286Smdodd 65452286Smdodd ifp->if_collisions += tx_status & No_Collisions_bits; 65552286Smdodd } 65652286Smdodd 65752286Smdodd /* 65852286Smdodd * The card should be ready to accept more packets now. 65952286Smdodd */ 66052286Smdodd 661148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 66252286Smdodd 66352286Smdodd DODEBUG(Status, printf("OIDLE tx_intr\n");); 66452286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit);); 66552286Smdodd 66652286Smdodd return; 66721769Sjkh} 66821769Sjkh 66952286Smdoddstatic void 67052286Smdoddex_rx_intr(struct ex_softc *sc) 67121769Sjkh{ 672147256Sbrooks struct ifnet * ifp = sc->ifp; 67352286Smdodd int rx_status; 67452286Smdodd int pkt_len; 67552286Smdodd int QQQ; 67652286Smdodd struct mbuf * m; 67752286Smdodd struct mbuf * ipkt; 67852286Smdodd struct ether_header * eh; 67921769Sjkh 68052286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit);); 68121769Sjkh 68252286Smdodd /* 68352286Smdodd * For all packets received since last receive interrupt: 68452286Smdodd * - If packet ok, read it into a new mbuf and queue it to interface, 68552286Smdodd * updating statistics. 68652286Smdodd * - If packet bad, just discard it, and update statistics. 68752286Smdodd * Finally, advance receive stop limit in card's memory to new location. 68852286Smdodd */ 68921769Sjkh 690131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->rx_head); 69121769Sjkh 692131192Simp while (CSR_READ_2(sc, IO_PORT_REG) == RCV_Done) { 69352286Smdodd 694131192Simp rx_status = CSR_READ_2(sc, IO_PORT_REG); 695131192Simp sc->rx_head = CSR_READ_2(sc, IO_PORT_REG); 696131192Simp QQQ = pkt_len = CSR_READ_2(sc, IO_PORT_REG); 69752286Smdodd 69852286Smdodd if (rx_status & RCV_OK_bit) { 699111119Simp MGETHDR(m, M_DONTWAIT, MT_DATA); 70052286Smdodd ipkt = m; 70152286Smdodd if (ipkt == NULL) { 70252286Smdodd ifp->if_iqdrops++; 70352286Smdodd } else { 70452286Smdodd ipkt->m_pkthdr.rcvif = ifp; 70552286Smdodd ipkt->m_pkthdr.len = pkt_len; 70652286Smdodd ipkt->m_len = MHLEN; 70752286Smdodd 70852286Smdodd while (pkt_len > 0) { 709136625Sglebius if (pkt_len >= MINCLSIZE) { 710111119Simp MCLGET(m, M_DONTWAIT); 71152286Smdodd if (m->m_flags & M_EXT) { 71252286Smdodd m->m_len = MCLBYTES; 71352286Smdodd } else { 71452286Smdodd m_freem(ipkt); 71552286Smdodd ifp->if_iqdrops++; 71652286Smdodd goto rx_another; 71752286Smdodd } 71852286Smdodd } 71952286Smdodd m->m_len = min(m->m_len, pkt_len); 72052286Smdodd 72121769Sjkh /* 72221769Sjkh * NOTE: I'm assuming that all mbufs allocated are of even length, 72321769Sjkh * except for the last one in an odd-length packet. 72421769Sjkh */ 72552286Smdodd 726131192Simp CSR_READ_MULTI_2(sc, IO_PORT_REG, 727131192Simp mtod(m, uint16_t *), m->m_len / 2); 72852286Smdodd 72952286Smdodd if (m->m_len & 1) { 730131192Simp *(mtod(m, caddr_t) + m->m_len - 1) = CSR_READ_1(sc, IO_PORT_REG); 73152286Smdodd } 73252286Smdodd pkt_len -= m->m_len; 73352286Smdodd 73452286Smdodd if (pkt_len > 0) { 735111119Simp MGET(m->m_next, M_DONTWAIT, MT_DATA); 73652286Smdodd if (m->m_next == NULL) { 73752286Smdodd m_freem(ipkt); 73852286Smdodd ifp->if_iqdrops++; 73952286Smdodd goto rx_another; 74052286Smdodd } 74152286Smdodd m = m->m_next; 74252286Smdodd m->m_len = MLEN; 74352286Smdodd } 74452286Smdodd } 74552286Smdodd eh = mtod(ipkt, struct ether_header *); 74652286Smdodd#ifdef EXDEBUG 74752286Smdodd if (debug_mask & Rcvd_Pkts) { 74852286Smdodd if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) { 74952286Smdodd printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":"); 75052286Smdodd printf("%6D\n", eh->ether_dhost, ":"); 75152286Smdodd } /* QQQ */ 75221769Sjkh } 75321769Sjkh#endif 754106937Ssam (*ifp->if_input)(ifp, ipkt); 75552286Smdodd ifp->if_ipackets++; 75652286Smdodd } 75752286Smdodd } else { 75852286Smdodd ifp->if_ierrors++; 75921769Sjkh } 760131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->rx_head); 76152286Smdoddrx_another: ; 76221769Sjkh } 76321769Sjkh 76452286Smdodd if (sc->rx_head < sc->rx_lower_limit + 2) 765131192Simp CSR_WRITE_2(sc, RCV_STOP_REG, sc->rx_upper_limit); 76652286Smdodd else 767131192Simp CSR_WRITE_2(sc, RCV_STOP_REG, sc->rx_head - 2); 76852286Smdodd 76952286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit);); 77052286Smdodd 77152286Smdodd return; 77221769Sjkh} 77321769Sjkh 77421769Sjkh 77555883Smdoddstatic int 77655883Smdoddex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 77721769Sjkh{ 77852286Smdodd struct ex_softc * sc = ifp->if_softc; 77957987Smdodd struct ifreq * ifr = (struct ifreq *)data; 78052286Smdodd int s; 78152286Smdodd int error = 0; 78221769Sjkh 783121816Sbrooks DODEBUG(Start_End, printf("%s: ex_ioctl: start ", ifp->if_xname);); 78421769Sjkh 78552286Smdodd s = splimp(); 78621769Sjkh 78752286Smdodd switch(cmd) { 78852286Smdodd case SIOCSIFADDR: 78952286Smdodd case SIOCGIFADDR: 79052286Smdodd case SIOCSIFMTU: 79152286Smdodd error = ether_ioctl(ifp, cmd, data); 79252286Smdodd break; 79321769Sjkh 79452286Smdodd case SIOCSIFFLAGS: 79552286Smdodd DODEBUG(Start_End, printf("SIOCSIFFLAGS");); 79652286Smdodd if ((ifp->if_flags & IFF_UP) == 0 && 797148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 79852286Smdodd 799148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 80052286Smdodd ex_stop(sc); 80152286Smdodd } else { 80252286Smdodd ex_init(sc); 80352286Smdodd } 80452286Smdodd break; 80521769Sjkh#ifdef NODEF 80652286Smdodd case SIOCGHWADDR: 80752286Smdodd DODEBUG(Start_End, printf("SIOCGHWADDR");); 80852286Smdodd bcopy((caddr_t)sc->sc_addr, (caddr_t)&ifr->ifr_data, 80952286Smdodd sizeof(sc->sc_addr)); 81052286Smdodd break; 81121769Sjkh#endif 81252286Smdodd case SIOCADDMULTI: 81352286Smdodd case SIOCDELMULTI: 814112731Smdodd ex_init(sc); 815112731Smdodd error = 0; 81652286Smdodd break; 81757987Smdodd case SIOCSIFMEDIA: 81857987Smdodd case SIOCGIFMEDIA: 81957987Smdodd error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd); 82057987Smdodd break; 82152286Smdodd default: 82252286Smdodd DODEBUG(Start_End, printf("unknown");); 82352286Smdodd error = EINVAL; 82452286Smdodd } 82521769Sjkh 82652286Smdodd splx(s); 82721769Sjkh 828121816Sbrooks DODEBUG(Start_End, printf("\n%s: ex_ioctl: finish\n", ifp->if_xname);); 82952286Smdodd 83052286Smdodd return(error); 83121769Sjkh} 83221769Sjkh 833112731Smdoddstatic void 834112731Smdoddex_setmulti(struct ex_softc *sc) 835112731Smdodd{ 836112731Smdodd struct ifnet *ifp; 837112731Smdodd struct ifmultiaddr *maddr; 838131192Simp uint16_t *addr; 839112731Smdodd int count; 840112731Smdodd int timeout, status; 841112731Smdodd 842147256Sbrooks ifp = sc->ifp; 84321769Sjkh 844112731Smdodd count = 0; 845148654Srwatson IF_ADDR_LOCK(ifp); 846112731Smdodd TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { 847112731Smdodd if (maddr->ifma_addr->sa_family != AF_LINK) 848112731Smdodd continue; 849112731Smdodd count++; 850112731Smdodd } 851148654Srwatson IF_ADDR_UNLOCK(ifp); 852112731Smdodd 853112731Smdodd if ((ifp->if_flags & IFF_PROMISC) || (ifp->if_flags & IFF_ALLMULTI) 854112731Smdodd || count > 63) { 855112731Smdodd /* Interface is in promiscuous mode or there are too many 856112731Smdodd * multicast addresses for the card to handle */ 857131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 858131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) | Promisc_Mode); 859131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3)); 860131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 861112731Smdodd } 862112731Smdodd else if ((ifp->if_flags & IFF_MULTICAST) && (count > 0)) { 863112731Smdodd /* Program multicast addresses plus our MAC address 864112731Smdodd * into the filter */ 865131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 866131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) | Multi_IA); 867131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3)); 868131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 869112731Smdodd 870112731Smdodd /* Borrow space from TX buffer; this should be safe 871112731Smdodd * as this is only called from ex_init */ 872112731Smdodd 873131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->tx_lower_limit); 874131192Simp CSR_WRITE_2(sc, IO_PORT_REG, MC_Setup_CMD); 875131192Simp CSR_WRITE_2(sc, IO_PORT_REG, 0); 876131192Simp CSR_WRITE_2(sc, IO_PORT_REG, 0); 877131192Simp CSR_WRITE_2(sc, IO_PORT_REG, (count + 1) * 6); 878148654Srwatson 879148654Srwatson IF_ADDR_LOCK(ifp); 880112731Smdodd TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { 881112731Smdodd if (maddr->ifma_addr->sa_family != AF_LINK) 882112731Smdodd continue; 883112731Smdodd 884131192Simp addr = (uint16_t*)LLADDR((struct sockaddr_dl *) 885112731Smdodd maddr->ifma_addr); 886131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 887131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 888131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 889112731Smdodd } 890148654Srwatson IF_ADDR_UNLOCK(ifp); 891112731Smdodd 892112731Smdodd /* Program our MAC address as well */ 893112731Smdodd /* XXX: Is this necessary? The Linux driver does this 894112731Smdodd * but the NetBSD driver does not */ 895147256Sbrooks addr = (uint16_t*)(&IFP2ENADDR(sc->ifp)); 896131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 897131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 898131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 899112731Smdodd 900131192Simp CSR_READ_2(sc, IO_PORT_REG); 901131192Simp CSR_WRITE_2(sc, XMT_BAR, sc->tx_lower_limit); 902131192Simp CSR_WRITE_1(sc, CMD_REG, MC_Setup_CMD); 903112731Smdodd 904112731Smdodd sc->tx_head = sc->tx_lower_limit; 905112731Smdodd sc->tx_tail = sc->tx_head + XMT_HEADER_LEN + (count + 1) * 6; 906112731Smdodd 907112731Smdodd for (timeout=0; timeout<100; timeout++) { 908112731Smdodd DELAY(2); 909131192Simp if ((CSR_READ_1(sc, STATUS_REG) & Exec_Int) == 0) 910112731Smdodd continue; 911112731Smdodd 912131192Simp status = CSR_READ_1(sc, CMD_REG); 913131192Simp CSR_WRITE_1(sc, STATUS_REG, Exec_Int); 914112731Smdodd break; 915112731Smdodd } 916112731Smdodd 917112731Smdodd sc->tx_head = sc->tx_tail; 918112731Smdodd } 919112731Smdodd else 920112731Smdodd { 921112731Smdodd /* No multicast or promiscuous mode */ 922131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 923131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) & 0xDE); 924112731Smdodd /* ~(Multi_IA | Promisc_Mode) */ 925131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3)); 926131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 927112731Smdodd } 928112731Smdodd} 929112731Smdodd 93052286Smdoddstatic void 93152286Smdoddex_reset(struct ex_softc *sc) 93221769Sjkh{ 93352286Smdodd int s; 93421769Sjkh 93552286Smdodd DODEBUG(Start_End, printf("ex_reset%d: start\n", unit);); 93621769Sjkh 93752286Smdodd s = splimp(); 93821769Sjkh 93952286Smdodd ex_stop(sc); 94052286Smdodd ex_init(sc); 94121769Sjkh 94252286Smdodd splx(s); 94321769Sjkh 94452286Smdodd DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit);); 94552286Smdodd 94652286Smdodd return; 94721769Sjkh} 94821769Sjkh 94952286Smdoddstatic void 95052286Smdoddex_watchdog(struct ifnet *ifp) 95121769Sjkh{ 95252286Smdodd struct ex_softc * sc = ifp->if_softc; 95321769Sjkh 954121816Sbrooks DODEBUG(Start_End, printf("%s: ex_watchdog: start\n", ifp->if_xname);); 95521769Sjkh 956148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 95721769Sjkh 95852286Smdodd DODEBUG(Status, printf("OIDLE watchdog\n");); 95952286Smdodd 96052286Smdodd ifp->if_oerrors++; 96152286Smdodd ex_reset(sc); 96252286Smdodd ex_start(ifp); 96352286Smdodd 964121816Sbrooks DODEBUG(Start_End, printf("%s: ex_watchdog: finish\n", ifp->if_xname);); 96552286Smdodd 96652286Smdodd return; 96721769Sjkh} 96821769Sjkh 96957987Smdoddstatic int 970131192Simpex_get_media(struct ex_softc *sc) 97159816Smdodd{ 972112764Smdodd int current; 973112764Smdodd int media; 97459816Smdodd 975131192Simp media = ex_eeprom_read(sc, EE_W5); 976112764Smdodd 977131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 978131192Simp current = CSR_READ_1(sc, REG3); 979131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 98059816Smdodd 981112764Smdodd if ((current & TPE_bit) && (media & EE_W5_PORT_TPE)) 98259816Smdodd return(IFM_ETHER|IFM_10_T); 983112764Smdodd if ((current & BNC_bit) && (media & EE_W5_PORT_BNC)) 98459816Smdodd return(IFM_ETHER|IFM_10_2); 98559816Smdodd 986112764Smdodd if (media & EE_W5_PORT_AUI) 987112764Smdodd return (IFM_ETHER|IFM_10_5); 988112764Smdodd 989112764Smdodd return (IFM_ETHER|IFM_AUTO); 99059816Smdodd} 99159816Smdodd 99259816Smdoddstatic int 993131192Simpex_ifmedia_upd(ifp) 99457987Smdodd struct ifnet * ifp; 99557987Smdodd{ 996112764Smdodd struct ex_softc * sc = ifp->if_softc; 99721769Sjkh 998112764Smdodd if (IFM_TYPE(sc->ifmedia.ifm_media) != IFM_ETHER) 999112764Smdodd return EINVAL; 1000112764Smdodd 100157987Smdodd return (0); 100257987Smdodd} 100357987Smdodd 100457987Smdoddstatic void 100557987Smdoddex_ifmedia_sts(ifp, ifmr) 100657987Smdodd struct ifnet * ifp; 100757987Smdodd struct ifmediareq * ifmr; 100857987Smdodd{ 100957987Smdodd struct ex_softc * sc = ifp->if_softc; 101057987Smdodd 1011131192Simp ifmr->ifm_active = ex_get_media(sc); 1012112764Smdodd ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; 101357987Smdodd 101457987Smdodd return; 101557987Smdodd} 101657987Smdodd 101759816Smdoddu_short 1018131192Simpex_eeprom_read(struct ex_softc *sc, int location) 101921769Sjkh{ 102021769Sjkh int i; 102121769Sjkh u_short data = 0; 102221769Sjkh int read_cmd = location | EE_READ_CMD; 102321769Sjkh short ctrl_val = EECS; 102421769Sjkh 1025131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 1026131192Simp CSR_WRITE_1(sc, EEPROM_REG, EECS); 102721769Sjkh for (i = 8; i >= 0; i--) { 102821769Sjkh short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; 1029131192Simp CSR_WRITE_1(sc, EEPROM_REG, outval); 1030131192Simp CSR_WRITE_1(sc, EEPROM_REG, outval | EESK); 103121769Sjkh DELAY(3); 1032131192Simp CSR_WRITE_1(sc, EEPROM_REG, outval); 103321769Sjkh DELAY(2); 103421769Sjkh } 1035131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val); 103621769Sjkh 103721769Sjkh for (i = 16; i > 0; i--) { 1038131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val | EESK); 103921769Sjkh DELAY(3); 1040131192Simp data = (data << 1) | 1041131192Simp ((CSR_READ_1(sc, EEPROM_REG) & EEDO) ? 1 : 0); 1042131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val); 104321769Sjkh DELAY(2); 104421769Sjkh } 104521769Sjkh 104621769Sjkh ctrl_val &= ~EECS; 1047131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val | EESK); 104821769Sjkh DELAY(3); 1049131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val); 105021769Sjkh DELAY(2); 1051131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 105221769Sjkh return(data); 105321769Sjkh} 1054