1139749Simp/*- 2230132Suqs * 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: stable/11/sys/dev/ex/if_ex.c 347962 2019-05-18 20:43:13Z brooks $"); 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> 59257176Sglebius#include <net/if_var.h> 6057987Smdodd#include <net/if_arp.h> 61112731Smdodd#include <net/if_dl.h> 6257987Smdodd#include <net/if_media.h> 63147256Sbrooks#include <net/if_types.h> 6450026Smdodd#include <net/ethernet.h> 6557987Smdodd#include <net/bpf.h> 6621769Sjkh 6750026Smdodd#include <netinet/in.h> 6850026Smdodd#include <netinet/if_ether.h> 6950026Smdodd 7021769Sjkh 7152286Smdodd#include <isa/isavar.h> 7252286Smdodd#include <isa/pnpvar.h> 7352286Smdodd 7455953Speter#include <dev/ex/if_exreg.h> 7559816Smdodd#include <dev/ex/if_exvar.h> 7621769Sjkh 7721769Sjkh#ifdef EXDEBUG 7852286Smdodd# define Start_End 1 7952286Smdodd# define Rcvd_Pkts 2 8052286Smdodd# define Sent_Pkts 4 8152286Smdodd# define Status 8 8221769Sjkhstatic int debug_mask = 0; 8352286Smdodd# define DODEBUG(level, action) if (level & debug_mask) action 8421769Sjkh#else 8552286Smdodd# define DODEBUG(level, action) 8621769Sjkh#endif 8721769Sjkh 88112801Smdodddevclass_t ex_devclass; 89112801Smdodd 9059816Smdoddchar irq2eemap[] = 9152286Smdodd { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 }; 9259816Smdoddu_char ee2irqmap[] = 9352286Smdodd { 9, 3, 5, 10, 11, 0, 0, 0 }; 9459816Smdodd 9559816Smdoddchar plus_irq2eemap[] = 9652286Smdodd { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 }; 9759816Smdoddu_char plus_ee2irqmap[] = 9852286Smdodd { 3, 4, 5, 7, 9, 10, 11, 12 }; 9921769Sjkh 10052286Smdodd/* Network Interface Functions */ 101131192Simpstatic void ex_init(void *); 102179775Sjhbstatic void ex_init_locked(struct ex_softc *); 103131192Simpstatic void ex_start(struct ifnet *); 104179775Sjhbstatic void ex_start_locked(struct ifnet *); 105131192Simpstatic int ex_ioctl(struct ifnet *, u_long, caddr_t); 106179775Sjhbstatic void ex_watchdog(void *); 10721769Sjkh 10857987Smdodd/* ifmedia Functions */ 109131192Simpstatic int ex_ifmedia_upd(struct ifnet *); 110131192Simpstatic void ex_ifmedia_sts(struct ifnet *, struct ifmediareq *); 11157987Smdodd 112131192Simpstatic int ex_get_media(struct ex_softc *); 11359816Smdodd 114131192Simpstatic void ex_reset(struct ex_softc *); 115131192Simpstatic void ex_setmulti(struct ex_softc *); 11652286Smdodd 117131192Simpstatic void ex_tx_intr(struct ex_softc *); 118131192Simpstatic void ex_rx_intr(struct ex_softc *); 11952286Smdodd 12059816Smdoddvoid 121131192Simpex_get_address(struct ex_softc *sc, u_char *enaddr) 12221769Sjkh{ 123131192Simp uint16_t eaddr_tmp; 12421769Sjkh 125131192Simp eaddr_tmp = ex_eeprom_read(sc, EE_Eth_Addr_Lo); 12652286Smdodd enaddr[5] = eaddr_tmp & 0xff; 12752286Smdodd enaddr[4] = eaddr_tmp >> 8; 128131192Simp eaddr_tmp = ex_eeprom_read(sc, EE_Eth_Addr_Mid); 12952286Smdodd enaddr[3] = eaddr_tmp & 0xff; 13052286Smdodd enaddr[2] = eaddr_tmp >> 8; 131131192Simp eaddr_tmp = ex_eeprom_read(sc, EE_Eth_Addr_Hi); 13252286Smdodd enaddr[1] = eaddr_tmp & 0xff; 13352286Smdodd enaddr[0] = eaddr_tmp >> 8; 13452286Smdodd 13552286Smdodd return; 13652286Smdodd} 13721769Sjkh 13859816Smdoddint 139131192Simpex_card_type(u_char *enaddr) 14052286Smdodd{ 14152286Smdodd if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9)) 14252286Smdodd return (CARD_TYPE_EX_10_PLUS); 14352286Smdodd 14452286Smdodd return (CARD_TYPE_EX_10); 14552286Smdodd} 14652286Smdodd 14755882Smdodd/* 14859816Smdodd * Caller is responsible for eventually calling 14959816Smdodd * ex_release_resources() on failure. 15055882Smdodd */ 15159816Smdoddint 152131192Simpex_alloc_resources(device_t dev) 15355882Smdodd{ 15459816Smdodd struct ex_softc * sc = device_get_softc(dev); 15559816Smdodd int error = 0; 15655882Smdodd 157127135Snjl sc->ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 158127135Snjl &sc->ioport_rid, RF_ACTIVE); 15959816Smdodd if (!sc->ioport) { 16059816Smdodd device_printf(dev, "No I/O space?!\n"); 16159816Smdodd error = ENOMEM; 16259816Smdodd goto bad; 16359816Smdodd } 16457989Smdodd 165127135Snjl sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, 166127135Snjl RF_ACTIVE); 16755882Smdodd 16859816Smdodd if (!sc->irq) { 16959816Smdodd device_printf(dev, "No IRQ?!\n"); 17059816Smdodd error = ENOMEM; 17159816Smdodd goto bad; 17255882Smdodd } 17355882Smdodd 17459816Smdoddbad: 17559816Smdodd return (error); 17655882Smdodd} 17755882Smdodd 17859816Smdoddvoid 179131192Simpex_release_resources(device_t dev) 18052286Smdodd{ 18159816Smdodd struct ex_softc * sc = device_get_softc(dev); 18252286Smdodd 18359816Smdodd if (sc->ih) { 18459816Smdodd bus_teardown_intr(dev, sc->irq, sc->ih); 18559816Smdodd sc->ih = NULL; 18652286Smdodd } 18752286Smdodd 18859816Smdodd if (sc->ioport) { 18959816Smdodd bus_release_resource(dev, SYS_RES_IOPORT, 19059816Smdodd sc->ioport_rid, sc->ioport); 19159816Smdodd sc->ioport = NULL; 19252286Smdodd } 19352286Smdodd 19459816Smdodd if (sc->irq) { 19559816Smdodd bus_release_resource(dev, SYS_RES_IRQ, 19659816Smdodd sc->irq_rid, sc->irq); 19759816Smdodd sc->irq = NULL; 19857989Smdodd } 19957989Smdodd 200150215Sru if (sc->ifp) 201150215Sru if_free(sc->ifp); 202150215Sru 20359816Smdodd return; 20452286Smdodd} 20552286Smdodd 20659816Smdoddint 20759816Smdoddex_attach(device_t dev) 20852286Smdodd{ 20952286Smdodd struct ex_softc * sc = device_get_softc(dev); 210147256Sbrooks struct ifnet * ifp; 21157987Smdodd struct ifmedia * ifm; 212179775Sjhb int error; 213131192Simp uint16_t temp; 21452286Smdodd 215147256Sbrooks ifp = sc->ifp = if_alloc(IFT_ETHER); 216147256Sbrooks if (ifp == NULL) { 217147256Sbrooks device_printf(dev, "can not if_alloc()\n"); 218147256Sbrooks return (ENOSPC); 219147256Sbrooks } 22029877Smsmith /* work out which set of irq <-> internal tables to use */ 221147256Sbrooks if (ex_card_type(sc->enaddr) == CARD_TYPE_EX_10_PLUS) { 22229877Smsmith sc->irq2ee = plus_irq2eemap; 22329877Smsmith sc->ee2irq = plus_ee2irqmap; 22452286Smdodd } else { 22529877Smsmith sc->irq2ee = irq2eemap; 22629877Smsmith sc->ee2irq = ee2irqmap; 22729877Smsmith } 22829877Smsmith 22921769Sjkh sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */ 23021769Sjkh 23121769Sjkh /* 23221769Sjkh * Initialize the ifnet structure. 23321769Sjkh */ 23421769Sjkh ifp->if_softc = sc; 235121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 236179775Sjhb ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 23721769Sjkh ifp->if_start = ex_start; 23821769Sjkh ifp->if_ioctl = ex_ioctl; 23955883Smdodd ifp->if_init = ex_init; 240207554Ssobomax IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 24121769Sjkh 24257987Smdodd ifmedia_init(&sc->ifmedia, 0, ex_ifmedia_upd, ex_ifmedia_sts); 243179775Sjhb mtx_init(&sc->lock, device_get_nameunit(dev), MTX_NETWORK_LOCK, 244179775Sjhb MTX_DEF); 245179775Sjhb callout_init_mtx(&sc->timer, &sc->lock, 0); 24657987Smdodd 247131192Simp temp = ex_eeprom_read(sc, EE_W5); 24857987Smdodd if (temp & EE_W5_PORT_TPE) 24957987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 25057987Smdodd if (temp & EE_W5_PORT_BNC) 25157987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); 25257987Smdodd if (temp & EE_W5_PORT_AUI) 25357987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 25457987Smdodd 255112764Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); 256112764Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_NONE, 0, NULL); 257131192Simp ifmedia_set(&sc->ifmedia, ex_get_media(sc)); 25857987Smdodd 25957987Smdodd ifm = &sc->ifmedia; 260179775Sjhb ifm->ifm_media = ifm->ifm_cur->ifm_media; 26157987Smdodd ex_ifmedia_upd(ifp); 26257987Smdodd 26321769Sjkh /* 26421769Sjkh * Attach the interface. 26521769Sjkh */ 266147256Sbrooks ether_ifattach(ifp, sc->enaddr); 26721769Sjkh 268179775Sjhb error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, 269179775Sjhb NULL, ex_intr, (void *)sc, &sc->ih); 270179775Sjhb if (error) { 271179775Sjhb device_printf(dev, "bus_setup_intr() failed!\n"); 272179775Sjhb ether_ifdetach(ifp); 273179775Sjhb mtx_destroy(&sc->lock); 274179775Sjhb return (error); 275179775Sjhb } 276179775Sjhb 277347962Sbrooks gone_by_fcp101_dev(dev); 278347962Sbrooks 27952286Smdodd return(0); 28021769Sjkh} 28121769Sjkh 282112800Smdoddint 283131192Simpex_detach(device_t dev) 284112800Smdodd{ 285112800Smdodd struct ex_softc *sc; 286112800Smdodd struct ifnet *ifp; 287112800Smdodd 288112800Smdodd sc = device_get_softc(dev); 289147256Sbrooks ifp = sc->ifp; 290112800Smdodd 291179775Sjhb EX_LOCK(sc); 292112800Smdodd ex_stop(sc); 293179775Sjhb EX_UNLOCK(sc); 294112800Smdodd 295112800Smdodd ether_ifdetach(ifp); 296179775Sjhb callout_drain(&sc->timer); 297112800Smdodd 298112800Smdodd ex_release_resources(dev); 299179775Sjhb mtx_destroy(&sc->lock); 300112800Smdodd 301112800Smdodd return (0); 302112800Smdodd} 303112800Smdodd 30455883Smdoddstatic void 30555883Smdoddex_init(void *xsc) 30621769Sjkh{ 30752286Smdodd struct ex_softc * sc = (struct ex_softc *) xsc; 308179775Sjhb 309179775Sjhb EX_LOCK(sc); 310179775Sjhb ex_init_locked(sc); 311179775Sjhb EX_UNLOCK(sc); 312179775Sjhb} 313179775Sjhb 314179775Sjhbstatic void 315179775Sjhbex_init_locked(struct ex_softc *sc) 316179775Sjhb{ 317147256Sbrooks struct ifnet * ifp = sc->ifp; 31852286Smdodd int i; 31952286Smdodd unsigned short temp_reg; 32021769Sjkh 321121816Sbrooks DODEBUG(Start_End, printf("%s: ex_init: start\n", ifp->if_xname);); 32221769Sjkh 323179775Sjhb sc->tx_timeout = 0; 32421769Sjkh 32521769Sjkh /* 32621769Sjkh * Load the ethernet address into the card. 32721769Sjkh */ 328131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 329131192Simp temp_reg = CSR_READ_1(sc, EEPROM_REG); 330182088Simp if (temp_reg & Trnoff_Enable) 331131192Simp CSR_WRITE_1(sc, EEPROM_REG, temp_reg & ~Trnoff_Enable); 332182088Simp for (i = 0; i < ETHER_ADDR_LEN; i++) 333152315Sru CSR_WRITE_1(sc, I_ADDR_REG0 + i, IF_LLADDR(sc->ifp)[i]); 334182088Simp 33521769Sjkh /* 33621769Sjkh * - Setup transmit chaining and discard bad received frames. 33721769Sjkh * - Match broadcast. 33821769Sjkh * - Clear test mode. 33921769Sjkh * - Set receiving mode. 34021769Sjkh */ 341131192Simp CSR_WRITE_1(sc, REG1, CSR_READ_1(sc, REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr); 342131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) | No_SA_Ins | RX_CRC_InMem); 343131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3) & 0x3f /* XXX constants. */ ); 344182088Simp /* 345182088Simp * - Set IRQ number, if this part has it. ISA devices have this, 346182088Simp * while PC Card devices don't seem to. Either way, we have to 347182088Simp * switch to Bank1 as the rest of this code relies on that. 348182088Simp */ 349131192Simp CSR_WRITE_1(sc, CMD_REG, Bank1_Sel); 350182088Simp if (sc->flags & HAS_INT_NO_REG) 351182088Simp CSR_WRITE_1(sc, INT_NO_REG, 352182088Simp (CSR_READ_1(sc, INT_NO_REG) & 0xf8) | 353182088Simp sc->irq2ee[sc->irq_no]); 35421769Sjkh 35521769Sjkh /* 35621769Sjkh * Divide the available memory in the card into rcv and xmt buffers. 35721769Sjkh * By default, I use the first 3/4 of the memory for the rcv buffer, 35821769Sjkh * and the remaining 1/4 of the memory for the xmt buffer. 35921769Sjkh */ 36021769Sjkh sc->rx_mem_size = sc->mem_size * 3 / 4; 36121769Sjkh sc->tx_mem_size = sc->mem_size - sc->rx_mem_size; 36221769Sjkh sc->rx_lower_limit = 0x0000; 36321769Sjkh sc->rx_upper_limit = sc->rx_mem_size - 2; 36421769Sjkh sc->tx_lower_limit = sc->rx_mem_size; 36521769Sjkh sc->tx_upper_limit = sc->mem_size - 2; 366131192Simp CSR_WRITE_1(sc, RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8); 367131192Simp CSR_WRITE_1(sc, RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8); 368131192Simp CSR_WRITE_1(sc, XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8); 369131192Simp CSR_WRITE_1(sc, XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8); 37021769Sjkh 37121769Sjkh /* 37221769Sjkh * Enable receive and transmit interrupts, and clear any pending int. 37321769Sjkh */ 374131192Simp CSR_WRITE_1(sc, REG1, CSR_READ_1(sc, REG1) | TriST_INT); 375131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 376131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 377131192Simp CSR_WRITE_1(sc, STATUS_REG, All_Int); 37821769Sjkh 37921769Sjkh /* 38021769Sjkh * Initialize receive and transmit ring buffers. 38121769Sjkh */ 382131192Simp CSR_WRITE_2(sc, RCV_BAR, sc->rx_lower_limit); 38321769Sjkh sc->rx_head = sc->rx_lower_limit; 384131192Simp CSR_WRITE_2(sc, RCV_STOP_REG, sc->rx_upper_limit | 0xfe); 385131192Simp CSR_WRITE_2(sc, XMT_BAR, sc->tx_lower_limit); 38621769Sjkh sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 38721769Sjkh 388148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 389148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 39021769Sjkh DODEBUG(Status, printf("OIDLE init\n");); 391179775Sjhb callout_reset(&sc->timer, hz, ex_watchdog, sc); 39221769Sjkh 393112731Smdodd ex_setmulti(sc); 394112731Smdodd 39521769Sjkh /* 39621769Sjkh * Final reset of the board, and enable operation. 39721769Sjkh */ 398131192Simp CSR_WRITE_1(sc, CMD_REG, Sel_Reset_CMD); 39921769Sjkh DELAY(2); 400131192Simp CSR_WRITE_1(sc, CMD_REG, Rcv_Enable_CMD); 40121769Sjkh 402179775Sjhb ex_start_locked(ifp); 40321769Sjkh 404121816Sbrooks DODEBUG(Start_End, printf("%s: ex_init: finish\n", ifp->if_xname);); 40521769Sjkh} 40621769Sjkh 40755883Smdoddstatic void 40852286Smdoddex_start(struct ifnet *ifp) 40921769Sjkh{ 41052286Smdodd struct ex_softc * sc = ifp->if_softc; 411179775Sjhb 412179775Sjhb EX_LOCK(sc); 413179775Sjhb ex_start_locked(ifp); 414179775Sjhb EX_UNLOCK(sc); 415179775Sjhb} 416179775Sjhb 417179775Sjhbstatic void 418179775Sjhbex_start_locked(struct ifnet *ifp) 419179775Sjhb{ 420179775Sjhb struct ex_softc * sc = ifp->if_softc; 421179775Sjhb int i, len, data_len, avail, dest, next; 42252286Smdodd unsigned char tmp16[2]; 42352286Smdodd struct mbuf * opkt; 42452286Smdodd struct mbuf * m; 42521769Sjkh 42652286Smdodd DODEBUG(Start_End, printf("ex_start%d: start\n", unit);); 42721769Sjkh 42852286Smdodd /* 42952286Smdodd * Main loop: send outgoing packets to network card until there are no 43052286Smdodd * more packets left, or the card cannot accept any more yet. 43152286Smdodd */ 43252286Smdodd while (((opkt = ifp->if_snd.ifq_head) != NULL) && 433148887Srwatson !(ifp->if_drv_flags & IFF_DRV_OACTIVE)) { 43421769Sjkh 43552286Smdodd /* 43652286Smdodd * Ensure there is enough free transmit buffer space for 43752286Smdodd * this packet, including its header. Note: the header 43852286Smdodd * cannot wrap around the end of the transmit buffer and 43952286Smdodd * must be kept together, so we allow space for twice the 44052286Smdodd * length of the header, just in case. 44152286Smdodd */ 44221769Sjkh 44352286Smdodd for (len = 0, m = opkt; m != NULL; m = m->m_next) { 44452286Smdodd len += m->m_len; 44552286Smdodd } 44652286Smdodd 44752286Smdodd data_len = len; 44852286Smdodd 44952286Smdodd DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len);); 45052286Smdodd 45152286Smdodd if (len & 1) { 45252286Smdodd len += XMT_HEADER_LEN + 1; 45352286Smdodd } else { 45452286Smdodd len += XMT_HEADER_LEN; 45552286Smdodd } 45652286Smdodd 45752286Smdodd if ((i = sc->tx_tail - sc->tx_head) >= 0) { 45852286Smdodd avail = sc->tx_mem_size - i; 45952286Smdodd } else { 46052286Smdodd avail = -i; 46152286Smdodd } 46252286Smdodd 46352286Smdodd DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail);); 46452286Smdodd 46552286Smdodd if (avail >= len + XMT_HEADER_LEN) { 46652286Smdodd IF_DEQUEUE(&ifp->if_snd, opkt); 46752286Smdodd 46821769Sjkh#ifdef EX_PSA_INTR 46952286Smdodd /* 47052286Smdodd * Disable rx and tx interrupts, to avoid corruption 47152286Smdodd * of the host address register by interrupt service 47252286Smdodd * routines. 47352286Smdodd * XXX Is this necessary with splimp() enabled? 47452286Smdodd */ 475131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int); 47621769Sjkh#endif 47721769Sjkh 47852286Smdodd /* 47952286Smdodd * Compute the start and end addresses of this 48052286Smdodd * frame in the tx buffer. 48152286Smdodd */ 48252286Smdodd dest = sc->tx_tail; 48352286Smdodd next = dest + len; 48421769Sjkh 48552286Smdodd if (next > sc->tx_upper_limit) { 48652286Smdodd if ((sc->tx_upper_limit + 2 - sc->tx_tail) <= 48752286Smdodd XMT_HEADER_LEN) { 48852286Smdodd dest = sc->tx_lower_limit; 48952286Smdodd next = dest + len; 49055881Smdodd } else { 49155881Smdodd next = sc->tx_lower_limit + 49255881Smdodd next - sc->tx_upper_limit - 2; 49352286Smdodd } 49452286Smdodd } 49521769Sjkh 49652286Smdodd /* 49752286Smdodd * Build the packet frame in the card's ring buffer. 49852286Smdodd */ 49952286Smdodd DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next);); 50021769Sjkh 501131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, dest); 502131192Simp CSR_WRITE_2(sc, IO_PORT_REG, Transmit_CMD); 503131192Simp CSR_WRITE_2(sc, IO_PORT_REG, 0); 504131192Simp CSR_WRITE_2(sc, IO_PORT_REG, next); 505131192Simp CSR_WRITE_2(sc, IO_PORT_REG, data_len); 50621769Sjkh 50752286Smdodd /* 50852286Smdodd * Output the packet data to the card. Ensure all 50952286Smdodd * transfers are 16-bit wide, even if individual 51052286Smdodd * mbufs have odd length. 51152286Smdodd */ 51252286Smdodd for (m = opkt, i = 0; m != NULL; m = m->m_next) { 51352286Smdodd DODEBUG(Sent_Pkts, printf("[%d]", m->m_len);); 51452286Smdodd if (i) { 51552286Smdodd tmp16[1] = *(mtod(m, caddr_t)); 516131192Simp CSR_WRITE_MULTI_2(sc, IO_PORT_REG, 517131192Simp (uint16_t *) tmp16, 1); 51852286Smdodd } 519131192Simp CSR_WRITE_MULTI_2(sc, IO_PORT_REG, 520131192Simp (uint16_t *) (mtod(m, caddr_t) + i), 521131192Simp (m->m_len - i) / 2); 52252286Smdodd if ((i = (m->m_len - i) & 1) != 0) { 52352286Smdodd tmp16[0] = *(mtod(m, caddr_t) + 52452286Smdodd m->m_len - 1); 52552286Smdodd } 52652286Smdodd } 527131192Simp if (i) 528131192Simp CSR_WRITE_MULTI_2(sc, IO_PORT_REG, 529131192Simp (uint16_t *) tmp16, 1); 53055881Smdodd /* 53155881Smdodd * If there were other frames chained, update the 53255881Smdodd * chain in the last one. 53355881Smdodd */ 53455881Smdodd if (sc->tx_head != sc->tx_tail) { 53555881Smdodd if (sc->tx_tail != dest) { 536131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, 53755881Smdodd sc->tx_last + XMT_Chain_Point); 538131192Simp CSR_WRITE_2(sc, IO_PORT_REG, dest); 53952286Smdodd } 540131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, 54155881Smdodd sc->tx_last + XMT_Byte_Count); 542131192Simp i = CSR_READ_2(sc, IO_PORT_REG); 543131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, 54455881Smdodd sc->tx_last + XMT_Byte_Count); 545131192Simp CSR_WRITE_2(sc, IO_PORT_REG, i | Ch_bit); 54655881Smdodd } 54755881Smdodd 54855881Smdodd /* 54955881Smdodd * Resume normal operation of the card: 55055881Smdodd * - Make a dummy read to flush the DRAM write 55155881Smdodd * pipeline. 55255881Smdodd * - Enable receive and transmit interrupts. 55355881Smdodd * - Send Transmit or Resume_XMT command, as 55455881Smdodd * appropriate. 55555881Smdodd */ 556131192Simp CSR_READ_2(sc, IO_PORT_REG); 55721769Sjkh#ifdef EX_PSA_INTR 558131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 55921769Sjkh#endif 56055881Smdodd if (sc->tx_head == sc->tx_tail) { 561131192Simp CSR_WRITE_2(sc, XMT_BAR, dest); 562131192Simp CSR_WRITE_1(sc, CMD_REG, Transmit_CMD); 56355881Smdodd sc->tx_head = dest; 56455881Smdodd DODEBUG(Sent_Pkts, printf("Transmit\n");); 56552286Smdodd } else { 566131192Simp CSR_WRITE_1(sc, CMD_REG, Resume_XMT_List_CMD); 56755881Smdodd DODEBUG(Sent_Pkts, printf("Resume\n");); 56852286Smdodd } 56955881Smdodd 57055881Smdodd sc->tx_last = dest; 57155881Smdodd sc->tx_tail = next; 57255881Smdodd 573106937Ssam BPF_MTAP(ifp, opkt); 57455881Smdodd 575179775Sjhb sc->tx_timeout = 2; 576271849Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 57755881Smdodd m_freem(opkt); 57855881Smdodd } else { 579148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 58055881Smdodd DODEBUG(Status, printf("OACTIVE start\n");); 58152286Smdodd } 58221769Sjkh } 58321769Sjkh 58452286Smdodd DODEBUG(Start_End, printf("ex_start%d: finish\n", unit);); 58521769Sjkh} 58621769Sjkh 58766440Simpvoid 58852286Smdoddex_stop(struct ex_softc *sc) 58921769Sjkh{ 590131192Simp 59152286Smdodd DODEBUG(Start_End, printf("ex_stop%d: start\n", unit);); 59221769Sjkh 593179775Sjhb EX_ASSERT_LOCKED(sc); 59452286Smdodd /* 59552286Smdodd * Disable card operation: 59652286Smdodd * - Disable the interrupt line. 59752286Smdodd * - Flush transmission and disable reception. 59852286Smdodd * - Mask and clear all interrupts. 59952286Smdodd * - Reset the 82595. 60052286Smdodd */ 601131192Simp CSR_WRITE_1(sc, CMD_REG, Bank1_Sel); 602131192Simp CSR_WRITE_1(sc, REG1, CSR_READ_1(sc, REG1) & ~TriST_INT); 603131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 604131192Simp CSR_WRITE_1(sc, CMD_REG, Rcv_Stop); 60552286Smdodd sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 60652286Smdodd 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. */ 607131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int); 608131192Simp CSR_WRITE_1(sc, STATUS_REG, All_Int); 609131192Simp CSR_WRITE_1(sc, CMD_REG, Reset_CMD); 61052286Smdodd DELAY(200); 611179775Sjhb sc->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 612179775Sjhb sc->tx_timeout = 0; 613179775Sjhb callout_stop(&sc->timer); 61421769Sjkh 61552286Smdodd DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit);); 61652286Smdodd 61752286Smdodd return; 61821769Sjkh} 61921769Sjkh 62059816Smdoddvoid 62155883Smdoddex_intr(void *arg) 62221769Sjkh{ 623131192Simp struct ex_softc *sc = (struct ex_softc *)arg; 624147256Sbrooks struct ifnet *ifp = sc->ifp; 625131192Simp int int_status, send_pkts; 626131192Simp int loops = 100; 62721769Sjkh 62855883Smdodd DODEBUG(Start_End, printf("ex_intr%d: start\n", unit);); 62921769Sjkh 630179775Sjhb EX_LOCK(sc); 63152286Smdodd send_pkts = 0; 632131192Simp while (loops-- > 0 && 633131192Simp (int_status = CSR_READ_1(sc, STATUS_REG)) & (Tx_Int | Rx_Int)) { 634131192Simp /* don't loop forever */ 635131192Simp if (int_status == 0xff) 636131192Simp break; 63752286Smdodd if (int_status & Rx_Int) { 638131192Simp CSR_WRITE_1(sc, STATUS_REG, Rx_Int); 63952286Smdodd ex_rx_intr(sc); 64052286Smdodd } else if (int_status & Tx_Int) { 641131192Simp CSR_WRITE_1(sc, STATUS_REG, Tx_Int); 64252286Smdodd ex_tx_intr(sc); 64352286Smdodd send_pkts = 1; 64452286Smdodd } 64552286Smdodd } 646131192Simp if (loops == 0) 647131192Simp printf("100 loops are not enough\n"); 64821769Sjkh 64952286Smdodd /* 65052286Smdodd * If any packet has been transmitted, and there are queued packets to 65152286Smdodd * be sent, attempt to send more packets to the network card. 65252286Smdodd */ 653131192Simp if (send_pkts && (ifp->if_snd.ifq_head != NULL)) 654179775Sjhb ex_start_locked(ifp); 655179775Sjhb EX_UNLOCK(sc); 65652286Smdodd 65755883Smdodd DODEBUG(Start_End, printf("ex_intr%d: finish\n", unit);); 65852286Smdodd 65952286Smdodd return; 66021769Sjkh} 66121769Sjkh 66252286Smdoddstatic void 66352286Smdoddex_tx_intr(struct ex_softc *sc) 66421769Sjkh{ 665147256Sbrooks struct ifnet * ifp = sc->ifp; 66652286Smdodd int tx_status; 66721769Sjkh 66852286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit);); 66921769Sjkh 67052286Smdodd /* 67152286Smdodd * - Cancel the watchdog. 67252286Smdodd * For all packets transmitted since last transmit interrupt: 67352286Smdodd * - Advance chain pointer to next queued packet. 67452286Smdodd * - Update statistics. 67552286Smdodd */ 67621769Sjkh 677179775Sjhb sc->tx_timeout = 0; 67821769Sjkh 67952286Smdodd while (sc->tx_head != sc->tx_tail) { 680131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->tx_head); 68121769Sjkh 682201794Strasz if (!(CSR_READ_2(sc, IO_PORT_REG) & Done_bit)) 68352286Smdodd break; 68421769Sjkh 685131192Simp tx_status = CSR_READ_2(sc, IO_PORT_REG); 686131192Simp sc->tx_head = CSR_READ_2(sc, IO_PORT_REG); 68752286Smdodd 68852286Smdodd if (tx_status & TX_OK_bit) { 689271849Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 69052286Smdodd } else { 691271849Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 69252286Smdodd } 69352286Smdodd 694271849Sglebius if_inc_counter(ifp, IFCOUNTER_COLLISIONS, tx_status & No_Collisions_bits); 69552286Smdodd } 69652286Smdodd 69752286Smdodd /* 69852286Smdodd * The card should be ready to accept more packets now. 69952286Smdodd */ 70052286Smdodd 701148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 70252286Smdodd 70352286Smdodd DODEBUG(Status, printf("OIDLE tx_intr\n");); 70452286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit);); 70552286Smdodd 70652286Smdodd return; 70721769Sjkh} 70821769Sjkh 70952286Smdoddstatic void 71052286Smdoddex_rx_intr(struct ex_softc *sc) 71121769Sjkh{ 712147256Sbrooks struct ifnet * ifp = sc->ifp; 71352286Smdodd int rx_status; 71452286Smdodd int pkt_len; 71552286Smdodd int QQQ; 71652286Smdodd struct mbuf * m; 71752286Smdodd struct mbuf * ipkt; 71852286Smdodd struct ether_header * eh; 71921769Sjkh 72052286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit);); 72121769Sjkh 72252286Smdodd /* 72352286Smdodd * For all packets received since last receive interrupt: 72452286Smdodd * - If packet ok, read it into a new mbuf and queue it to interface, 72552286Smdodd * updating statistics. 72652286Smdodd * - If packet bad, just discard it, and update statistics. 72752286Smdodd * Finally, advance receive stop limit in card's memory to new location. 72852286Smdodd */ 72921769Sjkh 730131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->rx_head); 73121769Sjkh 732131192Simp while (CSR_READ_2(sc, IO_PORT_REG) == RCV_Done) { 73352286Smdodd 734131192Simp rx_status = CSR_READ_2(sc, IO_PORT_REG); 735131192Simp sc->rx_head = CSR_READ_2(sc, IO_PORT_REG); 736131192Simp QQQ = pkt_len = CSR_READ_2(sc, IO_PORT_REG); 73752286Smdodd 73852286Smdodd if (rx_status & RCV_OK_bit) { 739243857Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 74052286Smdodd ipkt = m; 74152286Smdodd if (ipkt == NULL) { 742271849Sglebius if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 74352286Smdodd } else { 74452286Smdodd ipkt->m_pkthdr.rcvif = ifp; 74552286Smdodd ipkt->m_pkthdr.len = pkt_len; 74652286Smdodd ipkt->m_len = MHLEN; 74752286Smdodd 74852286Smdodd while (pkt_len > 0) { 749136625Sglebius if (pkt_len >= MINCLSIZE) { 750276750Srwatson if (MCLGET(m, M_NOWAIT)) { 75152286Smdodd m->m_len = MCLBYTES; 75252286Smdodd } else { 75352286Smdodd m_freem(ipkt); 754271849Sglebius if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 75552286Smdodd goto rx_another; 75652286Smdodd } 75752286Smdodd } 75852286Smdodd m->m_len = min(m->m_len, pkt_len); 75952286Smdodd 76021769Sjkh /* 76121769Sjkh * NOTE: I'm assuming that all mbufs allocated are of even length, 76221769Sjkh * except for the last one in an odd-length packet. 76321769Sjkh */ 76452286Smdodd 765131192Simp CSR_READ_MULTI_2(sc, IO_PORT_REG, 766131192Simp mtod(m, uint16_t *), m->m_len / 2); 76752286Smdodd 76852286Smdodd if (m->m_len & 1) { 769131192Simp *(mtod(m, caddr_t) + m->m_len - 1) = CSR_READ_1(sc, IO_PORT_REG); 77052286Smdodd } 77152286Smdodd pkt_len -= m->m_len; 77252286Smdodd 77352286Smdodd if (pkt_len > 0) { 774243857Sglebius MGET(m->m_next, M_NOWAIT, MT_DATA); 77552286Smdodd if (m->m_next == NULL) { 77652286Smdodd m_freem(ipkt); 777271849Sglebius if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 77852286Smdodd goto rx_another; 77952286Smdodd } 78052286Smdodd m = m->m_next; 78152286Smdodd m->m_len = MLEN; 78252286Smdodd } 78352286Smdodd } 78452286Smdodd eh = mtod(ipkt, struct ether_header *); 78552286Smdodd#ifdef EXDEBUG 78652286Smdodd if (debug_mask & Rcvd_Pkts) { 78752286Smdodd if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) { 78852286Smdodd printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":"); 78952286Smdodd printf("%6D\n", eh->ether_dhost, ":"); 79052286Smdodd } /* QQQ */ 79121769Sjkh } 79221769Sjkh#endif 793179775Sjhb EX_UNLOCK(sc); 794106937Ssam (*ifp->if_input)(ifp, ipkt); 795179775Sjhb EX_LOCK(sc); 796271849Sglebius if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 79752286Smdodd } 79852286Smdodd } else { 799271849Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 80021769Sjkh } 801131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->rx_head); 80252286Smdoddrx_another: ; 80321769Sjkh } 80421769Sjkh 80552286Smdodd if (sc->rx_head < sc->rx_lower_limit + 2) 806131192Simp CSR_WRITE_2(sc, RCV_STOP_REG, sc->rx_upper_limit); 80752286Smdodd else 808131192Simp CSR_WRITE_2(sc, RCV_STOP_REG, sc->rx_head - 2); 80952286Smdodd 81052286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit);); 81152286Smdodd 81252286Smdodd return; 81321769Sjkh} 81421769Sjkh 81521769Sjkh 81655883Smdoddstatic int 81755883Smdoddex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 81821769Sjkh{ 81952286Smdodd struct ex_softc * sc = ifp->if_softc; 82057987Smdodd struct ifreq * ifr = (struct ifreq *)data; 82152286Smdodd int error = 0; 82221769Sjkh 823121816Sbrooks DODEBUG(Start_End, printf("%s: ex_ioctl: start ", ifp->if_xname);); 82421769Sjkh 82552286Smdodd switch(cmd) { 82652286Smdodd case SIOCSIFADDR: 82752286Smdodd case SIOCGIFADDR: 82852286Smdodd case SIOCSIFMTU: 82952286Smdodd error = ether_ioctl(ifp, cmd, data); 83052286Smdodd break; 83121769Sjkh 83252286Smdodd case SIOCSIFFLAGS: 83352286Smdodd DODEBUG(Start_End, printf("SIOCSIFFLAGS");); 834179775Sjhb EX_LOCK(sc); 83552286Smdodd if ((ifp->if_flags & IFF_UP) == 0 && 836148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 83752286Smdodd ex_stop(sc); 83852286Smdodd } else { 839179775Sjhb ex_init_locked(sc); 84052286Smdodd } 841179775Sjhb EX_UNLOCK(sc); 84252286Smdodd break; 84352286Smdodd case SIOCADDMULTI: 84452286Smdodd case SIOCDELMULTI: 845112731Smdodd ex_init(sc); 846112731Smdodd error = 0; 84752286Smdodd break; 84857987Smdodd case SIOCSIFMEDIA: 84957987Smdodd case SIOCGIFMEDIA: 85057987Smdodd error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd); 85157987Smdodd break; 85252286Smdodd default: 85352286Smdodd DODEBUG(Start_End, printf("unknown");); 85452286Smdodd error = EINVAL; 85552286Smdodd } 85621769Sjkh 857121816Sbrooks DODEBUG(Start_End, printf("\n%s: ex_ioctl: finish\n", ifp->if_xname);); 85852286Smdodd 85952286Smdodd return(error); 86021769Sjkh} 86121769Sjkh 862112731Smdoddstatic void 863112731Smdoddex_setmulti(struct ex_softc *sc) 864112731Smdodd{ 865112731Smdodd struct ifnet *ifp; 866112731Smdodd struct ifmultiaddr *maddr; 867131192Simp uint16_t *addr; 868112731Smdodd int count; 869112731Smdodd int timeout, status; 870112731Smdodd 871147256Sbrooks ifp = sc->ifp; 87221769Sjkh 873112731Smdodd count = 0; 874195049Srwatson if_maddr_rlock(ifp); 875112731Smdodd TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { 876112731Smdodd if (maddr->ifma_addr->sa_family != AF_LINK) 877112731Smdodd continue; 878112731Smdodd count++; 879112731Smdodd } 880195049Srwatson if_maddr_runlock(ifp); 881112731Smdodd 882112731Smdodd if ((ifp->if_flags & IFF_PROMISC) || (ifp->if_flags & IFF_ALLMULTI) 883112731Smdodd || count > 63) { 884112731Smdodd /* Interface is in promiscuous mode or there are too many 885112731Smdodd * multicast addresses for the card to handle */ 886131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 887131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) | Promisc_Mode); 888131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3)); 889131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 890112731Smdodd } 891112731Smdodd else if ((ifp->if_flags & IFF_MULTICAST) && (count > 0)) { 892112731Smdodd /* Program multicast addresses plus our MAC address 893112731Smdodd * into the filter */ 894131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 895131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) | Multi_IA); 896131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3)); 897131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 898112731Smdodd 899112731Smdodd /* Borrow space from TX buffer; this should be safe 900112731Smdodd * as this is only called from ex_init */ 901112731Smdodd 902131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->tx_lower_limit); 903131192Simp CSR_WRITE_2(sc, IO_PORT_REG, MC_Setup_CMD); 904131192Simp CSR_WRITE_2(sc, IO_PORT_REG, 0); 905131192Simp CSR_WRITE_2(sc, IO_PORT_REG, 0); 906131192Simp CSR_WRITE_2(sc, IO_PORT_REG, (count + 1) * 6); 907148654Srwatson 908195049Srwatson if_maddr_rlock(ifp); 909112731Smdodd TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { 910112731Smdodd if (maddr->ifma_addr->sa_family != AF_LINK) 911112731Smdodd continue; 912112731Smdodd 913131192Simp addr = (uint16_t*)LLADDR((struct sockaddr_dl *) 914112731Smdodd maddr->ifma_addr); 915131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 916131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 917131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 918112731Smdodd } 919195049Srwatson if_maddr_runlock(ifp); 920112731Smdodd 921112731Smdodd /* Program our MAC address as well */ 922112731Smdodd /* XXX: Is this necessary? The Linux driver does this 923112731Smdodd * but the NetBSD driver does not */ 924152315Sru addr = (uint16_t*)IF_LLADDR(sc->ifp); 925131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 926131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 927131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 928112731Smdodd 929131192Simp CSR_READ_2(sc, IO_PORT_REG); 930131192Simp CSR_WRITE_2(sc, XMT_BAR, sc->tx_lower_limit); 931131192Simp CSR_WRITE_1(sc, CMD_REG, MC_Setup_CMD); 932112731Smdodd 933112731Smdodd sc->tx_head = sc->tx_lower_limit; 934112731Smdodd sc->tx_tail = sc->tx_head + XMT_HEADER_LEN + (count + 1) * 6; 935112731Smdodd 936112731Smdodd for (timeout=0; timeout<100; timeout++) { 937112731Smdodd DELAY(2); 938131192Simp if ((CSR_READ_1(sc, STATUS_REG) & Exec_Int) == 0) 939112731Smdodd continue; 940112731Smdodd 941131192Simp status = CSR_READ_1(sc, CMD_REG); 942131192Simp CSR_WRITE_1(sc, STATUS_REG, Exec_Int); 943112731Smdodd break; 944112731Smdodd } 945112731Smdodd 946112731Smdodd sc->tx_head = sc->tx_tail; 947112731Smdodd } 948112731Smdodd else 949112731Smdodd { 950112731Smdodd /* No multicast or promiscuous mode */ 951131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 952131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) & 0xDE); 953112731Smdodd /* ~(Multi_IA | Promisc_Mode) */ 954131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3)); 955131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 956112731Smdodd } 957112731Smdodd} 958112731Smdodd 95952286Smdoddstatic void 96052286Smdoddex_reset(struct ex_softc *sc) 96121769Sjkh{ 96221769Sjkh 96352286Smdodd DODEBUG(Start_End, printf("ex_reset%d: start\n", unit);); 96421769Sjkh 965179775Sjhb EX_ASSERT_LOCKED(sc); 96652286Smdodd ex_stop(sc); 967179775Sjhb ex_init_locked(sc); 96821769Sjkh 96952286Smdodd DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit);); 97052286Smdodd 97152286Smdodd return; 97221769Sjkh} 97321769Sjkh 97452286Smdoddstatic void 975179775Sjhbex_watchdog(void *arg) 97621769Sjkh{ 977179775Sjhb struct ex_softc * sc = arg; 978179775Sjhb struct ifnet *ifp = sc->ifp; 97921769Sjkh 980179775Sjhb if (sc->tx_timeout && --sc->tx_timeout == 0) { 981179775Sjhb DODEBUG(Start_End, if_printf(ifp, "ex_watchdog: start\n");); 98221769Sjkh 983179775Sjhb ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 98421769Sjkh 985179775Sjhb DODEBUG(Status, printf("OIDLE watchdog\n");); 98652286Smdodd 987271849Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 988179775Sjhb ex_reset(sc); 989179775Sjhb ex_start_locked(ifp); 99052286Smdodd 991179775Sjhb DODEBUG(Start_End, if_printf(ifp, "ex_watchdog: finish\n");); 992179775Sjhb } 99352286Smdodd 994179775Sjhb callout_reset(&sc->timer, hz, ex_watchdog, sc); 99521769Sjkh} 99621769Sjkh 99757987Smdoddstatic int 998131192Simpex_get_media(struct ex_softc *sc) 99959816Smdodd{ 1000112764Smdodd int current; 1001112764Smdodd int media; 100259816Smdodd 1003131192Simp media = ex_eeprom_read(sc, EE_W5); 1004112764Smdodd 1005131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 1006131192Simp current = CSR_READ_1(sc, REG3); 1007131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 100859816Smdodd 1009112764Smdodd if ((current & TPE_bit) && (media & EE_W5_PORT_TPE)) 101059816Smdodd return(IFM_ETHER|IFM_10_T); 1011112764Smdodd if ((current & BNC_bit) && (media & EE_W5_PORT_BNC)) 101259816Smdodd return(IFM_ETHER|IFM_10_2); 101359816Smdodd 1014112764Smdodd if (media & EE_W5_PORT_AUI) 1015112764Smdodd return (IFM_ETHER|IFM_10_5); 1016112764Smdodd 1017112764Smdodd return (IFM_ETHER|IFM_AUTO); 101859816Smdodd} 101959816Smdodd 102059816Smdoddstatic int 1021131192Simpex_ifmedia_upd(ifp) 102257987Smdodd struct ifnet * ifp; 102357987Smdodd{ 1024112764Smdodd struct ex_softc * sc = ifp->if_softc; 102521769Sjkh 1026112764Smdodd if (IFM_TYPE(sc->ifmedia.ifm_media) != IFM_ETHER) 1027112764Smdodd return EINVAL; 1028112764Smdodd 102957987Smdodd return (0); 103057987Smdodd} 103157987Smdodd 103257987Smdoddstatic void 103357987Smdoddex_ifmedia_sts(ifp, ifmr) 103457987Smdodd struct ifnet * ifp; 103557987Smdodd struct ifmediareq * ifmr; 103657987Smdodd{ 103757987Smdodd struct ex_softc * sc = ifp->if_softc; 103857987Smdodd 1039179775Sjhb EX_LOCK(sc); 1040131192Simp ifmr->ifm_active = ex_get_media(sc); 1041112764Smdodd ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; 1042179775Sjhb EX_UNLOCK(sc); 104357987Smdodd 104457987Smdodd return; 104557987Smdodd} 104657987Smdodd 104759816Smdoddu_short 1048131192Simpex_eeprom_read(struct ex_softc *sc, int location) 104921769Sjkh{ 105021769Sjkh int i; 105121769Sjkh u_short data = 0; 105221769Sjkh int read_cmd = location | EE_READ_CMD; 105321769Sjkh short ctrl_val = EECS; 105421769Sjkh 1055131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 1056131192Simp CSR_WRITE_1(sc, EEPROM_REG, EECS); 105721769Sjkh for (i = 8; i >= 0; i--) { 105821769Sjkh short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; 1059131192Simp CSR_WRITE_1(sc, EEPROM_REG, outval); 1060131192Simp CSR_WRITE_1(sc, EEPROM_REG, outval | EESK); 106121769Sjkh DELAY(3); 1062131192Simp CSR_WRITE_1(sc, EEPROM_REG, outval); 106321769Sjkh DELAY(2); 106421769Sjkh } 1065131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val); 106621769Sjkh 106721769Sjkh for (i = 16; i > 0; i--) { 1068131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val | EESK); 106921769Sjkh DELAY(3); 1070131192Simp data = (data << 1) | 1071131192Simp ((CSR_READ_1(sc, EEPROM_REG) & EEDO) ? 1 : 0); 1072131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val); 107321769Sjkh DELAY(2); 107421769Sjkh } 107521769Sjkh 107621769Sjkh ctrl_val &= ~EECS; 1077131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val | EESK); 107821769Sjkh DELAY(3); 1079131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val); 108021769Sjkh DELAY(2); 1081131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 108221769Sjkh return(data); 108321769Sjkh} 1084