if_vx.c revision 29671
119410Sguido/* 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 * 3019410Sguido */ 3119410Sguido 3219410Sguido/* 3319410Sguido * Created from if_ep.c driver by Fred Gray (fgray@rice.edu) to support 3419410Sguido * the 3c590 family. 3519410Sguido */ 3619410Sguido 3719410Sguido/* 3819410Sguido * Modified from the FreeBSD 1.1.5.1 version by: 3919410Sguido * Andres Vega Garcia 4019410Sguido * INRIA - Sophia Antipolis, France 4119410Sguido * avega@sophia.inria.fr 4219410Sguido */ 4319410Sguido 4419410Sguido/* 4519410Sguido * Promiscuous mode added and interrupt logic slightly changed 4619410Sguido * to reduce the number of adapter failures. Transceiver select 4719410Sguido * logic changed to use value from EEPROM. Autoconfiguration 4819410Sguido * features added. 4919410Sguido * Done by: 5019410Sguido * Serge Babkin 5119410Sguido * Chelindbank (Chelyabinsk, Russia) 5219410Sguido * babkin@hq.icb.chel.su 5319410Sguido */ 5419410Sguido 5519410Sguido#include "vx.h" 5619410Sguido#if NVX > 0 5719410Sguido 5820503Sphk#if NVX < 4 /* These cost 4 bytes apiece, so give us 4 */ 5920503Sphk#undef NVX 6020503Sphk#define NVX 4 6120503Sphk#endif 6220503Sphk 6319410Sguido#include "bpfilter.h" 6419410Sguido 6519410Sguido#include <sys/param.h> 6619410Sguido#include <sys/systm.h> 6724204Sbde#include <sys/sockio.h> 6826640Sbde#include <sys/malloc.h> 6919410Sguido#include <sys/mbuf.h> 7019410Sguido#include <sys/socket.h> 7119410Sguido 7219410Sguido#include <net/if.h> 7319410Sguido 7419410Sguido#ifdef INET 7519410Sguido#include <netinet/in.h> 7619410Sguido#include <netinet/if_ether.h> 7719410Sguido#endif 7819410Sguido 7919410Sguido#ifdef NS 8019410Sguido#include <netns/ns.h> 8119410Sguido#include <netns/ns_if.h> 8219410Sguido#endif 8319410Sguido 8419410Sguido#if NBPFILTER > 0 8519410Sguido#include <net/bpf.h> 8619410Sguido#endif 8719410Sguido 8819410Sguido#include <machine/clock.h> 8919410Sguido 9019410Sguido#include <dev/vx/if_vxreg.h> 9119410Sguido 9219410Sguido#define ETHER_MAX_LEN 1518 9319410Sguido#define ETHER_ADDR_LEN 6 9419410Sguido 9519410Sguidostruct vx_softc *vx_softc[NVX]; 9619410Sguido 9719410Sguidou_long vx_count; /* both PCI and EISA */ 9819410Sguido 9919410Sguidostatic struct connector_entry { 10019410Sguido int bit; 10119410Sguido char *name; 10220096Sguido} conn_tab[VX_CONNECTORS] = { 10319410Sguido#define CONNECTOR_UTP 0 10419410Sguido { 0x08, "utp"}, 10519410Sguido#define CONNECTOR_AUI 1 10619410Sguido { 0x20, "aui"}, 10719410Sguido/* dummy */ 10819410Sguido { 0, "???"}, 10919410Sguido#define CONNECTOR_BNC 3 11019410Sguido { 0x10, "bnc"}, 11119410Sguido#define CONNECTOR_TX 4 11219410Sguido { 0x02, "tx"}, 11319410Sguido#define CONNECTOR_FX 5 11419410Sguido { 0x04, "fx"}, 11519410Sguido#define CONNECTOR_MII 6 11619410Sguido { 0x40, "mii"}, 11719410Sguido { 0, "???"} 11819410Sguido}; 11919410Sguido 12019410Sguido/* struct vx_softc *vxalloc __P((int)); */ 12119410Sguido/* void *vxfree __P((struct vx_softc *)); */ 12219410Sguido/* int vxattach __P((struct vx_softc *)); */ 12319410Sguidostatic void vxtxstat __P((struct vx_softc *)); 12419410Sguidostatic int vxstatus __P((struct vx_softc *)); 12519410Sguidostatic void vxinit __P((void *)); 12619410Sguidostatic int vxioctl __P((struct ifnet *, int, caddr_t)); 12719410Sguidostatic void vxstart __P((struct ifnet *ifp)); 12819410Sguidostatic void vxwatchdog __P((struct ifnet *)); 12919410Sguidostatic void vxreset __P((struct vx_softc *)); 13019410Sguido/* void vxstop __P((struct vx_softc *)); */ 13119410Sguidostatic void vxread __P((struct vx_softc *)); 13219410Sguidostatic struct mbuf *vxget __P((struct vx_softc *, u_int)); 13319410Sguidostatic void vxmbuffill __P((void *)); 13419410Sguidostatic void vxmbufempty __P((struct vx_softc *)); 13519410Sguidostatic void vxsetfilter __P((struct vx_softc *)); 13619410Sguidostatic void vxgetlink __P((struct vx_softc *)); 13719410Sguidostatic void vxsetlink __P((struct vx_softc *)); 13819410Sguido/* int vxbusyeeprom __P((struct vx_softc *)); */ 13919410Sguido/* void vxintr __P((void *)); */ 14019410Sguido 14119410Sguidostruct vx_softc * 14219410Sguidovxalloc(unit) 14319410Sguido int unit; 14419410Sguido{ 14519410Sguido struct vx_softc *sc; 14619410Sguido 14719410Sguido if (unit >= NVX) { 14819410Sguido printf("vx%d: unit number too high.\n", unit); 14919410Sguido return NULL; 15019410Sguido } 15119410Sguido 15219410Sguido if (vx_softc[unit]) { 15319410Sguido printf("vx%d: already allocated.\n", unit); 15419410Sguido return NULL; 15519410Sguido } 15619410Sguido 15719410Sguido sc = malloc(sizeof(struct vx_softc), M_DEVBUF, M_NOWAIT); 15819410Sguido if (sc == NULL) { 15919410Sguido printf("vx%d: cannot malloc.\n", unit); 16019410Sguido return NULL; 16119410Sguido } 16219410Sguido bzero(sc, sizeof(struct vx_softc)); 16329671Sgibbs callout_handle_init(&sc->ch); 16419410Sguido 16519410Sguido vx_softc[unit] = sc; 16619410Sguido sc->unit = unit; 16719410Sguido return (sc); 16819410Sguido} 16919410Sguido 17019410Sguidovoid 17119410Sguidovxfree(sc) 17219410Sguido struct vx_softc *sc; 17319410Sguido{ 17419410Sguido vx_softc[sc->unit] = NULL; 17519410Sguido free(sc, M_DEVBUF); 17619410Sguido return; 17719410Sguido} 17819410Sguido 17919410Sguidoint 18019410Sguidovxattach(sc) 18119410Sguido struct vx_softc *sc; 18219410Sguido{ 18319410Sguido struct ifnet *ifp = &sc->arpcom.ac_if; 18419410Sguido int i; 18519410Sguido 18619410Sguido GO_WINDOW(0); 18719410Sguido outw(VX_COMMAND, GLOBAL_RESET); 18819410Sguido VX_BUSY_WAIT; 18919410Sguido 19019410Sguido vxgetlink(sc); 19119410Sguido 19219410Sguido /* 19319410Sguido * Read the station address from the eeprom 19419410Sguido */ 19519410Sguido GO_WINDOW(0); 19619410Sguido for (i = 0; i < 3; i++) { 19719410Sguido int x; 19819410Sguido if (vxbusyeeprom(sc)) 19919410Sguido return 0; 20019410Sguido outw(BASE + VX_W0_EEPROM_COMMAND, EEPROM_CMD_RD 20119410Sguido | (EEPROM_OEM_ADDR_0 + i)); 20219410Sguido if (vxbusyeeprom(sc)) 20319410Sguido return 0; 20419410Sguido x = inw(BASE + VX_W0_EEPROM_DATA); 20519410Sguido sc->arpcom.ac_enaddr[(i << 1)] = x >> 8; 20619410Sguido sc->arpcom.ac_enaddr[(i << 1) + 1] = x; 20719410Sguido } 20819410Sguido 20919410Sguido printf(" address %6D\n", sc->arpcom.ac_enaddr, ":"); 21019410Sguido 21119410Sguido ifp->if_unit = sc->unit; 21219410Sguido ifp->if_name = "vx"; 21319410Sguido ifp->if_mtu = ETHERMTU; 21419410Sguido ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 21519410Sguido ifp->if_output = ether_output; 21619410Sguido ifp->if_start = vxstart; 21719410Sguido ifp->if_ioctl = vxioctl; 21819410Sguido ifp->if_init = vxinit; 21919410Sguido ifp->if_watchdog = vxwatchdog; 22019410Sguido ifp->if_softc = sc; 22119410Sguido 22219410Sguido if_attach(ifp); 22319410Sguido ether_ifattach(ifp); 22419410Sguido 22519410Sguido#if NBPFILTER > 0 22619410Sguido bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 22719410Sguido#endif 22819410Sguido 22919410Sguido sc->tx_start_thresh = 20; /* probably a good starting point. */ 23019410Sguido 23119410Sguido vxstop(sc); 23219410Sguido 23319410Sguido return 1; 23419410Sguido} 23519410Sguido 23619410Sguido 23719410Sguido 23819410Sguido/* 23919410Sguido * The order in here seems important. Otherwise we may not receive 24019410Sguido * interrupts. ?! 24119410Sguido */ 24219410Sguidostatic void 24319410Sguidovxinit(xsc) 24419410Sguido void *xsc; 24519410Sguido{ 24619410Sguido struct vx_softc *sc = (struct vx_softc *) xsc; 24719410Sguido struct ifnet *ifp = &sc->arpcom.ac_if; 24819410Sguido int i; 24919410Sguido 25019410Sguido VX_BUSY_WAIT; 25119410Sguido 25219410Sguido GO_WINDOW(2); 25319410Sguido 25419410Sguido for (i = 0; i < 6; i++) /* Reload the ether_addr. */ 25519410Sguido outb(BASE + VX_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); 25619410Sguido 25719410Sguido outw(BASE + VX_COMMAND, RX_RESET); 25819410Sguido VX_BUSY_WAIT; 25919410Sguido outw(BASE + VX_COMMAND, TX_RESET); 26019410Sguido VX_BUSY_WAIT; 26119410Sguido 26219410Sguido GO_WINDOW(1); /* Window 1 is operating window */ 26319410Sguido for (i = 0; i < 31; i++) 26419410Sguido inb(BASE + VX_W1_TX_STATUS); 26519410Sguido 26619410Sguido outw(BASE + VX_COMMAND,SET_RD_0_MASK | S_CARD_FAILURE | 26719410Sguido S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); 26819410Sguido outw(BASE + VX_COMMAND,SET_INTR_MASK | S_CARD_FAILURE | 26919410Sguido S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); 27019410Sguido 27119410Sguido /* 27219410Sguido * Attempt to get rid of any stray interrupts that occured during 27319410Sguido * configuration. On the i386 this isn't possible because one may 27419410Sguido * already be queued. However, a single stray interrupt is 27519410Sguido * unimportant. 27619410Sguido */ 27719410Sguido outw(BASE + VX_COMMAND, ACK_INTR | 0xff); 27819410Sguido 27919410Sguido vxsetfilter(sc); 28019410Sguido vxsetlink(sc); 28119410Sguido 28219410Sguido outw(BASE + VX_COMMAND, RX_ENABLE); 28319410Sguido outw(BASE + VX_COMMAND, TX_ENABLE); 28419410Sguido 28519410Sguido vxmbuffill((caddr_t) sc); 28619410Sguido 28719410Sguido /* Interface is now `running', with no output active. */ 28819410Sguido ifp->if_flags |= IFF_RUNNING; 28919410Sguido ifp->if_flags &= ~IFF_OACTIVE; 29019410Sguido 29119410Sguido /* Attempt to start output, if any. */ 29219410Sguido vxstart(ifp); 29319410Sguido} 29419410Sguido 29519410Sguidostatic void 29619410Sguidovxsetfilter(sc) 29719410Sguido struct vx_softc *sc; 29819410Sguido{ 29919410Sguido register struct ifnet *ifp = &sc->arpcom.ac_if; 30021666Swollman 30119410Sguido GO_WINDOW(1); /* Window 1 is operating window */ 30219410Sguido outw(BASE + VX_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST | 30321666Swollman FIL_MULTICAST | 30421666Swollman ((ifp->if_flags & IFF_PROMISC) ? FIL_PROMISC : 0 )); 30519410Sguido} 30619410Sguido 30719410Sguidostatic void 30819410Sguidovxgetlink(sc) 30919410Sguido struct vx_softc *sc; 31019410Sguido{ 31119410Sguido int n, k; 31219410Sguido 31319410Sguido GO_WINDOW(3); 31419410Sguido sc->vx_connectors = inw(BASE + VX_W3_RESET_OPT) & 0x7f; 31519410Sguido for (n = 0, k = 0; k < VX_CONNECTORS; k++) { 31620096Sguido if (sc->vx_connectors & conn_tab[k].bit) { 31719410Sguido if (n > 0) { 31819410Sguido printf("/"); 31919410Sguido } 32020096Sguido printf(conn_tab[k].name); 32119410Sguido n++; 32219410Sguido } 32319410Sguido } 32419410Sguido if (sc->vx_connectors == 0) { 32519410Sguido printf("no connectors!"); 32619410Sguido return; 32719410Sguido } 32819410Sguido GO_WINDOW(3); 32919410Sguido sc->vx_connector = (inl(BASE + VX_W3_INTERNAL_CFG) 33019410Sguido & INTERNAL_CONNECTOR_MASK) 33119410Sguido >> INTERNAL_CONNECTOR_BITS; 33219410Sguido if (sc->vx_connector & 0x10) { 33319410Sguido sc->vx_connector &= 0x0f; 33420096Sguido printf("[*%s*]", conn_tab[sc->vx_connector].name); 33519410Sguido printf(": disable 'auto select' with DOS util!", sc->unit); 33619410Sguido } else { 33720096Sguido printf("[*%s*]", conn_tab[sc->vx_connector].name); 33819410Sguido } 33919410Sguido} 34019410Sguido 34119410Sguidostatic void 34219410Sguidovxsetlink(sc) 34319410Sguido struct vx_softc *sc; 34419410Sguido{ 34519410Sguido register struct ifnet *ifp = &sc->arpcom.ac_if; 34620096Sguido int i, j, k; 34720096Sguido char *reason, *warning; 34820096Sguido static short prev_flags; 34920096Sguido static char prev_conn = -1; 35019410Sguido 35120096Sguido if (prev_conn == -1) { 35220096Sguido prev_conn = sc->vx_connector; 35320096Sguido } 35420096Sguido 35519410Sguido /* 35619410Sguido * S.B. 35719410Sguido * 35819410Sguido * Now behavior was slightly changed: 35919410Sguido * 36019410Sguido * if any of flags link[0-2] is used and its connector is 36119410Sguido * physically present the following connectors are used: 36219410Sguido * 36319410Sguido * link0 - AUI * highest precedence 36419410Sguido * link1 - BNC 36519410Sguido * link2 - UTP * lowest precedence 36619410Sguido * 36719410Sguido * If none of them is specified then 36819410Sguido * connector specified in the EEPROM is used 36920096Sguido * (if present on card or UTP if not). 37019410Sguido */ 37120096Sguido 37220096Sguido i = sc->vx_connector; /* default in EEPROM */ 37320096Sguido reason = "default"; 37420096Sguido warning = 0; 37520096Sguido 37620096Sguido if (ifp->if_flags & IFF_LINK0) { 37720096Sguido if (sc->vx_connectors & conn_tab[CONNECTOR_AUI].bit) { 37820096Sguido i = CONNECTOR_AUI; 37920096Sguido reason = "link0"; 38020096Sguido } else { 38120096Sguido warning = "aui not present! (link0)"; 38220096Sguido } 38320096Sguido } else if (ifp->if_flags & IFF_LINK1) { 38420096Sguido if (sc->vx_connectors & conn_tab[CONNECTOR_BNC].bit) { 38520096Sguido i = CONNECTOR_BNC; 38620096Sguido reason = "link1"; 38720096Sguido } else { 38820096Sguido warning = "bnc not present! (link1)"; 38920096Sguido } 39020096Sguido } else if (ifp->if_flags & IFF_LINK2) { 39120096Sguido if (sc->vx_connectors & conn_tab[CONNECTOR_UTP].bit) { 39220096Sguido i = CONNECTOR_UTP; 39320096Sguido reason = "link2"; 39420096Sguido } else { 39520096Sguido warning = "utp not present! (link2)"; 39620096Sguido } 39720096Sguido } else if ((sc->vx_connectors & conn_tab[sc->vx_connector].bit) == 0) { 39820096Sguido warning = "strange connector type in EEPROM."; 39920096Sguido reason = "forced"; 40019410Sguido i = CONNECTOR_UTP; 40119410Sguido } 40220096Sguido 40320096Sguido /* Avoid unnecessary message. */ 40420096Sguido k = (prev_flags ^ ifp->if_flags) & (IFF_LINK0 | IFF_LINK1 | IFF_LINK2); 40520096Sguido if ((k != 0) || (prev_conn != i)) { 40620096Sguido if (warning != 0) { 40720096Sguido printf("vx%d: warning: %s\n", sc->unit); 40820096Sguido } 40920096Sguido printf("vx%d: selected %s. (%s)\n", 41020096Sguido sc->unit, conn_tab[i].name, reason); 41120096Sguido } 41220096Sguido 41320096Sguido /* Set the selected connector. */ 41419410Sguido GO_WINDOW(3); 41519410Sguido j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK; 41619410Sguido outl(BASE + VX_W3_INTERNAL_CFG, j | (i <<INTERNAL_CONNECTOR_BITS)); 41720096Sguido 41820096Sguido /* First, disable all. */ 41920096Sguido outw(BASE + VX_COMMAND, STOP_TRANSCEIVER); 42020096Sguido DELAY(800); 42120096Sguido GO_WINDOW(4); 42220096Sguido outw(BASE + VX_W4_MEDIA_TYPE, 0); 42320096Sguido 42420096Sguido /* Second, enable the selected one. */ 42519410Sguido switch(i) { 42619410Sguido case CONNECTOR_UTP: 42720096Sguido GO_WINDOW(4); 42820096Sguido outw(BASE + VX_W4_MEDIA_TYPE, ENABLE_UTP); 42919410Sguido break; 43019410Sguido case CONNECTOR_BNC: 43120096Sguido outw(BASE + VX_COMMAND, START_TRANSCEIVER); 43220096Sguido DELAY(800); 43319410Sguido break; 43419410Sguido case CONNECTOR_TX: 43519410Sguido case CONNECTOR_FX: 43620096Sguido GO_WINDOW(4); 43720096Sguido outw(BASE + VX_W4_MEDIA_TYPE, LINKBEAT_ENABLE); 43819410Sguido break; 43920096Sguido default: /* AUI and MII fall here */ 44019410Sguido break; 44119410Sguido } 44219410Sguido GO_WINDOW(1); 44320096Sguido 44420096Sguido prev_flags = ifp->if_flags; 44520096Sguido prev_conn = i; 44619410Sguido} 44719410Sguido 44819410Sguidostatic void 44919410Sguidovxstart(ifp) 45019410Sguido struct ifnet *ifp; 45119410Sguido{ 45219410Sguido register struct vx_softc *sc = vx_softc[ifp->if_unit]; 45319410Sguido register struct mbuf *m, *m0; 45419410Sguido int sh, len, pad; 45519410Sguido 45619410Sguido /* Don't transmit if interface is busy or not running */ 45719410Sguido if ((sc->arpcom.ac_if.if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING) 45819410Sguido return; 45919410Sguido 46019410Sguidostartagain: 46119410Sguido /* Sneak a peek at the next packet */ 46219410Sguido m0 = ifp->if_snd.ifq_head; 46319410Sguido if (m0 == 0) { 46419410Sguido return; 46519410Sguido } 46619410Sguido /* We need to use m->m_pkthdr.len, so require the header */ 46719410Sguido if ((m0->m_flags & M_PKTHDR) == 0) 46819410Sguido panic("vxstart: no header mbuf"); 46919410Sguido len = m0->m_pkthdr.len; 47019410Sguido 47119410Sguido pad = (4 - len) & 3; 47219410Sguido 47319410Sguido /* 47419410Sguido * The 3c509 automatically pads short packets to minimum ethernet length, 47519410Sguido * but we drop packets that are too large. Perhaps we should truncate 47619410Sguido * them instead? 47719410Sguido */ 47819410Sguido if (len + pad > ETHER_MAX_LEN) { 47919410Sguido /* packet is obviously too large: toss it */ 48019410Sguido ++ifp->if_oerrors; 48119410Sguido IF_DEQUEUE(&ifp->if_snd, m0); 48219410Sguido m_freem(m0); 48319410Sguido goto readcheck; 48419410Sguido } 48520096Sguido VX_BUSY_WAIT; 48619410Sguido if (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) { 48719410Sguido outw(BASE + VX_COMMAND, SET_TX_AVAIL_THRESH | ((len + pad + 4) >> 2)); 48819410Sguido /* not enough room in FIFO */ 48920096Sguido if (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) { /* make sure */ 49020096Sguido ifp->if_flags |= IFF_OACTIVE; 49120096Sguido ifp->if_timer = 1; 49220096Sguido return; 49320096Sguido } 49419410Sguido } 49520096Sguido outw(BASE + VX_COMMAND, SET_TX_AVAIL_THRESH | (8188 >> 2)); 49619410Sguido IF_DEQUEUE(&ifp->if_snd, m0); 49719410Sguido if (m0 == 0) { /* not really needed */ 49819410Sguido return; 49919410Sguido } 50019410Sguido 50120096Sguido VX_BUSY_WAIT; 50219410Sguido outw(BASE + VX_COMMAND, SET_TX_START_THRESH | 50319410Sguido ((len / 4 + sc->tx_start_thresh) >> 2)); 50419410Sguido 50519410Sguido#if NBPFILTER > 0 50619410Sguido if (sc->arpcom.ac_if.if_bpf) { 50719410Sguido bpf_mtap(&sc->arpcom.ac_if, m0); 50819410Sguido } 50919410Sguido#endif 51019410Sguido 51119410Sguido /* 51219410Sguido * Do the output at splhigh() so that an interrupt from another device 51319410Sguido * won't cause a FIFO underrun. 51419410Sguido */ 51519410Sguido sh = splhigh(); 51619410Sguido 51719410Sguido outl(BASE + VX_W1_TX_PIO_WR_1, len | TX_INDICATE); 51819410Sguido 51919410Sguido for (m = m0; m != 0;) { 52019410Sguido if (m->m_len > 3) 52119410Sguido outsl(BASE + VX_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 4); 52219410Sguido if (m->m_len & 3) 52319410Sguido outsb(BASE + VX_W1_TX_PIO_WR_1, 52419410Sguido mtod(m, caddr_t) + (m->m_len & ~3) , m->m_len & 3); 52519410Sguido MFREE(m, m0); 52619410Sguido m = m0; 52719410Sguido } 52819410Sguido while (pad--) 52919410Sguido outb(BASE + VX_W1_TX_PIO_WR_1, 0); /* Padding */ 53019410Sguido 53119410Sguido splx(sh); 53219410Sguido 53319410Sguido ++ifp->if_opackets; 53419410Sguido ifp->if_timer = 1; 53519410Sguido 53619410Sguidoreadcheck: 53719410Sguido if ((inw(BASE + VX_W1_RX_STATUS) & ERR_INCOMPLETE) == 0) { 53819410Sguido /* We received a complete packet. */ 53919410Sguido 54019410Sguido if ((inw(BASE + VX_STATUS) & S_INTR_LATCH) == 0) { 54119410Sguido /* 54219410Sguido * No interrupt, read the packet and continue 54319410Sguido * Is this supposed to happen? Is my motherboard 54419410Sguido * completely busted? 54519410Sguido */ 54619410Sguido vxread(sc); 54719410Sguido } else 54819410Sguido /* Got an interrupt, return so that it gets serviced. */ 54919410Sguido return; 55019410Sguido } else { 55119410Sguido /* Check if we are stuck and reset [see XXX comment] */ 55219410Sguido if (vxstatus(sc)) { 55319410Sguido if (ifp->if_flags & IFF_DEBUG) 55419410Sguido printf("vx%d: adapter reset\n", ifp->if_unit); 55519410Sguido vxreset(sc); 55619410Sguido } 55719410Sguido } 55819410Sguido 55919410Sguido goto startagain; 56019410Sguido} 56119410Sguido 56219410Sguido/* 56319410Sguido * XXX: The 3c509 card can get in a mode where both the fifo status bit 56419410Sguido * FIFOS_RX_OVERRUN and the status bit ERR_INCOMPLETE are set 56519410Sguido * We detect this situation and we reset the adapter. 56619410Sguido * It happens at times when there is a lot of broadcast traffic 56719410Sguido * on the cable (once in a blue moon). 56819410Sguido */ 56919410Sguidostatic int 57019410Sguidovxstatus(sc) 57119410Sguido struct vx_softc *sc; 57219410Sguido{ 57319410Sguido int fifost; 57419410Sguido 57519410Sguido /* 57619410Sguido * Check the FIFO status and act accordingly 57719410Sguido */ 57819410Sguido GO_WINDOW(4); 57919410Sguido fifost = inw(BASE + VX_W4_FIFO_DIAG); 58019410Sguido GO_WINDOW(1); 58119410Sguido 58219410Sguido if (fifost & FIFOS_RX_UNDERRUN) { 58319410Sguido if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 58419410Sguido printf("vx%d: RX underrun\n", sc->unit); 58519410Sguido vxreset(sc); 58619410Sguido return 0; 58719410Sguido } 58819410Sguido 58919410Sguido if (fifost & FIFOS_RX_STATUS_OVERRUN) { 59019410Sguido if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 59119410Sguido printf("vx%d: RX Status overrun\n", sc->unit); 59219410Sguido return 1; 59319410Sguido } 59419410Sguido 59519410Sguido if (fifost & FIFOS_RX_OVERRUN) { 59619410Sguido if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 59719410Sguido printf("vx%d: RX overrun\n", sc->unit); 59819410Sguido return 1; 59919410Sguido } 60019410Sguido 60119410Sguido if (fifost & FIFOS_TX_OVERRUN) { 60219410Sguido if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 60319410Sguido printf("vx%d: TX overrun\n", sc->unit); 60419410Sguido vxreset(sc); 60519410Sguido return 0; 60619410Sguido } 60719410Sguido 60819410Sguido return 0; 60919410Sguido} 61019410Sguido 61119410Sguidostatic void 61219410Sguidovxtxstat(sc) 61319410Sguido struct vx_softc *sc; 61419410Sguido{ 61519410Sguido int i; 61619410Sguido 61719410Sguido /* 61819410Sguido * We need to read+write TX_STATUS until we get a 0 status 61919410Sguido * in order to turn off the interrupt flag. 62019410Sguido */ 62119410Sguido while ((i = inb(BASE + VX_W1_TX_STATUS)) & TXS_COMPLETE) { 62219410Sguido outb(BASE + VX_W1_TX_STATUS, 0x0); 62319410Sguido 62419410Sguido if (i & TXS_JABBER) { 62519410Sguido ++sc->arpcom.ac_if.if_oerrors; 62619410Sguido if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 62719410Sguido printf("vx%d: jabber (%x)\n", sc->unit, i); 62819410Sguido vxreset(sc); 62919410Sguido } else if (i & TXS_UNDERRUN) { 63019410Sguido ++sc->arpcom.ac_if.if_oerrors; 63119410Sguido if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 63219410Sguido printf("vx%d: fifo underrun (%x) @%d\n", 63319410Sguido sc->unit, i, sc->tx_start_thresh); 63419410Sguido if (sc->tx_succ_ok < 100) 63519410Sguido sc->tx_start_thresh = min(ETHER_MAX_LEN, sc->tx_start_thresh + 20); 63619410Sguido sc->tx_succ_ok = 0; 63719410Sguido vxreset(sc); 63819410Sguido } else if (i & TXS_MAX_COLLISION) { 63919410Sguido ++sc->arpcom.ac_if.if_collisions; 64019410Sguido outw(BASE + VX_COMMAND, TX_ENABLE); 64119410Sguido sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; 64219410Sguido } else 64319410Sguido sc->tx_succ_ok = (sc->tx_succ_ok+1) & 127; 64419410Sguido } 64519410Sguido} 64619410Sguido 64719410Sguidovoid 64819410Sguidovxintr(sc) 64919410Sguido struct vx_softc *sc; 65019410Sguido{ 65119410Sguido register short status; 65219410Sguido struct ifnet *ifp = &sc->arpcom.ac_if; 65319410Sguido 65419410Sguido for (;;) { 65519410Sguido outw(BASE + VX_COMMAND, C_INTR_LATCH); 65619410Sguido 65719410Sguido status = inw(BASE + VX_STATUS); 65819410Sguido 65919410Sguido if ((status & (S_TX_COMPLETE | S_TX_AVAIL | 66019410Sguido S_RX_COMPLETE | S_CARD_FAILURE)) == 0) 66119410Sguido break; 66219410Sguido 66319410Sguido /* 66419410Sguido * Acknowledge any interrupts. It's important that we do this 66519410Sguido * first, since there would otherwise be a race condition. 66619410Sguido * Due to the i386 interrupt queueing, we may get spurious 66719410Sguido * interrupts occasionally. 66819410Sguido */ 66919410Sguido outw(BASE + VX_COMMAND, ACK_INTR | status); 67019410Sguido 67119410Sguido if (status & S_RX_COMPLETE) 67219410Sguido vxread(sc); 67319410Sguido if (status & S_TX_AVAIL) { 67419410Sguido ifp->if_timer = 0; 67519410Sguido sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; 67619410Sguido vxstart(&sc->arpcom.ac_if); 67719410Sguido } 67819410Sguido if (status & S_CARD_FAILURE) { 67919410Sguido printf("vx%d: adapter failure (%x)\n", sc->unit, status); 68019410Sguido ifp->if_timer = 0; 68119410Sguido vxreset(sc); 68219410Sguido return; 68319410Sguido } 68419410Sguido if (status & S_TX_COMPLETE) { 68519410Sguido ifp->if_timer = 0; 68619410Sguido vxtxstat(sc); 68719410Sguido vxstart(ifp); 68819410Sguido } 68919410Sguido } 69019410Sguido 69119410Sguido /* no more interrupts */ 69219410Sguido return; 69319410Sguido} 69419410Sguido 69519410Sguidostatic void 69619410Sguidovxread(sc) 69719410Sguido struct vx_softc *sc; 69819410Sguido{ 69919410Sguido struct ifnet *ifp = &sc->arpcom.ac_if; 70019410Sguido struct mbuf *m; 70119410Sguido struct ether_header *eh; 70219410Sguido u_int len; 70319410Sguido 70419410Sguido len = inw(BASE + VX_W1_RX_STATUS); 70519410Sguido 70619410Sguidoagain: 70719410Sguido 70819410Sguido if (ifp->if_flags & IFF_DEBUG) { 70919410Sguido int err = len & ERR_MASK; 71019410Sguido char *s = NULL; 71119410Sguido 71219410Sguido if (len & ERR_INCOMPLETE) 71319410Sguido s = "incomplete packet"; 71419410Sguido else if (err == ERR_OVERRUN) 71519410Sguido s = "packet overrun"; 71619410Sguido else if (err == ERR_RUNT) 71719410Sguido s = "runt packet"; 71819410Sguido else if (err == ERR_ALIGNMENT) 71919410Sguido s = "bad alignment"; 72019410Sguido else if (err == ERR_CRC) 72119410Sguido s = "bad crc"; 72219410Sguido else if (err == ERR_OVERSIZE) 72319410Sguido s = "oversized packet"; 72419410Sguido else if (err == ERR_DRIBBLE) 72519410Sguido s = "dribble bits"; 72619410Sguido 72719410Sguido if (s) 72819410Sguido printf("vx%d: %s\n", sc->unit, s); 72919410Sguido } 73019410Sguido 73119410Sguido if (len & ERR_INCOMPLETE) 73219410Sguido return; 73319410Sguido 73419410Sguido if (len & ERR_RX) { 73519410Sguido ++ifp->if_ierrors; 73619410Sguido goto abort; 73719410Sguido } 73819410Sguido 73919410Sguido len &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */ 74019410Sguido 74119410Sguido /* Pull packet off interface. */ 74219410Sguido m = vxget(sc, len); 74319410Sguido if (m == 0) { 74419410Sguido ifp->if_ierrors++; 74519410Sguido goto abort; 74619410Sguido } 74719410Sguido 74819410Sguido ++ifp->if_ipackets; 74919410Sguido 75019410Sguido /* We assume the header fit entirely in one mbuf. */ 75119410Sguido eh = mtod(m, struct ether_header *); 75219410Sguido 75319410Sguido#if NBPFILTER > 0 75419410Sguido /* 75519410Sguido * Check if there's a BPF listener on this interface. 75619410Sguido * If so, hand off the raw packet to BPF. 75719410Sguido */ 75819410Sguido if (sc->arpcom.ac_if.if_bpf) { 75919410Sguido bpf_mtap(&sc->arpcom.ac_if, m); 76022062Sphk } 76122062Sphk#endif 76219410Sguido 76322062Sphk /* 76422062Sphk * XXX: Some cards seem to be in promiscous mode all the time. 76522062Sphk * we need to make sure we only get our own stuff always. 76622062Sphk * bleah! 76722062Sphk */ 76822062Sphk 76922062Sphk if ((eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */ 77022062Sphk bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 77122062Sphk sizeof(eh->ether_dhost)) != 0) { 77222062Sphk m_freem(m); 77319410Sguido return; 77419410Sguido } 77519410Sguido /* We assume the header fit entirely in one mbuf. */ 77619410Sguido m_adj(m, sizeof(struct ether_header)); 77719410Sguido ether_input(ifp, eh, m); 77819410Sguido 77919410Sguido /* 78019410Sguido * In periods of high traffic we can actually receive enough 78119410Sguido * packets so that the fifo overrun bit will be set at this point, 78219410Sguido * even though we just read a packet. In this case we 78319410Sguido * are not going to receive any more interrupts. We check for 78419410Sguido * this condition and read again until the fifo is not full. 78519410Sguido * We could simplify this test by not using vxstatus(), but 78619410Sguido * rechecking the RX_STATUS register directly. This test could 78719410Sguido * result in unnecessary looping in cases where there is a new 78819410Sguido * packet but the fifo is not full, but it will not fix the 78919410Sguido * stuck behavior. 79019410Sguido * 79119410Sguido * Even with this improvement, we still get packet overrun errors 79219410Sguido * which are hurting performance. Maybe when I get some more time 79319410Sguido * I'll modify vxread() so that it can handle RX_EARLY interrupts. 79419410Sguido */ 79519410Sguido if (vxstatus(sc)) { 79619410Sguido len = inw(BASE + VX_W1_RX_STATUS); 79719410Sguido /* Check if we are stuck and reset [see XXX comment] */ 79819410Sguido if (len & ERR_INCOMPLETE) { 79919410Sguido if (ifp->if_flags & IFF_DEBUG) 80019410Sguido printf("vx%d: adapter reset\n", sc->unit); 80119410Sguido vxreset(sc); 80219410Sguido return; 80319410Sguido } 80419410Sguido goto again; 80519410Sguido } 80619410Sguido 80719410Sguido return; 80819410Sguido 80919410Sguidoabort: 81019410Sguido outw(BASE + VX_COMMAND, RX_DISCARD_TOP_PACK); 81119410Sguido} 81219410Sguido 81319410Sguidostatic struct mbuf * 81419410Sguidovxget(sc, totlen) 81519410Sguido struct vx_softc *sc; 81619410Sguido u_int totlen; 81719410Sguido{ 81819410Sguido struct ifnet *ifp = &sc->arpcom.ac_if; 81919410Sguido struct mbuf *top, **mp, *m; 82019410Sguido int len; 82119410Sguido int sh; 82219410Sguido 82319410Sguido m = sc->mb[sc->next_mb]; 82419410Sguido sc->mb[sc->next_mb] = 0; 82519410Sguido if (m == 0) { 82619410Sguido MGETHDR(m, M_DONTWAIT, MT_DATA); 82719410Sguido if (m == 0) 82819410Sguido return 0; 82919410Sguido } else { 83019410Sguido /* If the queue is no longer full, refill. */ 83129671Sgibbs if (sc->last_mb == sc->next_mb && sc->buffill_pending == 0) { 83229671Sgibbs sc->ch = timeout(vxmbuffill, sc, 1); 83329671Sgibbs sc->buffill_pending = 1; 83429671Sgibbs } 83519410Sguido /* Convert one of our saved mbuf's. */ 83619410Sguido sc->next_mb = (sc->next_mb + 1) % MAX_MBS; 83719410Sguido m->m_data = m->m_pktdat; 83819410Sguido m->m_flags = M_PKTHDR; 83919410Sguido } 84019410Sguido m->m_pkthdr.rcvif = ifp; 84119410Sguido m->m_pkthdr.len = totlen; 84219410Sguido len = MHLEN; 84319410Sguido top = 0; 84419410Sguido mp = ⊤ 84519410Sguido 84619410Sguido /* 84719410Sguido * We read the packet at splhigh() so that an interrupt from another 84819410Sguido * device doesn't cause the card's buffer to overflow while we're 84919410Sguido * reading it. We may still lose packets at other times. 85019410Sguido */ 85119410Sguido sh = splhigh(); 85219410Sguido 85319410Sguido while (totlen > 0) { 85419410Sguido if (top) { 85519410Sguido m = sc->mb[sc->next_mb]; 85619410Sguido sc->mb[sc->next_mb] = 0; 85719410Sguido if (m == 0) { 85819410Sguido MGET(m, M_DONTWAIT, MT_DATA); 85919410Sguido if (m == 0) { 86019410Sguido splx(sh); 86119410Sguido m_freem(top); 86219410Sguido return 0; 86319410Sguido } 86419410Sguido } else { 86519410Sguido sc->next_mb = (sc->next_mb + 1) % MAX_MBS; 86619410Sguido } 86719410Sguido len = MLEN; 86819410Sguido } 86919410Sguido if (totlen >= MINCLSIZE) { 87019410Sguido MCLGET(m, M_DONTWAIT); 87119410Sguido if (m->m_flags & M_EXT) 87219410Sguido len = MCLBYTES; 87319410Sguido } 87419410Sguido len = min(totlen, len); 87519410Sguido if (len > 3) { 87619410Sguido len &= ~3; 87719410Sguido insl(BASE + VX_W1_RX_PIO_RD_1, mtod(m, u_int32_t *), 87819410Sguido len / 4); 87919410Sguido } else 88019410Sguido insb(BASE + VX_W1_RX_PIO_RD_1, mtod(m, u_int8_t *), 88119410Sguido len); 88219410Sguido m->m_len = len; 88319410Sguido totlen -= len; 88419410Sguido *mp = m; 88519410Sguido mp = &m->m_next; 88619410Sguido } 88719410Sguido 88819410Sguido outw(BASE +VX_COMMAND, RX_DISCARD_TOP_PACK); 88919410Sguido 89019410Sguido splx(sh); 89119410Sguido 89219410Sguido return top; 89319410Sguido} 89419410Sguido 89519410Sguido 89619410Sguidostatic int 89719410Sguidovxioctl(ifp, cmd, data) 89819410Sguido register struct ifnet *ifp; 89919410Sguido int cmd; 90019410Sguido caddr_t data; 90119410Sguido{ 90219410Sguido struct vx_softc *sc = vx_softc[ifp->if_unit]; 90319410Sguido struct ifaddr *ifa = (struct ifaddr *) data; 90419410Sguido struct ifreq *ifr = (struct ifreq *) data; 90519410Sguido int s, error = 0; 90619410Sguido 90719410Sguido s = splimp(); 90819410Sguido 90919410Sguido switch (cmd) { 91019410Sguido case SIOCSIFADDR: 91119410Sguido case SIOCGIFADDR: 91219410Sguido ether_ioctl(ifp, cmd, data); 91319410Sguido break; 91419410Sguido 91519410Sguido case SIOCSIFFLAGS: 91619410Sguido if ((ifp->if_flags & IFF_UP) == 0 && 91719410Sguido (ifp->if_flags & IFF_RUNNING) != 0) { 91819410Sguido /* 91919410Sguido * If interface is marked up and it is stopped, then 92019410Sguido * start it. 92119410Sguido */ 92219410Sguido vxstop(sc); 92319410Sguido ifp->if_flags &= ~IFF_RUNNING; 92419410Sguido } else if ((ifp->if_flags & IFF_UP) != 0 && 92519410Sguido (ifp->if_flags & IFF_RUNNING) == 0) { 92619410Sguido /* 92719410Sguido * If interface is marked up and it is stopped, then 92819410Sguido * start it. 92919410Sguido */ 93019410Sguido vxinit(sc); 93119410Sguido } else { 93219410Sguido /* 93319410Sguido * deal with flags changes: 93419410Sguido * IFF_MULTICAST, IFF_PROMISC, 93519410Sguido * IFF_LINK0, IFF_LINK1, 93619410Sguido */ 93719410Sguido vxsetfilter(sc); 93819410Sguido vxsetlink(sc); 93919410Sguido } 94019410Sguido break; 94119410Sguido 94219410Sguido case SIOCSIFMTU: 94319410Sguido /* 94419410Sguido * Set the interface MTU. 94519410Sguido */ 94619410Sguido if (ifr->ifr_mtu > ETHERMTU) { 94719410Sguido error = EINVAL; 94819410Sguido } else { 94919410Sguido ifp->if_mtu = ifr->ifr_mtu; 95019410Sguido } 95119410Sguido break; 95219410Sguido 95319410Sguido case SIOCADDMULTI: 95419410Sguido case SIOCDELMULTI: 95521666Swollman /* 95621666Swollman * Multicast list has changed; set the hardware filter 95721666Swollman * accordingly. 95821666Swollman */ 95921666Swollman vxreset(sc); 96021666Swollman error = 0; 96119410Sguido break; 96219410Sguido 96319410Sguido 96419410Sguido default: 96519410Sguido error = EINVAL; 96619410Sguido } 96719410Sguido 96819410Sguido splx(s); 96919410Sguido 97019410Sguido return (error); 97119410Sguido} 97219410Sguido 97319410Sguidostatic void 97419410Sguidovxreset(sc) 97519410Sguido struct vx_softc *sc; 97619410Sguido{ 97719410Sguido int s; 97819410Sguido s = splimp(); 97919410Sguido 98019410Sguido vxstop(sc); 98119410Sguido vxinit(sc); 98219410Sguido splx(s); 98319410Sguido} 98419410Sguido 98519410Sguidostatic void 98619410Sguidovxwatchdog(ifp) 98719410Sguido struct ifnet *ifp; 98819410Sguido{ 98919410Sguido struct vx_softc *sc = vx_softc[ifp->if_unit]; 99019410Sguido 99119410Sguido if (ifp->if_flags & IFF_DEBUG) 99219410Sguido printf("vx%d: device timeout\n", ifp->if_unit); 99319410Sguido ifp->if_flags &= ~IFF_OACTIVE; 99419410Sguido vxstart(ifp); 99519410Sguido vxintr(sc); 99619410Sguido} 99719410Sguido 99819410Sguidovoid 99919410Sguidovxstop(sc) 100019410Sguido struct vx_softc *sc; 100119410Sguido{ 100219410Sguido struct ifnet *ifp = &sc->arpcom.ac_if; 100319410Sguido 100419410Sguido ifp->if_timer = 0; 100519410Sguido 100619410Sguido outw(BASE + VX_COMMAND, RX_DISABLE); 100719410Sguido outw(BASE + VX_COMMAND, RX_DISCARD_TOP_PACK); 100819410Sguido VX_BUSY_WAIT; 100919410Sguido outw(BASE + VX_COMMAND, TX_DISABLE); 101019410Sguido outw(BASE + VX_COMMAND, STOP_TRANSCEIVER); 101119410Sguido DELAY(800); 101219410Sguido outw(BASE + VX_COMMAND, RX_RESET); 101319410Sguido VX_BUSY_WAIT; 101419410Sguido outw(BASE + VX_COMMAND, TX_RESET); 101519410Sguido VX_BUSY_WAIT; 101619410Sguido outw(BASE + VX_COMMAND, C_INTR_LATCH); 101719410Sguido outw(BASE + VX_COMMAND, SET_RD_0_MASK); 101819410Sguido outw(BASE + VX_COMMAND, SET_INTR_MASK); 101919410Sguido outw(BASE + VX_COMMAND, SET_RX_FILTER); 102019410Sguido 102119410Sguido vxmbufempty(sc); 102219410Sguido} 102319410Sguido 102419410Sguidoint 102519410Sguidovxbusyeeprom(sc) 102619410Sguido struct vx_softc *sc; 102719410Sguido{ 102819410Sguido int j, i = 100; 102919410Sguido 103019410Sguido while (i--) { 103119410Sguido j = inw(BASE + VX_W0_EEPROM_COMMAND); 103219410Sguido if (j & EEPROM_BUSY) 103319410Sguido DELAY(100); 103419410Sguido else 103519410Sguido break; 103619410Sguido } 103719410Sguido if (!i) { 103819410Sguido printf("vx%d: eeprom failed to come ready\n", sc->unit); 103919410Sguido return (1); 104019410Sguido } 104119410Sguido return (0); 104219410Sguido} 104319410Sguido 104419410Sguidostatic void 104519410Sguidovxmbuffill(sp) 104619410Sguido void *sp; 104719410Sguido{ 104819410Sguido struct vx_softc *sc = (struct vx_softc *) sp; 104919410Sguido int s, i; 105019410Sguido 105119410Sguido s = splimp(); 105219410Sguido i = sc->last_mb; 105319410Sguido do { 105419410Sguido if (sc->mb[i] == NULL) 105519410Sguido MGET(sc->mb[i], M_DONTWAIT, MT_DATA); 105619410Sguido if (sc->mb[i] == NULL) 105719410Sguido break; 105819410Sguido i = (i + 1) % MAX_MBS; 105919410Sguido } while (i != sc->next_mb); 106019410Sguido sc->last_mb = i; 106119410Sguido /* If the queue was not filled, try again. */ 106229671Sgibbs if (sc->last_mb != sc->next_mb) { 106329671Sgibbs sc->ch = timeout(vxmbuffill, sc, 1); 106429671Sgibbs sc->buffill_pending = 1; 106529671Sgibbs } else { 106629671Sgibbs sc->buffill_pending = 0; 106729671Sgibbs } 106819410Sguido splx(s); 106919410Sguido} 107019410Sguido 107119410Sguidostatic void 107219410Sguidovxmbufempty(sc) 107319410Sguido struct vx_softc *sc; 107419410Sguido{ 107519410Sguido int s, i; 107619410Sguido 107719410Sguido s = splimp(); 107819410Sguido for (i = 0; i < MAX_MBS; i++) { 107919410Sguido if (sc->mb[i]) { 108019410Sguido m_freem(sc->mb[i]); 108119410Sguido sc->mb[i] = NULL; 108219410Sguido } 108319410Sguido } 108419410Sguido sc->last_mb = sc->next_mb = 0; 108529671Sgibbs if (sc->buffill_pending != 0) 108629671Sgibbs untimeout(vxmbuffill, sc, sc->ch); 108719410Sguido splx(s); 108819410Sguido} 108919410Sguido 109019410Sguido#endif /* NVX > 0 */ 1091