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 = &top;
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