1139749Simp/*- 219410Sguido * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca> 319410Sguido * All rights reserved. 419410Sguido * 519410Sguido * Redistribution and use in source and binary forms, with or without 619410Sguido * modification, are permitted provided that the following conditions 719410Sguido * are met: 819410Sguido * 1. Redistributions of source code must retain the above copyright 919410Sguido * notice, this list of conditions and the following disclaimer. 1019410Sguido * 2. Redistributions in binary form must reproduce the above copyright 1119410Sguido * notice, this list of conditions and the following disclaimer in the 1219410Sguido * documentation and/or other materials provided with the distribution. 1319410Sguido * 3. All advertising materials mentioning features or use of this software 1419410Sguido * must display the following acknowledgement: 1519410Sguido * This product includes software developed by Herb Peyerl. 1619410Sguido * 4. The name of Herb Peyerl may not be used to endorse or promote products 1719410Sguido * derived from this software without specific prior written permission. 1819410Sguido * 1919410Sguido * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2019410Sguido * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2119410Sguido * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2219410Sguido * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2319410Sguido * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2419410Sguido * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2519410Sguido * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2619410Sguido * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2719410Sguido * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2819410Sguido * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2919410Sguido * 3033707Sgpalmer * 3119410Sguido */ 3219410Sguido 33119418Sobrien#include <sys/cdefs.h> 34119418Sobrien__FBSDID("$FreeBSD$"); 35119418Sobrien 3619410Sguido/* 3719410Sguido * Created from if_ep.c driver by Fred Gray (fgray@rice.edu) to support 3819410Sguido * the 3c590 family. 3919410Sguido */ 4019410Sguido 4119410Sguido/* 4219410Sguido * Modified from the FreeBSD 1.1.5.1 version by: 4319410Sguido * Andres Vega Garcia 4419410Sguido * INRIA - Sophia Antipolis, France 4519410Sguido * avega@sophia.inria.fr 4619410Sguido */ 4719410Sguido 4819410Sguido/* 4919410Sguido * Promiscuous mode added and interrupt logic slightly changed 5019410Sguido * to reduce the number of adapter failures. Transceiver select 5119410Sguido * logic changed to use value from EEPROM. Autoconfiguration 5219410Sguido * features added. 5319410Sguido * Done by: 5419410Sguido * Serge Babkin 5519410Sguido * Chelindbank (Chelyabinsk, Russia) 5619410Sguido * babkin@hq.icb.chel.su 5719410Sguido */ 5819410Sguido 5919410Sguido 6019410Sguido#include <sys/param.h> 6119410Sguido#include <sys/systm.h> 6224204Sbde#include <sys/sockio.h> 63151014Sjhb#include <sys/kernel.h> 6426640Sbde#include <sys/malloc.h> 6519410Sguido#include <sys/mbuf.h> 6619410Sguido#include <sys/socket.h> 6719410Sguido 6819410Sguido#include <net/if.h> 6919410Sguido 7032350Seivind#include <net/ethernet.h> 71152315Sru#include <net/if_dl.h> 72147256Sbrooks#include <net/if_types.h> 7319410Sguido 7468417Swpaul#include <machine/bus.h> 7568417Swpaul 76121816Sbrooks#include <sys/bus.h> 77121816Sbrooks 7819410Sguido#include <net/bpf.h> 7919410Sguido 8019410Sguido#include <dev/vx/if_vxreg.h> 81121491Simp#include <dev/vx/if_vxvar.h> 8219410Sguido 8319410Sguido#define ETHER_MAX_LEN 1518 8419410Sguido#define ETHER_ADDR_LEN 6 8569732Swpaul#define ETHER_ALIGN 2 8619410Sguido 8719410Sguidostatic struct connector_entry { 88133980Sgibbs int bit; 89133980Sgibbs char *name; 9020096Sguido} conn_tab[VX_CONNECTORS] = { 91133980Sgibbs 9219410Sguido#define CONNECTOR_UTP 0 93133980Sgibbs { 94133980Sgibbs 0x08, "utp" 95133980Sgibbs }, 9619410Sguido#define CONNECTOR_AUI 1 97133980Sgibbs { 98133980Sgibbs 0x20, "aui" 99133980Sgibbs }, 10019410Sguido/* dummy */ 101133980Sgibbs { 102133980Sgibbs 0, "???" 103133980Sgibbs }, 10419410Sguido#define CONNECTOR_BNC 3 105133980Sgibbs { 106133980Sgibbs 0x10, "bnc" 107133980Sgibbs }, 10819410Sguido#define CONNECTOR_TX 4 109133980Sgibbs { 110133980Sgibbs 0x02, "tx" 111133980Sgibbs }, 11219410Sguido#define CONNECTOR_FX 5 113133980Sgibbs { 114133980Sgibbs 0x04, "fx" 115133980Sgibbs }, 11619410Sguido#define CONNECTOR_MII 6 117133980Sgibbs { 118133980Sgibbs 0x40, "mii" 119133980Sgibbs }, 120133980Sgibbs { 121133980Sgibbs 0, "???" 122133980Sgibbs } 12319410Sguido}; 12419410Sguido 125151014Sjhbstatic void vx_txstat(struct vx_softc *); 126151014Sjhbstatic int vx_status(struct vx_softc *); 127151014Sjhbstatic void vx_init(void *); 128151014Sjhbstatic void vx_init_locked(struct vx_softc *); 129151014Sjhbstatic int vx_ioctl(struct ifnet *, u_long, caddr_t); 130151014Sjhbstatic void vx_start(struct ifnet *); 131151014Sjhbstatic void vx_start_locked(struct ifnet *); 132199559Sjhbstatic void vx_watchdog(void *); 133151014Sjhbstatic void vx_reset(struct vx_softc *); 134151014Sjhbstatic void vx_read(struct vx_softc *); 135151014Sjhbstatic struct mbuf *vx_get(struct vx_softc *, u_int); 136151014Sjhbstatic void vx_mbuf_fill(void *); 137151014Sjhbstatic void vx_mbuf_empty(struct vx_softc *); 138151014Sjhbstatic void vx_setfilter(struct vx_softc *); 139151014Sjhbstatic void vx_getlink(struct vx_softc *); 140151014Sjhbstatic void vx_setlink(struct vx_softc *); 14119410Sguido 14219410Sguidoint 143151014Sjhbvx_attach(device_t dev) 14419410Sguido{ 145133980Sgibbs struct vx_softc *sc = device_get_softc(dev); 146147256Sbrooks struct ifnet *ifp; 147133980Sgibbs int i; 148147256Sbrooks u_char eaddr[6]; 14919410Sguido 150151014Sjhb ifp = sc->vx_ifp = if_alloc(IFT_ETHER); 151147256Sbrooks if (ifp == NULL) { 152147256Sbrooks device_printf(dev, "can not if_alloc()\n"); 153147256Sbrooks return 0; 154147256Sbrooks } 155151014Sjhb if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 156147256Sbrooks 157151014Sjhb mtx_init(&sc->vx_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 158151014Sjhb MTX_DEF); 159151014Sjhb callout_init_mtx(&sc->vx_callout, &sc->vx_mtx, 0); 160199559Sjhb callout_init_mtx(&sc->vx_watchdog, &sc->vx_mtx, 0); 161133980Sgibbs GO_WINDOW(0); 162133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, GLOBAL_RESET); 163133980Sgibbs VX_BUSY_WAIT; 16419410Sguido 165151014Sjhb vx_getlink(sc); 16619410Sguido 167133980Sgibbs /* 168133980Sgibbs * Read the station address from the eeprom 169133980Sgibbs */ 170133980Sgibbs GO_WINDOW(0); 171133980Sgibbs for (i = 0; i < 3; i++) { 172133980Sgibbs int x; 17319410Sguido 174151014Sjhb if (vx_busy_eeprom(sc)) { 175151014Sjhb mtx_destroy(&sc->vx_mtx); 176151014Sjhb if_free(ifp); 177133980Sgibbs return 0; 178151014Sjhb } 179133980Sgibbs CSR_WRITE_2(sc, VX_W0_EEPROM_COMMAND, EEPROM_CMD_RD 180133980Sgibbs | (EEPROM_OEM_ADDR0 + i)); 181151014Sjhb if (vx_busy_eeprom(sc)) { 182151014Sjhb mtx_destroy(&sc->vx_mtx); 183151014Sjhb if_free(ifp); 184133980Sgibbs return 0; 185151014Sjhb } 186133980Sgibbs x = CSR_READ_2(sc, VX_W0_EEPROM_DATA); 187147256Sbrooks eaddr[(i << 1)] = x >> 8; 188147256Sbrooks eaddr[(i << 1) + 1] = x; 189133980Sgibbs } 19019410Sguido 191207554Ssobomax ifp->if_snd.ifq_maxlen = ifqmaxlen; 192151014Sjhb ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 193151014Sjhb ifp->if_start = vx_start; 194151014Sjhb ifp->if_ioctl = vx_ioctl; 195151014Sjhb ifp->if_init = vx_init; 196133980Sgibbs ifp->if_softc = sc; 19719410Sguido 198147256Sbrooks ether_ifattach(ifp, eaddr); 19919410Sguido 200151014Sjhb sc->vx_tx_start_thresh = 20; /* probably a good starting point. */ 20119410Sguido 202178469Smarius VX_LOCK(sc); 203151014Sjhb vx_stop(sc); 204178469Smarius VX_UNLOCK(sc); 205133980Sgibbs 206133980Sgibbs return 1; 20719410Sguido} 20819410Sguido 20919410Sguido/* 21019410Sguido * The order in here seems important. Otherwise we may not receive 21119410Sguido * interrupts. ?! 21219410Sguido */ 21319410Sguidostatic void 214151014Sjhbvx_init(void *xsc) 21519410Sguido{ 216133980Sgibbs struct vx_softc *sc = (struct vx_softc *)xsc; 217151014Sjhb 218151014Sjhb VX_LOCK(sc); 219151014Sjhb vx_init_locked(sc); 220151014Sjhb VX_UNLOCK(sc); 221151014Sjhb} 222151014Sjhb 223151014Sjhbstatic void 224151014Sjhbvx_init_locked(struct vx_softc *sc) 225151014Sjhb{ 226151014Sjhb struct ifnet *ifp = sc->vx_ifp; 227133980Sgibbs int i; 22819410Sguido 229151014Sjhb VX_LOCK_ASSERT(sc); 230151014Sjhb 231133980Sgibbs VX_BUSY_WAIT; 23219410Sguido 233133980Sgibbs GO_WINDOW(2); 23419410Sguido 235133980Sgibbs for (i = 0; i < 6; i++) /* Reload the ether_addr. */ 236152315Sru CSR_WRITE_1(sc, VX_W2_ADDR_0 + i, IF_LLADDR(sc->vx_ifp)[i]); 23719410Sguido 238133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_RESET); 239133980Sgibbs VX_BUSY_WAIT; 240133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, TX_RESET); 241133980Sgibbs VX_BUSY_WAIT; 24219410Sguido 243133980Sgibbs GO_WINDOW(1); /* Window 1 is operating window */ 244133980Sgibbs for (i = 0; i < 31; i++) 245133980Sgibbs CSR_READ_1(sc, VX_W1_TX_STATUS); 24619410Sguido 247133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | 248133980Sgibbs S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); 249133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | 250133980Sgibbs S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); 25119410Sguido 252133980Sgibbs /* 253133980Sgibbs * Attempt to get rid of any stray interrupts that occured during 254133980Sgibbs * configuration. On the i386 this isn't possible because one may 255133980Sgibbs * already be queued. However, a single stray interrupt is 256133980Sgibbs * unimportant. 257133980Sgibbs */ 258133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, ACK_INTR | 0xff); 25919410Sguido 260151014Sjhb vx_setfilter(sc); 261151014Sjhb vx_setlink(sc); 26219410Sguido 263133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_ENABLE); 264133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, TX_ENABLE); 26519410Sguido 266151014Sjhb vx_mbuf_fill(sc); 26719410Sguido 268133980Sgibbs /* Interface is now `running', with no output active. */ 269148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 270148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 271199559Sjhb callout_reset(&sc->vx_watchdog, hz, vx_watchdog, sc); 27219410Sguido 273133980Sgibbs /* Attempt to start output, if any. */ 274151014Sjhb vx_start_locked(ifp); 27519410Sguido} 27619410Sguido 27719410Sguidostatic void 278151014Sjhbvx_setfilter(struct vx_softc *sc) 27919410Sguido{ 280151014Sjhb struct ifnet *ifp = sc->vx_ifp; 28119410Sguido 282151014Sjhb VX_LOCK_ASSERT(sc); 283133980Sgibbs GO_WINDOW(1); /* Window 1 is operating window */ 284133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_RX_FILTER | 285133980Sgibbs FIL_INDIVIDUAL | FIL_BRDCST | FIL_MULTICAST | 286133980Sgibbs ((ifp->if_flags & IFF_PROMISC) ? FIL_PROMISC : 0)); 287133980Sgibbs} 288133980Sgibbs 289133980Sgibbsstatic void 290151014Sjhbvx_getlink(struct vx_softc *sc) 29119410Sguido{ 292133980Sgibbs int n, k; 29319410Sguido 294133980Sgibbs GO_WINDOW(3); 295133980Sgibbs sc->vx_connectors = CSR_READ_2(sc, VX_W3_RESET_OPT) & 0x7f; 296133980Sgibbs for (n = 0, k = 0; k < VX_CONNECTORS; k++) { 297133980Sgibbs if (sc->vx_connectors & conn_tab[k].bit) { 298133980Sgibbs if (n > 0) 299133980Sgibbs printf("/"); 300133980Sgibbs printf("%s", conn_tab[k].name); 301133980Sgibbs n++; 302133980Sgibbs } 30319410Sguido } 304133980Sgibbs if (sc->vx_connectors == 0) { 305178469Smarius printf("no connectors!\n"); 306133980Sgibbs return; 307133980Sgibbs } 308133980Sgibbs GO_WINDOW(3); 309133980Sgibbs sc->vx_connector = 310133980Sgibbs (CSR_READ_4(sc, VX_W3_INTERNAL_CFG) & INTERNAL_CONNECTOR_MASK) 311133980Sgibbs >> INTERNAL_CONNECTOR_BITS; 312133980Sgibbs if (sc->vx_connector & 0x10) { 313133980Sgibbs sc->vx_connector &= 0x0f; 314133980Sgibbs printf("[*%s*]", conn_tab[(int)sc->vx_connector].name); 315178469Smarius printf(": disable 'auto select' with DOS util!\n"); 316133980Sgibbs } else { 317178469Smarius printf("[*%s*]\n", conn_tab[(int)sc->vx_connector].name); 318133980Sgibbs } 31919410Sguido} 32019410Sguido 321133980Sgibbsstatic void 322151014Sjhbvx_setlink(struct vx_softc *sc) 323133980Sgibbs{ 324151014Sjhb struct ifnet *ifp = sc->vx_ifp; 325133980Sgibbs int i, j, k; 326133980Sgibbs char *reason, *warning; 327133980Sgibbs static int prev_flags; 328141166Sgrehan static signed char prev_conn = -1; 32919410Sguido 330151014Sjhb VX_LOCK_ASSERT(sc); 331133980Sgibbs if (prev_conn == -1) 332133980Sgibbs prev_conn = sc->vx_connector; 33320096Sguido 334133980Sgibbs /* 335133980Sgibbs * S.B. 336133980Sgibbs * 337133980Sgibbs * Now behavior was slightly changed: 338133980Sgibbs * 339133980Sgibbs * if any of flags link[0-2] is used and its connector is 340133980Sgibbs * physically present the following connectors are used: 341133980Sgibbs * 342133980Sgibbs * link0 - AUI * highest precedence 343133980Sgibbs * link1 - BNC 344133980Sgibbs * link2 - UTP * lowest precedence 345133980Sgibbs * 346133980Sgibbs * If none of them is specified then 347133980Sgibbs * connector specified in the EEPROM is used 348133980Sgibbs * (if present on card or UTP if not). 349133980Sgibbs */ 350133980Sgibbs i = sc->vx_connector; /* default in EEPROM */ 351133980Sgibbs reason = "default"; 352133980Sgibbs warning = 0; 35320096Sguido 354133980Sgibbs if (ifp->if_flags & IFF_LINK0) { 355133980Sgibbs if (sc->vx_connectors & conn_tab[CONNECTOR_AUI].bit) { 356133980Sgibbs i = CONNECTOR_AUI; 357133980Sgibbs reason = "link0"; 358133980Sgibbs } else { 359133980Sgibbs warning = "aui not present! (link0)"; 360133980Sgibbs } 361133980Sgibbs } else if (ifp->if_flags & IFF_LINK1) { 362133980Sgibbs if (sc->vx_connectors & conn_tab[CONNECTOR_BNC].bit) { 363133980Sgibbs i = CONNECTOR_BNC; 364133980Sgibbs reason = "link1"; 365133980Sgibbs } else { 366133980Sgibbs warning = "bnc not present! (link1)"; 367133980Sgibbs } 368133980Sgibbs } else if (ifp->if_flags & IFF_LINK2) { 369133980Sgibbs if (sc->vx_connectors & conn_tab[CONNECTOR_UTP].bit) { 370133980Sgibbs i = CONNECTOR_UTP; 371133980Sgibbs reason = "link2"; 372133980Sgibbs } else { 373133980Sgibbs warning = "utp not present! (link2)"; 374133980Sgibbs } 375133980Sgibbs } else if ((sc->vx_connectors & conn_tab[(int)sc->vx_connector].bit) == 0) { 376133980Sgibbs warning = "strange connector type in EEPROM."; 377133980Sgibbs reason = "forced"; 378133980Sgibbs i = CONNECTOR_UTP; 37920096Sguido } 380133980Sgibbs /* Avoid unnecessary message. */ 381133980Sgibbs k = (prev_flags ^ ifp->if_flags) & (IFF_LINK0 | IFF_LINK1 | IFF_LINK2); 382133980Sgibbs if ((k != 0) || (prev_conn != i)) { 383151014Sjhb if (warning != NULL) 384151014Sjhb if_printf(ifp, "warning: %s\n", warning); 385151014Sjhb if_printf(ifp, "selected %s. (%s)\n", conn_tab[i].name, reason); 38620096Sguido } 387133980Sgibbs /* Set the selected connector. */ 388133980Sgibbs GO_WINDOW(3); 389133980Sgibbs j = CSR_READ_4(sc, VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK; 390133980Sgibbs CSR_WRITE_4(sc, VX_W3_INTERNAL_CFG, j | (i << INTERNAL_CONNECTOR_BITS)); 39120096Sguido 392133980Sgibbs /* First, disable all. */ 393133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, STOP_TRANSCEIVER); 39420096Sguido DELAY(800); 39520096Sguido GO_WINDOW(4); 396133980Sgibbs CSR_WRITE_2(sc, VX_W4_MEDIA_TYPE, 0); 39720096Sguido 398133980Sgibbs /* Second, enable the selected one. */ 399133980Sgibbs switch (i) { 400133980Sgibbs case CONNECTOR_UTP: 401133980Sgibbs GO_WINDOW(4); 402133980Sgibbs CSR_WRITE_2(sc, VX_W4_MEDIA_TYPE, ENABLE_UTP); 403133980Sgibbs break; 404133980Sgibbs case CONNECTOR_BNC: 405133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, START_TRANSCEIVER); 406133980Sgibbs DELAY(800); 407133980Sgibbs break; 408133980Sgibbs case CONNECTOR_TX: 409133980Sgibbs case CONNECTOR_FX: 410133980Sgibbs GO_WINDOW(4); 411133980Sgibbs CSR_WRITE_2(sc, VX_W4_MEDIA_TYPE, LINKBEAT_ENABLE); 412133980Sgibbs break; 413133980Sgibbs default: /* AUI and MII fall here */ 414133980Sgibbs break; 415133980Sgibbs } 416133980Sgibbs GO_WINDOW(1); 417133980Sgibbs 418133980Sgibbs prev_flags = ifp->if_flags; 419133980Sgibbs prev_conn = i; 42019410Sguido} 42119410Sguido 42219410Sguidostatic void 423151014Sjhbvx_start(struct ifnet *ifp) 42419410Sguido{ 425151014Sjhb struct vx_softc *sc = ifp->if_softc; 42619410Sguido 427151014Sjhb VX_LOCK(sc); 428151014Sjhb vx_start_locked(ifp); 429151014Sjhb VX_UNLOCK(sc); 430151014Sjhb} 431151014Sjhb 432151014Sjhbstatic void 433151014Sjhbvx_start_locked(struct ifnet *ifp) 434151014Sjhb{ 435151014Sjhb struct vx_softc *sc = ifp->if_softc; 436151014Sjhb struct mbuf *m; 437151014Sjhb int len, pad; 438151014Sjhb 439151014Sjhb VX_LOCK_ASSERT(sc); 440151014Sjhb 441133980Sgibbs /* Don't transmit if interface is busy or not running */ 442151014Sjhb if ((sc->vx_ifp->if_drv_flags & 443148887Srwatson (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) 444133980Sgibbs return; 44519410Sguido 44619410Sguidostartagain: 447133980Sgibbs /* Sneak a peek at the next packet */ 448133980Sgibbs m = ifp->if_snd.ifq_head; 449133980Sgibbs if (m == NULL) { 450133980Sgibbs return; 451133980Sgibbs } 452133980Sgibbs /* We need to use m->m_pkthdr.len, so require the header */ 453133980Sgibbs M_ASSERTPKTHDR(m); 454133980Sgibbs len = m->m_pkthdr.len; 45519410Sguido 456133980Sgibbs pad = (4 - len) & 3; 45719410Sguido 458133980Sgibbs /* 459133980Sgibbs * The 3c509 automatically pads short packets to minimum ethernet 460133980Sgibbs * length, but we drop packets that are too large. Perhaps we should 461133980Sgibbs * truncate them instead? 462133980Sgibbs */ 463133980Sgibbs if (len + pad > ETHER_MAX_LEN) { 464133980Sgibbs /* packet is obviously too large: toss it */ 465133980Sgibbs ++ifp->if_oerrors; 466133980Sgibbs IF_DEQUEUE(&ifp->if_snd, m); 467133980Sgibbs m_freem(m); 468133980Sgibbs goto readcheck; 469133980Sgibbs } 470133980Sgibbs VX_BUSY_WAIT; 471133980Sgibbs if (CSR_READ_2(sc, VX_W1_FREE_TX) < len + pad + 4) { 472133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, 473133980Sgibbs SET_TX_AVAIL_THRESH | ((len + pad + 4) >> 2)); 474133980Sgibbs /* not enough room in FIFO - make sure */ 475133980Sgibbs if (CSR_READ_2(sc, VX_W1_FREE_TX) < len + pad + 4) { 476148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 477199559Sjhb sc->vx_timer = 1; 478133980Sgibbs return; 479133980Sgibbs } 480133980Sgibbs } 481133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_TX_AVAIL_THRESH | (8188 >> 2)); 48290227Sdillon IF_DEQUEUE(&ifp->if_snd, m); 483133980Sgibbs if (m == NULL) /* not really needed */ 484133980Sgibbs return; 48519410Sguido 486133980Sgibbs VX_BUSY_WAIT; 487133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_TX_START_THRESH | 488151014Sjhb ((len / 4 + sc->vx_tx_start_thresh) >> 2)); 48919410Sguido 490151014Sjhb BPF_MTAP(sc->vx_ifp, m); 49119410Sguido 492133980Sgibbs /* 493133980Sgibbs * Do the output at splhigh() so that an interrupt from another device 494133980Sgibbs * won't cause a FIFO underrun. 495151014Sjhb * 496151014Sjhb * XXX: Can't enforce that anymore. 497133980Sgibbs */ 49819410Sguido 499133980Sgibbs CSR_WRITE_4(sc, VX_W1_TX_PIO_WR_1, len | TX_INDICATE); 50019410Sguido 501133980Sgibbs while (m) { 502133980Sgibbs if (m->m_len > 3) 503151014Sjhb bus_space_write_multi_4(sc->vx_bst, sc->vx_bsh, 504133980Sgibbs VX_W1_TX_PIO_WR_1, (u_int32_t *)mtod(m, caddr_t), 505133980Sgibbs m->m_len / 4); 506133980Sgibbs if (m->m_len & 3) 507151014Sjhb bus_space_write_multi_1(sc->vx_bst, sc->vx_bsh, 508133980Sgibbs VX_W1_TX_PIO_WR_1, 509133980Sgibbs mtod(m, caddr_t) + (m->m_len & ~3), m->m_len & 3); 510133980Sgibbs m = m_free(m); 511133980Sgibbs } 512133980Sgibbs while (pad--) 513133980Sgibbs CSR_WRITE_1(sc, VX_W1_TX_PIO_WR_1, 0); /* Padding */ 51419410Sguido 515133980Sgibbs ++ifp->if_opackets; 516199559Sjhb sc->vx_timer = 1; 51719410Sguido 51819410Sguidoreadcheck: 519133980Sgibbs if ((CSR_READ_2(sc, VX_W1_RX_STATUS) & ERR_INCOMPLETE) == 0) { 520133980Sgibbs /* We received a complete packet. */ 521133980Sgibbs 522133980Sgibbs if ((CSR_READ_2(sc, VX_STATUS) & S_INTR_LATCH) == 0) { 523133980Sgibbs /* 524133980Sgibbs * No interrupt, read the packet and continue 525133980Sgibbs * Is this supposed to happen? Is my motherboard 526133980Sgibbs * completely busted? 527133980Sgibbs */ 528151014Sjhb vx_read(sc); 529133980Sgibbs } else 530133980Sgibbs /* 531133980Sgibbs * Got an interrupt, return so that it gets 532133980Sgibbs * serviced. 533133980Sgibbs */ 534133980Sgibbs return; 535133980Sgibbs } else { 536133980Sgibbs /* Check if we are stuck and reset [see XXX comment] */ 537151014Sjhb if (vx_status(sc)) { 538133980Sgibbs if (ifp->if_flags & IFF_DEBUG) 539133980Sgibbs if_printf(ifp, "adapter reset\n"); 540151014Sjhb vx_reset(sc); 541133980Sgibbs } 54219410Sguido } 54319410Sguido 544133980Sgibbs goto startagain; 54519410Sguido} 54619410Sguido 54719410Sguido/* 54819410Sguido * XXX: The 3c509 card can get in a mode where both the fifo status bit 54919410Sguido * FIFOS_RX_OVERRUN and the status bit ERR_INCOMPLETE are set 55019410Sguido * We detect this situation and we reset the adapter. 55119410Sguido * It happens at times when there is a lot of broadcast traffic 55219410Sguido * on the cable (once in a blue moon). 55319410Sguido */ 55419410Sguidostatic int 555151014Sjhbvx_status(struct vx_softc *sc) 55619410Sguido{ 557151014Sjhb struct ifnet *ifp; 558133980Sgibbs int fifost; 55919410Sguido 560151014Sjhb VX_LOCK_ASSERT(sc); 561151014Sjhb 562133980Sgibbs /* 563133980Sgibbs * Check the FIFO status and act accordingly 564133980Sgibbs */ 565133980Sgibbs GO_WINDOW(4); 566133980Sgibbs fifost = CSR_READ_2(sc, VX_W4_FIFO_DIAG); 567133980Sgibbs GO_WINDOW(1); 56819410Sguido 569151014Sjhb ifp = sc->vx_ifp; 570133980Sgibbs if (fifost & FIFOS_RX_UNDERRUN) { 571151014Sjhb if (ifp->if_flags & IFF_DEBUG) 572151014Sjhb if_printf(ifp, "RX underrun\n"); 573151014Sjhb vx_reset(sc); 574133980Sgibbs return 0; 575133980Sgibbs } 576133980Sgibbs if (fifost & FIFOS_RX_STATUS_OVERRUN) { 577151014Sjhb if (ifp->if_flags & IFF_DEBUG) 578151014Sjhb if_printf(ifp, "RX Status overrun\n"); 579133980Sgibbs return 1; 580133980Sgibbs } 581133980Sgibbs if (fifost & FIFOS_RX_OVERRUN) { 582151014Sjhb if (ifp->if_flags & IFF_DEBUG) 583151014Sjhb if_printf(ifp, "RX overrun\n"); 584133980Sgibbs return 1; 585133980Sgibbs } 586133980Sgibbs if (fifost & FIFOS_TX_OVERRUN) { 587151014Sjhb if (ifp->if_flags & IFF_DEBUG) 588151014Sjhb if_printf(ifp, "TX overrun\n"); 589151014Sjhb vx_reset(sc); 590133980Sgibbs return 0; 591133980Sgibbs } 59219410Sguido return 0; 59319410Sguido} 59419410Sguido 595133980Sgibbsstatic void 596151014Sjhbvx_txstat(struct vx_softc *sc) 59719410Sguido{ 598151014Sjhb struct ifnet *ifp; 599133980Sgibbs int i; 60019410Sguido 601151014Sjhb VX_LOCK_ASSERT(sc); 602151014Sjhb 603133980Sgibbs /* 604133980Sgibbs * We need to read+write TX_STATUS until we get a 0 status 605133980Sgibbs * in order to turn off the interrupt flag. 606133980Sgibbs */ 607151014Sjhb ifp = sc->vx_ifp; 608133980Sgibbs while ((i = CSR_READ_1(sc, VX_W1_TX_STATUS)) & TXS_COMPLETE) { 609133980Sgibbs CSR_WRITE_1(sc, VX_W1_TX_STATUS, 0x0); 61019410Sguido 611133980Sgibbs if (i & TXS_JABBER) { 612151014Sjhb ++ifp->if_oerrors; 613151014Sjhb if (ifp->if_flags & IFF_DEBUG) 614151014Sjhb if_printf(ifp, "jabber (%x)\n", i); 615151014Sjhb vx_reset(sc); 616133980Sgibbs } else if (i & TXS_UNDERRUN) { 617151014Sjhb ++ifp->if_oerrors; 618151014Sjhb if (ifp->if_flags & IFF_DEBUG) 619151014Sjhb if_printf(ifp, "fifo underrun (%x) @%d\n", i, 620151014Sjhb sc->vx_tx_start_thresh); 621151014Sjhb if (sc->vx_tx_succ_ok < 100) 622151014Sjhb sc->vx_tx_start_thresh = 623151014Sjhb min(ETHER_MAX_LEN, 624151014Sjhb sc->vx_tx_start_thresh + 20); 625151014Sjhb sc->vx_tx_succ_ok = 0; 626151014Sjhb vx_reset(sc); 627133980Sgibbs } else if (i & TXS_MAX_COLLISION) { 628151014Sjhb ++ifp->if_collisions; 629133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, TX_ENABLE); 630151014Sjhb ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 631133980Sgibbs } else 632151014Sjhb sc->vx_tx_succ_ok = (sc->vx_tx_succ_ok + 1) & 127; 633133980Sgibbs } 63419410Sguido} 63519410Sguido 63619410Sguidovoid 637151014Sjhbvx_intr(void *voidsc) 63819410Sguido{ 639151014Sjhb short status; 640133980Sgibbs struct vx_softc *sc = voidsc; 641151014Sjhb struct ifnet *ifp = sc->vx_ifp; 64219410Sguido 643151014Sjhb VX_LOCK(sc); 644133980Sgibbs for (;;) { 645133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, C_INTR_LATCH); 64619410Sguido 647133980Sgibbs status = CSR_READ_2(sc, VX_STATUS); 64819410Sguido 649133980Sgibbs if ((status & (S_TX_COMPLETE | S_TX_AVAIL | 650133980Sgibbs S_RX_COMPLETE | S_CARD_FAILURE)) == 0) 651133980Sgibbs break; 65219410Sguido 653133980Sgibbs /* 654133980Sgibbs * Acknowledge any interrupts. It's important that we do this 655133980Sgibbs * first, since there would otherwise be a race condition. 656133980Sgibbs * Due to the i386 interrupt queueing, we may get spurious 657133980Sgibbs * interrupts occasionally. 658133980Sgibbs */ 659133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, ACK_INTR | status); 66019410Sguido 661133980Sgibbs if (status & S_RX_COMPLETE) 662151014Sjhb vx_read(sc); 663133980Sgibbs if (status & S_TX_AVAIL) { 664199559Sjhb sc->vx_timer = 0; 665151014Sjhb sc->vx_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 666151014Sjhb vx_start_locked(sc->vx_ifp); 667133980Sgibbs } 668133980Sgibbs if (status & S_CARD_FAILURE) { 669151014Sjhb if_printf(ifp, "adapter failure (%x)\n", status); 670199559Sjhb sc->vx_timer = 0; 671151014Sjhb vx_reset(sc); 672151014Sjhb break; 673133980Sgibbs } 674133980Sgibbs if (status & S_TX_COMPLETE) { 675199559Sjhb sc->vx_timer = 0; 676151014Sjhb vx_txstat(sc); 677151014Sjhb vx_start_locked(ifp); 678133980Sgibbs } 67919410Sguido } 680151014Sjhb VX_UNLOCK(sc); 68119410Sguido 682133980Sgibbs /* no more interrupts */ 683133980Sgibbs return; 68419410Sguido} 68519410Sguido 68619410Sguidostatic void 687151014Sjhbvx_read(struct vx_softc *sc) 68819410Sguido{ 689151014Sjhb struct ifnet *ifp = sc->vx_ifp; 690133980Sgibbs struct mbuf *m; 691133980Sgibbs struct ether_header *eh; 692133980Sgibbs u_int len; 69319410Sguido 694151014Sjhb VX_LOCK_ASSERT(sc); 695133980Sgibbs len = CSR_READ_2(sc, VX_W1_RX_STATUS); 69619410Sguidoagain: 69719410Sguido 698133980Sgibbs if (ifp->if_flags & IFF_DEBUG) { 699133980Sgibbs int err = len & ERR_MASK; 700133980Sgibbs char *s = NULL; 70119410Sguido 702133980Sgibbs if (len & ERR_INCOMPLETE) 703133980Sgibbs s = "incomplete packet"; 704133980Sgibbs else if (err == ERR_OVERRUN) 705133980Sgibbs s = "packet overrun"; 706133980Sgibbs else if (err == ERR_RUNT) 707133980Sgibbs s = "runt packet"; 708133980Sgibbs else if (err == ERR_ALIGNMENT) 709133980Sgibbs s = "bad alignment"; 710133980Sgibbs else if (err == ERR_CRC) 711133980Sgibbs s = "bad crc"; 712133980Sgibbs else if (err == ERR_OVERSIZE) 713133980Sgibbs s = "oversized packet"; 714133980Sgibbs else if (err == ERR_DRIBBLE) 715133980Sgibbs s = "dribble bits"; 716133980Sgibbs 717133980Sgibbs if (s) 718151014Sjhb if_printf(ifp, "%s\n", s); 719133980Sgibbs } 72019410Sguido if (len & ERR_INCOMPLETE) 721133980Sgibbs return; 72219410Sguido 723133980Sgibbs if (len & ERR_RX) { 724133980Sgibbs ++ifp->if_ierrors; 725133980Sgibbs goto abort; 726133980Sgibbs } 727133980Sgibbs len &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */ 72819410Sguido 729133980Sgibbs /* Pull packet off interface. */ 730151014Sjhb m = vx_get(sc, len); 731133980Sgibbs if (m == 0) { 73269732Swpaul ifp->if_ierrors++; 73369732Swpaul goto abort; 73469732Swpaul } 735133980Sgibbs ++ifp->if_ipackets; 73669732Swpaul 737133980Sgibbs { 738133980Sgibbs struct mbuf *m0; 73969732Swpaul 740151014Sjhb m0 = m_devget(mtod(m, char *), m->m_pkthdr.len, ETHER_ALIGN, 741151014Sjhb ifp, NULL); 742133980Sgibbs if (m0 == NULL) { 743133980Sgibbs ifp->if_ierrors++; 744133980Sgibbs goto abort; 745133980Sgibbs } 746133980Sgibbs m_freem(m); 747133980Sgibbs m = m0; 748133980Sgibbs } 74919410Sguido 750133980Sgibbs /* We assume the header fit entirely in one mbuf. */ 751133980Sgibbs eh = mtod(m, struct ether_header *); 75222062Sphk 753133980Sgibbs /* 754133980Sgibbs * XXX: Some cards seem to be in promiscous mode all the time. 755133980Sgibbs * we need to make sure we only get our own stuff always. 756133980Sgibbs * bleah! 757133980Sgibbs */ 75860536Sarchie 759148284Sru if (!(ifp->if_flags & IFF_PROMISC) 760148284Sru && (eh->ether_dhost[0] & 1) == 0 /* !mcast and !bcast */ 761152315Sru && bcmp(eh->ether_dhost, IF_LLADDR(sc->vx_ifp), 762151014Sjhb ETHER_ADDR_LEN) != 0) { 763133980Sgibbs m_freem(m); 764133980Sgibbs return; 765133980Sgibbs } 766151014Sjhb VX_UNLOCK(sc); 767151014Sjhb (*ifp->if_input)(ifp, m); 768151014Sjhb VX_LOCK(sc); 76919410Sguido 770133980Sgibbs /* 771133980Sgibbs * In periods of high traffic we can actually receive enough 772133980Sgibbs * packets so that the fifo overrun bit will be set at this point, 773133980Sgibbs * even though we just read a packet. In this case we 774133980Sgibbs * are not going to receive any more interrupts. We check for 775133980Sgibbs * this condition and read again until the fifo is not full. 776151014Sjhb * We could simplify this test by not using vx_status(), but 777133980Sgibbs * rechecking the RX_STATUS register directly. This test could 778133980Sgibbs * result in unnecessary looping in cases where there is a new 779133980Sgibbs * packet but the fifo is not full, but it will not fix the 780133980Sgibbs * stuck behavior. 781133980Sgibbs * 782133980Sgibbs * Even with this improvement, we still get packet overrun errors 783133980Sgibbs * which are hurting performance. Maybe when I get some more time 784151014Sjhb * I'll modify vx_read() so that it can handle RX_EARLY interrupts. 785133980Sgibbs */ 786151014Sjhb if (vx_status(sc)) { 787133980Sgibbs len = CSR_READ_2(sc, VX_W1_RX_STATUS); 788133980Sgibbs /* Check if we are stuck and reset [see XXX comment] */ 789133980Sgibbs if (len & ERR_INCOMPLETE) { 790133980Sgibbs if (ifp->if_flags & IFF_DEBUG) 791151014Sjhb if_printf(ifp, "adapter reset\n"); 792151014Sjhb vx_reset(sc); 793133980Sgibbs return; 794133980Sgibbs } 795133980Sgibbs goto again; 79619410Sguido } 797133980Sgibbs return; 79819410Sguido 79919410Sguidoabort: 800133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_DISCARD_TOP_PACK); 80119410Sguido} 80219410Sguido 80319410Sguidostatic struct mbuf * 804151014Sjhbvx_get(struct vx_softc *sc, u_int totlen) 80519410Sguido{ 806151014Sjhb struct ifnet *ifp = sc->vx_ifp; 807133980Sgibbs struct mbuf *top, **mp, *m; 808133980Sgibbs int len; 80919410Sguido 810151014Sjhb VX_LOCK_ASSERT(sc); 811151014Sjhb m = sc->vx_mb[sc->vx_next_mb]; 812151014Sjhb sc->vx_mb[sc->vx_next_mb] = NULL; 813151014Sjhb if (m == NULL) { 814243857Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 815151014Sjhb if (m == NULL) 816151014Sjhb return NULL; 817133980Sgibbs } else { 818133980Sgibbs /* If the queue is no longer full, refill. */ 819151014Sjhb if (sc->vx_last_mb == sc->vx_next_mb && 820151014Sjhb sc->vx_buffill_pending == 0) { 821151014Sjhb callout_reset(&sc->vx_callout, hz / 100, vx_mbuf_fill, 822151014Sjhb sc); 823151014Sjhb sc->vx_buffill_pending = 1; 824133980Sgibbs } 825133980Sgibbs /* Convert one of our saved mbuf's. */ 826151014Sjhb sc->vx_next_mb = (sc->vx_next_mb + 1) % MAX_MBS; 827133980Sgibbs m->m_data = m->m_pktdat; 828133980Sgibbs m->m_flags = M_PKTHDR; 829133980Sgibbs bzero(&m->m_pkthdr, sizeof(m->m_pkthdr)); 83029671Sgibbs } 831133980Sgibbs m->m_pkthdr.rcvif = ifp; 832133980Sgibbs m->m_pkthdr.len = totlen; 833133980Sgibbs len = MHLEN; 834151014Sjhb top = NULL; 835133980Sgibbs mp = ⊤ 83619410Sguido 837133980Sgibbs /* 838133980Sgibbs * We read the packet at splhigh() so that an interrupt from another 839133980Sgibbs * device doesn't cause the card's buffer to overflow while we're 840133980Sgibbs * reading it. We may still lose packets at other times. 841151014Sjhb * 842151014Sjhb * XXX: Can't enforce this anymore. 843133980Sgibbs */ 84419410Sguido 845133980Sgibbs /* 846133980Sgibbs * Since we don't set allowLargePackets bit in MacControl register, 847133980Sgibbs * we can assume that totlen <= 1500bytes. 848133980Sgibbs * The while loop will be performed iff we have a packet with 849133980Sgibbs * MLEN < m_len < MINCLSIZE. 850133980Sgibbs */ 851133980Sgibbs while (totlen > 0) { 852133980Sgibbs if (top) { 853151014Sjhb m = sc->vx_mb[sc->vx_next_mb]; 854151014Sjhb sc->vx_mb[sc->vx_next_mb] = NULL; 855151014Sjhb if (m == NULL) { 856243857Sglebius MGET(m, M_NOWAIT, MT_DATA); 857151014Sjhb if (m == NULL) { 858133980Sgibbs m_freem(top); 859151014Sjhb return NULL; 860133980Sgibbs } 861133980Sgibbs } else { 862151014Sjhb sc->vx_next_mb = (sc->vx_next_mb + 1) % MAX_MBS; 863133980Sgibbs } 864133980Sgibbs len = MLEN; 865133980Sgibbs } 866133980Sgibbs if (totlen >= MINCLSIZE) { 867243857Sglebius MCLGET(m, M_NOWAIT); 868133980Sgibbs if (m->m_flags & M_EXT) 869133980Sgibbs len = MCLBYTES; 870133980Sgibbs } 871133980Sgibbs len = min(totlen, len); 872133980Sgibbs if (len > 3) 873151014Sjhb bus_space_read_multi_4(sc->vx_bst, sc->vx_bsh, 874133980Sgibbs VX_W1_RX_PIO_RD_1, mtod(m, u_int32_t *), len / 4); 875133980Sgibbs if (len & 3) { 876151014Sjhb bus_space_read_multi_1(sc->vx_bst, sc->vx_bsh, 877133980Sgibbs VX_W1_RX_PIO_RD_1, mtod(m, u_int8_t *) + (len & ~3), 878133980Sgibbs len & 3); 879133980Sgibbs } 880133980Sgibbs m->m_len = len; 881133980Sgibbs totlen -= len; 882133980Sgibbs *mp = m; 883133980Sgibbs mp = &m->m_next; 88430022Sitojun } 88519410Sguido 886133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_DISCARD_TOP_PACK); 88719410Sguido 888133980Sgibbs return top; 88919410Sguido} 89019410Sguido 89119410Sguido 89219410Sguidostatic int 893151014Sjhbvx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 89419410Sguido{ 895133980Sgibbs struct vx_softc *sc = ifp->if_softc; 896133980Sgibbs struct ifreq *ifr = (struct ifreq *) data; 897151014Sjhb int error = 0; 89819410Sguido 899133980Sgibbs switch (cmd) { 900133980Sgibbs case SIOCSIFFLAGS: 901151014Sjhb VX_LOCK(sc); 902133980Sgibbs if ((ifp->if_flags & IFF_UP) == 0 && 903148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 904133980Sgibbs /* 905133980Sgibbs * If interface is marked up and it is stopped, then 906133980Sgibbs * start it. 907133980Sgibbs */ 908151014Sjhb vx_stop(sc); 909148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 910133980Sgibbs } else if ((ifp->if_flags & IFF_UP) != 0 && 911148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 912133980Sgibbs /* 913133980Sgibbs * If interface is marked up and it is stopped, then 914133980Sgibbs * start it. 915133980Sgibbs */ 916151014Sjhb vx_init_locked(sc); 917133980Sgibbs } else { 918133980Sgibbs /* 919133980Sgibbs * deal with flags changes: 920133980Sgibbs * IFF_MULTICAST, IFF_PROMISC, 921133980Sgibbs * IFF_LINK0, IFF_LINK1, 922133980Sgibbs */ 923151014Sjhb vx_setfilter(sc); 924151014Sjhb vx_setlink(sc); 925133980Sgibbs } 926151014Sjhb VX_UNLOCK(sc); 927133980Sgibbs break; 92819410Sguido 929133980Sgibbs case SIOCSIFMTU: 930133980Sgibbs /* 931133980Sgibbs * Set the interface MTU. 932133980Sgibbs */ 933151014Sjhb VX_LOCK(sc); 934133980Sgibbs if (ifr->ifr_mtu > ETHERMTU) { 935133980Sgibbs error = EINVAL; 936133980Sgibbs } else { 937133980Sgibbs ifp->if_mtu = ifr->ifr_mtu; 938133980Sgibbs } 939151014Sjhb VX_UNLOCK(sc); 940133980Sgibbs break; 94119410Sguido 942133980Sgibbs case SIOCADDMULTI: 943133980Sgibbs case SIOCDELMULTI: 944133980Sgibbs /* 945133980Sgibbs * Multicast list has changed; set the hardware filter 946133980Sgibbs * accordingly. 947133980Sgibbs */ 948151014Sjhb VX_LOCK(sc); 949151014Sjhb vx_reset(sc); 950151014Sjhb VX_UNLOCK(sc); 951133980Sgibbs error = 0; 952133980Sgibbs break; 95319410Sguido 95419410Sguido 955133980Sgibbs default: 956133980Sgibbs error = ether_ioctl(ifp, cmd, data); 957133980Sgibbs break; 958133980Sgibbs } 95919410Sguido 960133980Sgibbs return (error); 96119410Sguido} 96219410Sguido 96319410Sguidostatic void 964151014Sjhbvx_reset(struct vx_softc *sc) 96519410Sguido{ 96619410Sguido 967151014Sjhb VX_LOCK_ASSERT(sc); 968151014Sjhb vx_stop(sc); 969151014Sjhb vx_init_locked(sc); 97019410Sguido} 97119410Sguido 97219410Sguidostatic void 973199559Sjhbvx_watchdog(void *arg) 97419410Sguido{ 975199559Sjhb struct vx_softc *sc; 976199559Sjhb struct ifnet *ifp; 97719410Sguido 978199559Sjhb sc = arg; 979199559Sjhb VX_LOCK_ASSERT(sc); 980199559Sjhb callout_reset(&sc->vx_watchdog, hz, vx_watchdog, sc); 981199559Sjhb if (sc->vx_timer == 0 || --sc->vx_timer > 0) 982199559Sjhb return; 983199559Sjhb 984199559Sjhb ifp = sc->vx_ifp; 985133980Sgibbs if (ifp->if_flags & IFF_DEBUG) 986133980Sgibbs if_printf(ifp, "device timeout\n"); 987148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 988151014Sjhb vx_start_locked(ifp); 989151014Sjhb vx_intr(sc); 99019410Sguido} 99119410Sguido 99219410Sguidovoid 993151014Sjhbvx_stop(struct vx_softc *sc) 99419410Sguido{ 99519410Sguido 996151014Sjhb VX_LOCK_ASSERT(sc); 997199559Sjhb sc->vx_timer = 0; 998199559Sjhb callout_stop(&sc->vx_watchdog); 99919410Sguido 1000133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_DISABLE); 1001133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_DISCARD_TOP_PACK); 1002133980Sgibbs VX_BUSY_WAIT; 1003133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, TX_DISABLE); 1004133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, STOP_TRANSCEIVER); 1005133980Sgibbs DELAY(800); 1006133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_RESET); 1007133980Sgibbs VX_BUSY_WAIT; 1008133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, TX_RESET); 1009133980Sgibbs VX_BUSY_WAIT; 1010133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, C_INTR_LATCH); 1011133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_RD_0_MASK); 1012133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_INTR_MASK); 1013133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_RX_FILTER); 101419410Sguido 1015151014Sjhb vx_mbuf_empty(sc); 101619410Sguido} 101719410Sguido 101819410Sguidoint 1019151014Sjhbvx_busy_eeprom(struct vx_softc *sc) 102019410Sguido{ 1021133980Sgibbs int j, i = 100; 102219410Sguido 1023133980Sgibbs while (i--) { 1024133980Sgibbs j = CSR_READ_2(sc, VX_W0_EEPROM_COMMAND); 1025133980Sgibbs if (j & EEPROM_BUSY) 1026133980Sgibbs DELAY(100); 1027133980Sgibbs else 1028133980Sgibbs break; 1029133980Sgibbs } 1030133980Sgibbs if (!i) { 1031151014Sjhb if_printf(sc->vx_ifp, "eeprom failed to come ready\n"); 1032133980Sgibbs return (1); 1033133980Sgibbs } 1034133980Sgibbs return (0); 103519410Sguido} 103619410Sguido 103719410Sguidostatic void 1038151014Sjhbvx_mbuf_fill(void *sp) 103919410Sguido{ 1040133980Sgibbs struct vx_softc *sc = (struct vx_softc *)sp; 1041151014Sjhb int i; 104219410Sguido 1043151014Sjhb VX_LOCK_ASSERT(sc); 1044151014Sjhb i = sc->vx_last_mb; 1045133980Sgibbs do { 1046151014Sjhb if (sc->vx_mb[i] == NULL) 1047243857Sglebius MGET(sc->vx_mb[i], M_NOWAIT, MT_DATA); 1048151014Sjhb if (sc->vx_mb[i] == NULL) 1049133980Sgibbs break; 1050133980Sgibbs i = (i + 1) % MAX_MBS; 1051151014Sjhb } while (i != sc->vx_next_mb); 1052151014Sjhb sc->vx_last_mb = i; 1053133980Sgibbs /* If the queue was not filled, try again. */ 1054151014Sjhb if (sc->vx_last_mb != sc->vx_next_mb) { 1055151014Sjhb callout_reset(&sc->vx_callout, hz / 100, vx_mbuf_fill, sc); 1056151014Sjhb sc->vx_buffill_pending = 1; 1057133980Sgibbs } else { 1058151014Sjhb sc->vx_buffill_pending = 0; 1059133980Sgibbs } 106019410Sguido} 106119410Sguido 106219410Sguidostatic void 1063151014Sjhbvx_mbuf_empty(struct vx_softc *sc) 106419410Sguido{ 1065151014Sjhb int i; 106619410Sguido 1067151014Sjhb VX_LOCK_ASSERT(sc); 1068133980Sgibbs for (i = 0; i < MAX_MBS; i++) { 1069151014Sjhb if (sc->vx_mb[i]) { 1070151014Sjhb m_freem(sc->vx_mb[i]); 1071151014Sjhb sc->vx_mb[i] = NULL; 1072133980Sgibbs } 107319410Sguido } 1074151014Sjhb sc->vx_last_mb = sc->vx_next_mb = 0; 1075151014Sjhb if (sc->vx_buffill_pending != 0) 1076151014Sjhb callout_stop(&sc->vx_callout); 107719410Sguido} 1078