BSD 4_3 release
[unix-history] / usr / src / sys / vaxif / if_vv.c
index edde2c4..07e28de 100644 (file)
-/*     if_vv.c 6.1     83/07/29        */
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ *
+ *     @(#)if_vv.c     7.1 (Berkeley) 6/5/86
+ */
 
 #include "vv.h"
 
 #include "vv.h"
+#if NVV > 0
 
 /*
 
 /*
- * Proteon 10 Meg Ring Driver.
- * This device is called "vv" because its "real name",
- * V2LNI won't work if shortened to the obvious "v2".
- * Hence the subterfuge.
+ * Proteon proNET-10 and proNET-80 token ring driver.
+ * The name of this device driver derives from the old MIT
+ * name of V2LNI for the proNET hardware, would would abbreviate
+ * to "v2", but this won't work right. Thus the name is "vv".
+ *
+ * This driver is compatible with the proNET 10 meagbit and
+ * 80 megabit token ring interfaces (models p1000 and p1080).
+ * A unit may be marked as 80 megabit using "flags 1" in the
+ * config file.
+ *
+ * TRAILERS: This driver has a new implementation of trailers that
+ * is at least a tolerable neighbor on the ring. The offset is not
+ * stored in the protocol type, but instead only in the vh_info
+ * field. Also, the vh_info field, and the two shorts before the
+ * trailing header, are in network byte order, not VAX byte order.
+ *
+ * Of course, nothing but BSD UNIX supports trailers on ProNET.
+ * If you need interoperability with anything else, turn off
+ * trailers using the -trailers option to /etc/ifconfig!
+ *
+ * HARDWARE COMPATABILITY: This driver prefers that the HSBU (p1001)
+ * have a serial number >= 040, which is about March, 1982. Older
+ * HSBUs do not carry across 64kbyte boundaries. They can be supported
+ * by adding "| UBA_NEED16" to the vs_ifuba.ifu_flags initialization
+ * in vvattach().
  *
  *
+ * The old warning about use without Wire Centers applies only to CTL
+ * (p1002) cards with serial <= 057, which have not received ECO 176-743,
+ * which was implemented in March, 1982. Most such CTLs have received
+ * this ECO.
  */
 #include "../machine/pte.h"
 
  */
 #include "../machine/pte.h"
 
-#include "../h/param.h"
-#include "../h/systm.h"
-#include "../h/mbuf.h"
-#include "../h/buf.h"
-#include "../h/protosw.h"
-#include "../h/socket.h"
-#include "../h/vmmac.h"
-#include "../h/errno.h"
-#include "../h/time.h"
-#include "../h/kernel.h"
-#include "../h/ioctl.h"
+#include "param.h"
+#include "systm.h"
+#include "mbuf.h"
+#include "buf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "vmmac.h"
+#include "errno.h"
+#include "ioctl.h"
 
 #include "../net/if.h"
 #include "../net/netisr.h"
 #include "../net/route.h"
 
 
 #include "../net/if.h"
 #include "../net/netisr.h"
 #include "../net/route.h"
 
+#ifdef INET
 #include "../netinet/in.h"
 #include "../netinet/in_systm.h"
 #include "../netinet/in.h"
 #include "../netinet/in_systm.h"
+#include "../netinet/in_var.h"
 #include "../netinet/ip.h"
 #include "../netinet/ip.h"
-#include "../netinet/ip_var.h"
+#endif
 
 
-#include "../vax/mtpr.h"
 #include "../vax/cpu.h"
 #include "../vax/cpu.h"
-
+#include "../vax/mtpr.h"
+#include "if_vv.h"
+#include "if_uba.h"
 #include "../vaxuba/ubareg.h"
 #include "../vaxuba/ubavar.h"
 
 #include "../vaxuba/ubareg.h"
 #include "../vaxuba/ubavar.h"
 
-#include "../vaxif/if_vv.h"
-#include "../vaxif/if_uba.h"
-
 /*
 /*
- * N.B. - if WIRECENTER is defined wrong, it can well break
- * the hardware!!
+ *    maximum transmission unit definition --
+ *        you can set VVMTU at anything from 576 to 2024.
+ *        1536 is a popular "large" value, because it is a multiple
+ *       of 512, which the trailer scheme likes.
+ *        The absolute maximum size is 2024, which is enforced.
  */
  */
-#define        WIRECENTER
 
 
-#ifdef WIRECENTER
-#define        VV_CONF VV_HEN          /* drive wire center relay */
-#else
-#define        VV_CONF VV_STE          /* allow operation without wire center */
+#define VVMTU (1536)
+
+#define VVMRU (VVMTU + 16)
+#define VVBUFSIZE (VVMRU + sizeof(struct vv_header))
+#if VVMTU>2024
+#undef VVMTU
+#undef VVMRU
+#undef VVBUFSIZE
+#define VVBUFSIZE (2046)
+#define VVMRU (VVBUFSIZE - sizeof (struct vv_header))
+#define VVMTU (VVMRU - 16)
 #endif
 
 #endif
 
-#define        VVMTU   (1024+512)
-#define VVMRU  (1024+512+16)   /* space for trailer */
-
-int vv_tracehdr = 0,           /* 1 => trace headers (slowly!!) */
-    vv_tracetimeout = 1;       /* 1 => trace input error-rate limiting */
-    vv_logreaderrors = 0;      /* 1 => log all read errors */
-
-#define vvtracehdr     if (vv_tracehdr) vvprt_hdr
-#define        vvtrprintf      if (vv_tracetimeout) printf
+/*
+ *   debugging and tracing stuff
+ */
+int    vv_tracehdr = 0;        /* 1 => trace headers (slowly!!) */
 
 
-int vv_ticking = 0;            /* error flywheel is running */
+#define vvtracehdr  if (vv_tracehdr) vvprt_hdr
+#define vvprintf    if (vs->vs_if.if_flags & IFF_DEBUG) printf
 
 /*
 
 /*
- * Interval in HZ - 50 msec.
- * N.B. all times below are in units of flywheel ticks
+ * externals, types, etc.
  */
  */
-#define VV_FLYWHEEL            3
-#define        VV_ERRORTHRESHOLD       100     /* errors/flywheel-interval */
-#define        VV_MODE1ATTEMPTS        10      /* number mode 1 retries */
-#define        VV_MODE1DELAY           2       /* period interface is PAUSEd - 100ms */
-#define VV_MODE2DELAY          4       /* base interval host relay is off - 200ms */
-#define        VV_MAXDELAY             6400    /* max interval host relay is off - 2 minutes */
-
-int    vvprobe(), vvattach(), vvrint(), vvxint();
+int    vvprobe(), vvattach(), vvreset(), vvinit();
+int    vvidentify(), vvstart(), vvxint(), vvwatchdog();
+int    vvrint(), vvoutput(), vvioctl();
 struct uba_device *vvinfo[NVV];
 u_short vvstd[] = { 0 };
 struct uba_driver vvdriver =
        { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo };
 #define        VVUNIT(x)       minor(x)
 struct uba_device *vvinfo[NVV];
 u_short vvstd[] = { 0 };
 struct uba_driver vvdriver =
        { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo };
 #define        VVUNIT(x)       minor(x)
-int    vvinit(),vvioctl(),vvoutput(),vvreset();
+
+#define LOOPBACK               /* use loopback for packets meant for us */
+#ifdef LOOPBACK
+extern struct ifnet loif;
+#endif
 
 /*
  * Software status of each interface.
 
 /*
  * Software status of each interface.
@@ -99,59 +132,53 @@ int        vvinit(),vvioctl(),vvoutput(),vvreset();
 struct vv_softc {
        struct  ifnet vs_if;            /* network-visible interface */
        struct  ifuba vs_ifuba;         /* UNIBUS resources */
 struct vv_softc {
        struct  ifnet vs_if;            /* network-visible interface */
        struct  ifuba vs_ifuba;         /* UNIBUS resources */
+       u_short vs_host;                /* this interface address */
        short   vs_oactive;             /* is output active */
        short   vs_oactive;             /* is output active */
-       short   vs_iactive;             /* is input active */
+       short   vs_is80;                /* is 80 megabit version */
        short   vs_olen;                /* length of last output */
        short   vs_olen;                /* length of last output */
-       u_short vs_lastx;               /* last destination address */
+       u_short vs_lastx;               /* address of last packet sent */
+       u_short vs_lastr;               /* address of last packet received */
        short   vs_tries;               /* transmit current retry count */
        short   vs_init;                /* number of ring inits */
        short   vs_tries;               /* transmit current retry count */
        short   vs_init;                /* number of ring inits */
-       short   vs_nottaken;            /* number of packets refused */
-       /* input error rate limiting state */
-       short   vs_major;               /* recovery major state */
-       short   vs_minor;               /* recovery minor state */
-       short   vs_retry;               /* recovery retry count */
-       short   vs_delayclock;          /* recovery delay clock */
-       short   vs_delayrange;          /* increasing delay interval */
-       short   vs_dropped;             /* number of packes tossed in last dt */
+       short   vs_refused;             /* number of packets refused */
+       short   vs_timeouts;            /* number of transmit timeouts */
+       short   vs_otimeout;            /* number of output timeouts */
+       short   vs_ibadf;               /* number of input bad formats */
+       short   vs_parity;              /* number of parity errors on 10 meg, */
+                                       /* link data errors on 80 meg */
 } vv_softc[NVV];
 
 } vv_softc[NVV];
 
-/*
- * States of vs_iactive.
- */
-#define        ACTIVE  1               /* interface should post new receives */
-#define        PAUSE   0               /* interface should NOT post new receives */
-#define        OPEN    -1              /* PAUSE and open host relay */
+#define        NOHOST  0xffff                  /* illegal host number */
 
 /*
 
 /*
- * Recovery major states.
+ * probe the interface to see that the registers exist, and then
+ * cause an interrupt to find its vector
  */
  */
-#define        MODE0   0               /* everything is wonderful */
-#define        MODE1   1               /* hopefully whatever will go away */
-#define        MODE2   2               /* drastic measures - open host relay for increasing intervals */
-
 vvprobe(reg)
        caddr_t reg;
 {
        register int br, cvec;
 vvprobe(reg)
        caddr_t reg;
 {
        register int br, cvec;
-       register struct vvreg *addr = (struct vvreg *)reg;
+       register struct vvreg *addr;
 
 #ifdef lint
 
 #ifdef lint
-       br = 0; cvec = br; br = cvec; vvrint(0);
+       br = 0; cvec = br; br = cvec;
 #endif
 #endif
+       addr = (struct vvreg *)reg;
+
        /* reset interface, enable, and wait till dust settles */
        addr->vvicsr = VV_RST;
        addr->vvocsr = VV_RST;
        /* reset interface, enable, and wait till dust settles */
        addr->vvicsr = VV_RST;
        addr->vvocsr = VV_RST;
-       DELAY(10000);
+       DELAY(100000);
+
        /* generate interrupt by doing 1 word DMA from 0 in uba space!! */
        /* generate interrupt by doing 1 word DMA from 0 in uba space!! */
-       addr->vvocsr = VV_IEN;          /* enable interrupt */
        addr->vvoba = 0;                /* low 16 bits */
        addr->vvoea = 0;                /* extended bits */
        addr->vvowc = -1;               /* for 1 word */
        addr->vvoba = 0;                /* low 16 bits */
        addr->vvoea = 0;                /* extended bits */
        addr->vvowc = -1;               /* for 1 word */
-       addr->vvocsr |= VV_DEN;         /* start the DMA */
+       addr->vvocsr = VV_IEN | VV_DEN; /* start the DMA, with interrupt */
        DELAY(100000);
        DELAY(100000);
-       addr->vvocsr = 0;
+       addr->vvocsr = VV_RST;          /* clear out the CSR */
        if (cvec && cvec != 0x200)
        if (cvec && cvec != 0x200)
-               cvec -= 4;              /* backup so vector => recieve */
+               cvec -= 4;              /* backup so vector => receive */
        return(1);
 }
 
        return(1);
 }
 
@@ -163,16 +190,24 @@ vvprobe(reg)
 vvattach(ui)
        struct uba_device *ui;
 {
 vvattach(ui)
        struct uba_device *ui;
 {
-       register struct vv_softc *vs = &vv_softc[ui->ui_unit];
+       register struct vv_softc *vs;
 
 
+       vs = &vv_softc[ui->ui_unit];
        vs->vs_if.if_unit = ui->ui_unit;
        vs->vs_if.if_name = "vv";
        vs->vs_if.if_mtu = VVMTU;
        vs->vs_if.if_unit = ui->ui_unit;
        vs->vs_if.if_name = "vv";
        vs->vs_if.if_mtu = VVMTU;
+       vs->vs_if.if_flags = IFF_BROADCAST;
        vs->vs_if.if_init = vvinit;
        vs->vs_if.if_ioctl = vvioctl;
        vs->vs_if.if_output = vvoutput;
        vs->vs_if.if_reset = vvreset;
        vs->vs_if.if_init = vvinit;
        vs->vs_if.if_ioctl = vvioctl;
        vs->vs_if.if_output = vvoutput;
        vs->vs_if.if_reset = vvreset;
-       vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16;
+       vs->vs_if.if_timer = 0;
+       vs->vs_if.if_watchdog = vvwatchdog;
+       vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP;
+
+       /* use flag to determine if this is proNET-80 */
+       vs->vs_is80 = (short)(ui->ui_flags & 01);
+
 #if defined(VAX750)
        /* don't chew up 750 bdp's */
        if (cpu == VAX_750 && ui->ui_unit > 0)
 #if defined(VAX750)
        /* don't chew up 750 bdp's */
        if (cpu == VAX_750 && ui->ui_unit > 0)
@@ -204,45 +239,52 @@ vvreset(unit, uban)
 vvinit(unit)
        int unit;
 {
 vvinit(unit)
        int unit;
 {
-       register struct vv_softc *vs = &vv_softc[unit];
-       register struct uba_device *ui = vvinfo[unit];
+       register struct vv_softc *vs;
+       register struct uba_device *ui;
        register struct vvreg *addr;
        register struct vvreg *addr;
-       struct sockaddr_in *sin;
-       int ubainfo, s;
-       int vvtimeout();
+       register int ubainfo, s;
 
 
-       if (vs->vs_if.if_net == 0)
+       vs = &vv_softc[unit];
+       ui = vvinfo[unit];
+
+       if (vs->vs_if.if_addrlist == (struct ifaddr *)0)
                return;
                return;
+
        addr = (struct vvreg *)ui->ui_addr;
        if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum,
        addr = (struct vvreg *)ui->ui_addr;
        if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum,
-           sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 
-               printf("vv%d: can't initialize\n", unit);
+           sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) {
+               printf("vv%d: can't initialize, if_ubainit() failed\n", unit);
                vs->vs_if.if_flags &= ~IFF_UP;
                return;
        }
                vs->vs_if.if_flags &= ~IFF_UP;
                return;
        }
-       if (vv_ticking++ == 0)
-               timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL);
+
        /*
        /*
-        * Discover our host address and post it
+        * Now that the uba is set up, figure out our address and
+        * update complete our host address.
         */
         */
-       vs->vs_if.if_host[0] = vvidentify(unit);
-       printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]);
-       sin = (struct sockaddr_in *)&vs->vs_if.if_addr;
-       sin->sin_family = AF_INET;
-       sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]);
-       sin = (struct sockaddr_in *)&vs->vs_if.if_broadaddr;
-       sin->sin_family = AF_INET;
-       sin->sin_addr = if_makeaddr(vs->vs_if.if_net, VV_BROADCAST);
+       if ((vs->vs_host = vvidentify(unit)) == NOHOST) {
+               vs->vs_if.if_flags &= ~IFF_UP;
+               return;
+       }
+       printf("vv%d: host %u\n", unit, vs->vs_host);
 
        /*
 
        /*
-        * Reset the interface, and join the ring
+        * Reset the interface, and stay in the ring
         */
         */
-       addr->vvocsr = VV_RST | VV_CPB;         /* clear packet buffer */
-       addr->vvicsr = VV_RST | VV_CONF;        /* close logical relay */
+       addr->vvocsr = VV_RST;                  /* take over output */
+       addr->vvocsr = VV_CPB;                  /* clear packet buffer */
+       addr->vvicsr = VV_RST | VV_HEN;         /* take over input, */
+                                               /* keep relay closed */
        DELAY(500000);                          /* let contacts settle */
        DELAY(500000);                          /* let contacts settle */
-       vs->vs_init = 0;
-       vs->vs_dropped = 0;
-       vs->vs_nottaken = 0;
+
+       vs->vs_init = 0;                        /* clear counters, etc. */
+       vs->vs_refused = 0;
+       vs->vs_timeouts = 0;
+       vs->vs_otimeout = 0;
+       vs->vs_ibadf = 0;
+       vs->vs_parity = 0;
+       vs->vs_lastx = 256;                     /* an invalid address */
+       vs->vs_lastr = 256;                     /* an invalid address */
 
        /*
         * Hang a receive and start any
 
        /*
         * Hang a receive and start any
@@ -252,37 +294,56 @@ vvinit(unit)
        ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
        addr->vviba = (u_short)ubainfo;
        addr->vviea = (u_short)(ubainfo >> 16);
        ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
        addr->vviba = (u_short)ubainfo;
        addr->vviea = (u_short)(ubainfo >> 16);
-       addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
-       addr->vvicsr = VV_IEN | VV_CONF | VV_DEN | VV_ENB;
-       vs->vs_iactive = ACTIVE;
+       addr->vviwc = -(VVBUFSIZE) >> 1;
+       addr->vvicsr = VV_IEN | VV_HEN | VV_DEN | VV_ENB;
        vs->vs_oactive = 1;
        vs->vs_oactive = 1;
-       vs->vs_if.if_flags |= IFF_UP | IFF_RUNNING;
+       vs->vs_if.if_flags |= IFF_RUNNING;
        vvxint(unit);
        splx(s);
        vvxint(unit);
        splx(s);
-       if_rtinit(&vs->vs_if, RTF_UP);
 }
 
 /*
 }
 
 /*
- * vvidentify() - return our host address
+ * Do a moderately thorough self-test in all three modes. Mostly
+ * to keeps defective nodes off the ring, rather than to be especially
+ * thorough. The key issue is to detect any cable breaks before joining
+ * the ring. Return our node address on success, return -1 on failure.
+ *
  */
  */
+
+/* the three self-test modes */
+static u_short vv_modes[] = {
+       VV_STE|VV_LPB,                  /* digital loopback */
+       VV_STE,                         /* analog loopback */
+       VV_HEN                          /* network mode */
+};
+
 vvidentify(unit)
        int unit;
 {
 vvidentify(unit)
        int unit;
 {
-       register struct vv_softc *vs = &vv_softc[unit];
-       register struct uba_device *ui = vvinfo[unit];
+       register struct vv_softc *vs;
+       register struct uba_device *ui;
        register struct vvreg *addr;
        register struct vvreg *addr;
-       struct mbuf *m;
-       struct vv_header *v;
-       int ubainfo, attempts, waitcount;
+       register struct mbuf *m;
+       register struct vv_header *v;
+       register int ubainfo;
+       register int i, successes, failures, waitcount;
+       u_short shost = NOHOST;
+
+       vs = &vv_softc[unit];
+       ui = vvinfo[unit];
+       addr = (struct vvreg *)ui->ui_addr;
 
        /*
         * Build a multicast message to identify our address
 
        /*
         * Build a multicast message to identify our address
+        * We need do this only once, since nobody else is about to use
+        * the intermediate transmit buffer (ifu_w.ifrw_addr) that
+        * if_ubainit() aquired for us.
         */
         */
-       addr = (struct vvreg *)ui->ui_addr;
-       attempts = 0;           /* total attempts, including bad msg type */
        m = m_get(M_DONTWAIT, MT_HEADER);
        m = m_get(M_DONTWAIT, MT_HEADER);
-       if (m == NULL)
+       if (m == NULL) {
+               printf("vv%d: can't initialize, m_get() failed\n", unit);
                return (0);
                return (0);
+       }
        m->m_next = 0;
        m->m_off = MMINOFF;
        m->m_len = sizeof(struct vv_header);
        m->m_next = 0;
        m->m_off = MMINOFF;
        m->m_len = sizeof(struct vv_header);
@@ -290,211 +351,128 @@ vvidentify(unit)
        v->vh_dhost = VV_BROADCAST;     /* multicast destination address */
        v->vh_shost = 0;                /* will be overwritten with ours */
        v->vh_version = RING_VERSION;
        v->vh_dhost = VV_BROADCAST;     /* multicast destination address */
        v->vh_shost = 0;                /* will be overwritten with ours */
        v->vh_version = RING_VERSION;
-       v->vh_type = RING_WHOAMI;
+       v->vh_type = RING_DIAGNOSTICS;
        v->vh_info = 0;
        v->vh_info = 0;
-       /* map xmit message into uba */
-       vs->vs_olen =  if_wubaput(&vs->vs_ifuba, m);
-       if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
-               UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
-       /*
-        * Reset interface, establish Digital Loopback Mode, and
-        * send the multicast (to myself) with Input Copy enabled.
-        */
-retry:
-       ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
-       addr->vvicsr = VV_RST;
-       addr->vviba = (u_short) ubainfo;
-       addr->vviea = (u_short) (ubainfo >> 16);
-       addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
-       addr->vvicsr = VV_STE | VV_DEN | VV_ENB | VV_LPB;
-
-       /* let flag timers fire so ring will initialize */
-       DELAY(2000000);
+       /* map xmit message into uba, copying to intermediate buffer */
+       vs->vs_olen = if_wubaput(&vs->vs_ifuba, m);
 
 
-       addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */
-       ubainfo = vs->vs_ifuba.ifu_w.ifrw_info;
-       addr->vvoba = (u_short) ubainfo;
-       addr->vvoea = (u_short) (ubainfo >> 16);
-       addr->vvowc = -((vs->vs_olen + 1) >> 1);
-       addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB;
        /*
        /*
-        * Wait for receive side to finish.
-        * Extract source address (which will our own),
-        * and post to interface structure.
+        * For each of the modes (digital, analog, network), go through
+        * a self-test that requires me to send VVIDENTSUCC good packets
+        * in VVIDENTRETRY attempts. Use broadcast destination to find out
+        * who I am, then use this as my address to check my address match
+        * logic. Only data checked is the vh_type field.
         */
         */
-       DELAY(1000);
-       for (waitcount = 0; (addr->vvicsr & VV_RDY) == 0; waitcount++) {
-               if (waitcount < 10) {
-                       DELAY(1000);
-                       continue;
-               }
-               if (attempts++ >= 10) {
-                       printf("vv%d: can't initialize\n", unit);
-                       printf("vvinit loopwait: icsr = %b\n",
-                               0xffff&(addr->vvicsr), VV_IBITS);
-                       vs->vs_if.if_flags &= ~IFF_UP;
-                       return (0);
-               }
-               goto retry;
-       }
-       if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
-               UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
-       if (vs->vs_ifuba.ifu_xtofree)
-               m_freem(vs->vs_ifuba.ifu_xtofree);
-       if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
-               UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
-       m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0);
-       if (m != NULL)
-               m_freem(m);
-       /*
-        * Check message type before we believe the source host address
-        */
-       v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
-       if (v->vh_type != RING_WHOAMI)
-               goto retry;
-       return(v->vh_shost);
-}
 
 
-/*
- * vvtimeout() - called by timer flywheel to monitor input packet
- * discard rate.  Interfaces getting too many errors are shut
- * down for a while.  If the condition persists, the interface
- * is marked down.
- */
-/*ARGSUSED*/
-vvtimeout(junk)
-       int junk;
-{
-       register struct vv_softc *vs;
-       register int i;
-       register struct vvreg *addr;
-       int ubainfo;
+       for (i = 0; i < 3; i++) {
+               successes = 0;  /* clear successes for this mode */
+               failures = 0;   /* and clear failures, too */
 
 
-       timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL);
-       for (i = 0; i < NVV; i++) {
-               vs = &vv_softc[i];
-               addr = (struct vvreg *)vvinfo[i]->ui_addr;
-               if ((vs->vs_if.if_flags & IFF_UP) == 0)
-                       continue;
-               switch (vs->vs_major) {
+               /* take over device, and leave ring */
+               addr->vvicsr = VV_RST;
+               addr->vvocsr = VV_RST;
+               addr->vvicsr = vv_modes[i];     /* test mode */
 
                /*
 
                /*
-                * MODE0: generally OK, just check error rate 
+                * let the flag and token timers pop so that the init ring bit
+                * will be allowed to work, by waiting about 1 second
                 */
                 */
-               case MODE0:
-                       if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
-                               vs->vs_dropped = 0;
-                               continue;
-                       }
-                       /* suspend reads for a while */
-                       vvtrprintf("vv%d going MODE1 in vvtimeout\n",i);
-                       vs->vs_major = MODE1;
-                       vs->vs_iactive = PAUSE; /* no new reads */
-                       vs->vs_retry = VV_MODE1ATTEMPTS;
-                       vs->vs_delayclock = VV_MODE1DELAY;
-                       vs->vs_minor = 0;
-                       continue;
+               DELAY(1000000L);
 
                /*
 
                /*
-                * MODE1: excessive error rate observed
-                * Scheme: try simply suspending reads for a
-                * short while a small number of times
-                */
-               case MODE1:
-                       if (vs->vs_delayclock > 0) {
-                               vs->vs_delayclock--;
-                               continue;
-                       }
-                       switch (vs->vs_minor) {
-
-                       case 0:                         /* reenable reads */
-                               vvtrprintf("vv%d M1m0\n",i);
-                               vs->vs_dropped = 0;
-                               vs->vs_iactive = ACTIVE;
-                               vs->vs_minor = 1;       /* next state */
-                               ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
-                               addr->vviba = (u_short) ubainfo;
-                               addr->vviea = (u_short) (ubainfo >> 16);
-                               addr->vviwc =
-                                 -(sizeof (struct vv_header) + VVMTU) >> 1;
-                               addr->vvicsr = VV_RST | VV_CONF;
-                               addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
-                               continue;
-
-                       case 1:                         /* see if it worked */
-                               vvtrprintf("vv%d M1m1\n",i);
-                               if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
-                                       vs->vs_dropped = 0;
-                                       vs->vs_major = MODE0;   /* yeah!! */
-                                       continue;
-                               }
-                               if (vs->vs_retry -- > 0) {
-                                       vs->vs_dropped = 0;
-                                       vs->vs_iactive = PAUSE;
-                                       vs->vs_delayclock = VV_MODE1DELAY;
-                                       vs->vs_minor = 0; /* recheck */
-                                       continue;
-                               }
-                               vs->vs_major = MODE2;
-                               vs->vs_minor = 0;
-                               vs->vs_dropped = 0;
-                               vs->vs_iactive = OPEN;
-                               vs->vs_delayrange = VV_MODE2DELAY;
-                               vs->vs_delayclock = VV_MODE2DELAY;
-                               /* fall thru ... */
+                * retry loop
+                */
+               while ((successes < VVIDENTSUCC) && (failures < VVIDENTRETRY))
+               {
+                       /* start a receive */
+                       ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
+                       addr->vvicsr = VV_RST | vv_modes[i]; /* abort last */
+                       addr->vviba = (u_short) ubainfo;
+                       addr->vviea = (u_short) (ubainfo >> 16);
+                       addr->vviwc = -(VVBUFSIZE) >> 1;
+                       addr->vvicsr = vv_modes[i] | VV_DEN | VV_ENB;
+
+                       /* purge stale data from BDP */
+                       if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
+                               UBAPURGE(vs->vs_ifuba.ifu_uba,
+                                   vs->vs_ifuba.ifu_w.ifrw_bdp);
+
+                       /* do a transmit */
+                       ubainfo = vs->vs_ifuba.ifu_w.ifrw_info;
+                       addr->vvocsr = VV_RST;  /* abort last try */
+                       addr->vvoba = (u_short) ubainfo;
+                       addr->vvoea = (u_short) (ubainfo >> 16);
+                       addr->vvowc = -((vs->vs_olen + 1) >> 1);
+                       addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB;
+
+                       /* poll receive side for completion */
+                       DELAY(10000);           /* give it a chance */
+                       for (waitcount = 0; waitcount < 10; waitcount++) {
+                               if (addr->vvicsr & VV_RDY)
+                                       goto gotit;
+                               DELAY(1000);
                        }
                        }
+                       failures++;             /* no luck */
+                       continue;
 
 
-               /*
-                * MODE2: simply ignoring traffic didn't relieve condition
-                * Scheme: open host relay for intervals linearly
-                * increasing up to some maximum of a several minutes.
-                * This allows broken networks to return to operation
-                * without rebooting.
-                */
-               case MODE2:
-                       if (vs->vs_delayclock > 0) {
-                               vs->vs_delayclock--;
+gotit:                 /* we got something--is it any good? */
+                       if ((addr->vvicsr & (VVRERR|VV_LDE)) ||
+                           (addr->vvocsr & (VVXERR|VV_RFS))) {
+                               failures++;
                                continue;
                        }
                                continue;
                        }
-                       switch (vs->vs_minor) {
-
-                       case 0:         /* close relay and reenable reads */
-                               vvtrprintf("vv%d M2m0\n",i);
-                               vs->vs_dropped = 0;
-                               vs->vs_iactive = ACTIVE;
-                               vs->vs_minor = 1;       /* next state */
-                               ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
-                               addr->vviba = (u_short) ubainfo;
-                               addr->vviea = (u_short) (ubainfo >> 16);
-                               addr->vviwc =
-                                 -(sizeof (struct vv_header) + VVMTU) >> 1;
-                               addr->vvicsr = VV_RST | VV_CONF;
-                               addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
-                               continue;
 
 
-                       case 1:                         /* see if it worked */
-                               vvtrprintf("vv%d M2m1\n",i);
-                               if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
-                                       vs->vs_dropped = 0;
-                                       vs->vs_major = MODE0;   /* yeah!! */
-                                       continue;
+                       /* Purge BDP before looking at received packet */
+                       if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
+                               UBAPURGE(vs->vs_ifuba.ifu_uba,
+                                   vs->vs_ifuba.ifu_r.ifrw_bdp);
+                       m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header),
+                               0, &vs->vs_if);
+                       if (m != NULL)
+                               m_freem(m);
+                       
+                       v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
+
+                       /* check message type, catch our node address */
+                       if ((v->vh_type & 0xff) == RING_DIAGNOSTICS) {
+                               if (shost == NOHOST) {
+                                       shost = v->vh_shost & 0xff;
+                                       /* send to ourself now */
+                                       ((struct vv_header *)
+                                           (vs->vs_ifuba.ifu_r.ifrw_addr))
+                                           ->vh_dhost = shost;
                                }
                                }
-                               vvtrprintf("vv%d M2m1 ++ delay\n",i);
-                               vs->vs_dropped = 0;
-                               vs->vs_iactive = OPEN;
-                               vs->vs_minor = 0;
-                               if (vs->vs_delayrange < VV_MAXDELAY)
-                                       vs->vs_delayrange +=
-                                         (vs->vs_delayrange/2);
-                               vs->vs_delayclock = vs->vs_delayrange;
-                               continue;
+                               successes++;
+                       } else {
+                               failures++;
                        }
                        }
+                       v->vh_type = 0;  /* clear to check again */
+               }
 
 
-               default:
-                       printf("vv%d: major state screwed\n", i);
-                       vs->vs_if.if_flags &= ~IFF_UP;
+               if (failures >= VVIDENTRETRY)
+               {
+                       printf("vv%d: failed self-test after %d tries \
+in %s mode\n",
+                           unit, VVIDENTRETRY, i == 0 ? "digital loopback" :
+                           (i == 1 ? "analog loopback" : "network"));
+                       printf("vv%d: icsr = %b, ocsr = %b\n",
+                           unit, 0xffff & addr->vvicsr, VV_IBITS,
+                           0xffff & addr->vvocsr, VV_OBITS);
+                       addr->vvicsr = VV_RST;  /* kill the sick board */
+                       addr->vvocsr = VV_RST;
+                       shost = NOHOST;
+                       goto done;
                }
        }
                }
        }
+
+done:
+       /* deallocate mbuf used for send packet (won't be one, anyways) */
+       if (vs->vs_ifuba.ifu_xtofree) {
+               m_freem(vs->vs_ifuba.ifu_xtofree);
+               vs->vs_ifuba.ifu_xtofree = 0;
+       }
+
+       return(shost);
 }
 
 /*
 }
 
 /*
@@ -508,14 +486,15 @@ vvtimeout(junk)
 vvstart(dev)
        dev_t dev;
 {
 vvstart(dev)
        dev_t dev;
 {
-        int unit = VVUNIT(dev);
-       struct uba_device *ui = vvinfo[unit];
-       register struct vv_softc *vs = &vv_softc[unit];
+       register struct uba_device *ui;
+       register struct vv_softc *vs;
        register struct vvreg *addr;
        register struct vvreg *addr;
-       struct mbuf *m;
-       int ubainfo;
-       int dest;
+       register struct mbuf *m;
+       register int unit, ubainfo, dest, s;
 
 
+       unit = VVUNIT(dev);
+       ui = vvinfo[unit];
+       vs = &vv_softc[unit];
        if (vs->vs_oactive)
                goto restart;
        /*
        if (vs->vs_oactive)
                goto restart;
        /*
@@ -523,7 +502,9 @@ vvstart(dev)
         * and map it to the UNIBUS.  If no more requests,
         * just return.
         */
         * and map it to the UNIBUS.  If no more requests,
         * just return.
         */
+       s = splimp();
        IF_DEQUEUE(&vs->vs_if.if_snd, m);
        IF_DEQUEUE(&vs->vs_if.if_snd, m);
+       splx(s);
        if (m == NULL) {
                vs->vs_oactive = 0;
                return;
        if (m == NULL) {
                vs->vs_oactive = 0;
                return;
@@ -534,12 +515,19 @@ vvstart(dev)
 restart:
        /*
         * Have request mapped to UNIBUS for transmission.
 restart:
        /*
         * Have request mapped to UNIBUS for transmission.
-        * Purge any stale data from this BDP, and start the otput.
+        * Purge any stale data from this BDP, and start the output.
+        *
+        * Make sure this packet will fit in the interface.
         */
         */
-       if (vs->vs_olen > VVMTU + sizeof (struct vv_header)) {
-               printf("vv%d vs_olen: %d > VVMTU\n", unit, vs->vs_olen);
+       if (vs->vs_olen > VVBUFSIZE) {
+               printf("vv%d vs_olen: %d > VVBUFSIZE\n", unit, vs->vs_olen);
                panic("vvdriver vs_olen botch");
        }
                panic("vvdriver vs_olen botch");
        }
+
+       vs->vs_if.if_timer = VVTIMEOUT;
+       vs->vs_oactive = 1;
+
+       /* ship it */
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
        addr = (struct vvreg *)ui->ui_addr;
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
        addr = (struct vvreg *)ui->ui_addr;
@@ -547,96 +535,147 @@ restart:
        addr->vvoba = (u_short) ubainfo;
        addr->vvoea = (u_short) (ubainfo >> 16);
        addr->vvowc = -((vs->vs_olen + 1) >> 1);
        addr->vvoba = (u_short) ubainfo;
        addr->vvoea = (u_short) (ubainfo >> 16);
        addr->vvowc = -((vs->vs_olen + 1) >> 1);
+       addr->vvowc = -((vs->vs_olen + 1) >> 1); /* extra byte is garbage */
+       if (addr->vvocsr & VV_NOK)
+               vs->vs_init++;                  /* count ring inits */
        addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB;
        addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB;
-       vs->vs_oactive = 1;
 }
 
 /*
 }
 
 /*
- * VVLNI transmit interrupt
+ * proNET transmit interrupt
  * Start another output if more data to send.
  */
 vvxint(unit)
        int unit;
 {
  * Start another output if more data to send.
  */
 vvxint(unit)
        int unit;
 {
-       register struct uba_device *ui = vvinfo[unit];
-       register struct vv_softc *vs = &vv_softc[unit];
+       register struct uba_device *ui;
+       register struct vv_softc *vs;
        register struct vvreg *addr;
        register int oc;
 
        register struct vvreg *addr;
        register int oc;
 
+       ui = vvinfo[unit];
+       vs = &vv_softc[unit];
+       vs->vs_if.if_timer = 0;
        addr = (struct vvreg *)ui->ui_addr;
        oc = 0xffff & (addr->vvocsr);
        if (vs->vs_oactive == 0) {
        addr = (struct vvreg *)ui->ui_addr;
        oc = 0xffff & (addr->vvocsr);
        if (vs->vs_oactive == 0) {
-               printf("vv%d: stray interrupt vvocsr = %b\n", unit,
-                       oc, VV_OBITS);
+               vvprintf("vv%d: stray interrupt vvocsr = %b\n", unit,
+                   oc, VV_OBITS);
                return;
        }
                return;
        }
-       if (oc &  (VV_OPT | VV_RFS)) {
-               vs->vs_if.if_collisions++;
+
+       /*
+        * we retransmit on soft error
+        * TODO: sort retransmits to end of queue if possible!
+        */
+       if (oc & (VV_OPT | VV_RFS)) {
                if (vs->vs_tries++ < VVRETRY) {
                        if (oc & VV_OPT)
                if (vs->vs_tries++ < VVRETRY) {
                        if (oc & VV_OPT)
-                               vs->vs_init++;
-                       if (oc & VV_RFS)
-                               vs->vs_nottaken++;
+                               vs->vs_otimeout++;
+                       if (oc & VV_RFS) {
+                               vs->vs_if.if_collisions++;
+                               vs->vs_refused++;
+                       }
                        vvstart(unit);          /* restart this message */
                        return;
                }
                        vvstart(unit);          /* restart this message */
                        return;
                }
-               if (oc & VV_OPT)
-                       printf("vv%d: output timeout\n");
        }
        vs->vs_if.if_opackets++;
        vs->vs_oactive = 0;
        vs->vs_tries = 0;
        }
        vs->vs_if.if_opackets++;
        vs->vs_oactive = 0;
        vs->vs_tries = 0;
+
        if (oc & VVXERR) {
                vs->vs_if.if_oerrors++;
        if (oc & VVXERR) {
                vs->vs_if.if_oerrors++;
-               printf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc,
-                       VV_OBITS);
+               vvprintf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc,
+                   VV_OBITS);
        }
        if (vs->vs_ifuba.ifu_xtofree) {
                m_freem(vs->vs_ifuba.ifu_xtofree);
                vs->vs_ifuba.ifu_xtofree = 0;
        }
        }
        if (vs->vs_ifuba.ifu_xtofree) {
                m_freem(vs->vs_ifuba.ifu_xtofree);
                vs->vs_ifuba.ifu_xtofree = 0;
        }
-       if (vs->vs_if.if_snd.ifq_head == 0) {
-               vs->vs_lastx = 256;             /* an invalid address */
-               return;
-       }
        vvstart(unit);
 }
 
 /*
        vvstart(unit);
 }
 
 /*
- * V2lni interface receiver interrupt.
+ * Transmit watchdog timer routine.
+ * This routine gets called when we lose a transmit interrupt.
+ * The best we can do is try to restart output.
+ */
+vvwatchdog(unit)
+       int unit;
+{
+       register struct vv_softc *vs;
+       register int s;
+
+       vs = &vv_softc[unit];
+       vvprintf("vv%d: lost a transmit interrupt.\n", unit);
+       vs->vs_timeouts++;
+       s = splimp();
+       vvstart(unit);
+       splx(s);
+}
+
+/*
+ * proNET interface receiver interrupt.
  * If input error just drop packet.
  * If input error just drop packet.
- * Otherwise purge input buffered data path and examine 
+ * Otherwise purge input buffered data path and examine
  * packet to determine type.  If can't determine length
  * packet to determine type.  If can't determine length
- * from type, then have to drop packet.  Othewise decapsulate
+ * from type, then have to drop packet.  Otherwise decapsulate
  * packet based on type and pass to type specific higher-level
  * input routine.
  */
 vvrint(unit)
        int unit;
 {
  * packet based on type and pass to type specific higher-level
  * input routine.
  */
 vvrint(unit)
        int unit;
 {
-       register struct vv_softc *vs = &vv_softc[unit];
-       struct vvreg *addr = (struct vvreg *)vvinfo[unit]->ui_addr;
+       register struct vv_softc *vs;
+       register struct vvreg *addr;
        register struct vv_header *vv;
        register struct ifqueue *inq;
        register struct vv_header *vv;
        register struct ifqueue *inq;
-       struct mbuf *m;
-       int ubainfo, len, off;
+       register struct mbuf *m;
+       int ubainfo, len, off, s;
        short resid;
 
        short resid;
 
+       vs = &vv_softc[unit];
        vs->vs_if.if_ipackets++;
        vs->vs_if.if_ipackets++;
+       addr = (struct vvreg *)vvinfo[unit]->ui_addr;
+
        /*
        /*
-        * Purge BDP; drop if input error indicated.
+        * Purge BDP
         */
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
         */
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
+
+       /*
+        * receive errors?
+        */
        if (addr->vvicsr & VVRERR) {
        if (addr->vvicsr & VVRERR) {
-               if (vv_logreaderrors)
-                       printf("vv%d: error vvicsr = %b\n", unit,
-                               0xffff&(addr->vvicsr), VV_IBITS);
+               vvprintf("vv%d: receive error, vvicsr = %b\n", unit,
+                   0xffff&(addr->vvicsr), VV_IBITS);
+               if (addr->vvicsr & VV_BDF)
+                       vs->vs_ibadf++;
                goto dropit;
        }
 
        /*
                goto dropit;
        }
 
        /*
-        * Get packet length from word count residue
+        * parity errors?
+        */
+       if (addr->vvicsr & VV_LDE) {
+               /* we don't have to clear it because the receive command */
+               /* writes 0 to parity bit */
+               vs->vs_parity++;
+
+               /*
+                * only on 10 megabit proNET is VV_LDE an end-to-end parity
+                * bit. On 80 megabit, it returns to the intended use of
+                * node-to-node parity. End-to-end parity errors on 80 megabit
+                * give VV_BDF.
+                */
+               if (vs->vs_is80 == 0)
+                   goto dropit;
+       }
+
+       /*
+        * Get packet length from residual word count
         *
         * Compute header offset if trailer protocol
         *
         *
         * Compute header offset if trailer protocol
         *
@@ -648,38 +687,73 @@ vvrint(unit)
         */
        vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
        vvtracehdr("vi", vv);
         */
        vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
        vvtracehdr("vi", vv);
-       resid = addr->vviwc;
+       resid = addr->vviwc & 01777;    /* only low 10 bits valid */
        if (resid)
        if (resid)
-               resid |= 0176000;               /* ugly!!!! */
-       len = (((sizeof (struct vv_header) + VVMRU) >> 1) + resid) << 1;
+               resid |= 0176000;       /* high 6 bits are undefined */
+       len = ((VVBUFSIZE >> 1) + resid) << 1;
        len -= sizeof(struct vv_header);
        len -= sizeof(struct vv_header);
-       if (len > VVMRU || len <= 0)
+
+       if ((addr->vvicsr & VV_DPR) || len > VVMRU || len <= 0) {
+               vvprintf("vv%d: len too long or short, \
+len = %d, vvicsr = %b\n",
+                           unit, len, 0xffff&(addr->vvicsr), VV_IBITS);
+               goto dropit;
+       }
+
+       /* check the protocol header version */
+       if (vv->vh_version != RING_VERSION) {
+               vvprintf("vv%d: bad protocol header version %d\n",
+                   unit, vv->vh_version & 0xff);
                goto dropit;
                goto dropit;
+       }
+
 #define        vvdataaddr(vv, off, type)       ((type)(((caddr_t)((vv)+1)+(off))))
 #define        vvdataaddr(vv, off, type)       ((type)(((caddr_t)((vv)+1)+(off))))
-       if (vv->vh_type >= RING_IPTrailer &&
-            vv->vh_type < RING_IPTrailer+RING_IPNTrailer) {
-               off = (vv->vh_type - RING_IPTrailer) * 512;
-               if (off > VVMTU)
+       if (vv->vh_type == RING_TRAILER ) {
+               off = ntohs((u_short)vv->vh_info);
+               if (off > VVMTU) {
+                       vvprintf("vv%d: off > VVMTU, off = %d, vvicsr = %b\n",
+                                   unit, off, 0xffff&(addr->vvicsr), VV_IBITS);
                        goto dropit;
                        goto dropit;
-               vv->vh_type = *vvdataaddr(vv, off, u_short *);
-               resid = *(vvdataaddr(vv, off+2, u_short *));
-               if (off + resid > len)
+               }
+               vv->vh_type = ntohs(*vvdataaddr(vv, off, u_short *));
+               resid = ntohs(*(vvdataaddr(vv, off+sizeof(u_short), u_short *)));
+               if (off + resid > len) {
+                       vvprintf("vv%d: trailer packet too short\n", unit);
+                       vvprintf("vv%d: off = %d, resid = %d, vvicsr = %b\n",
+                                   unit, off, resid,
+                                   0xffff&(addr->vvicsr), VV_IBITS);
                        goto dropit;
                        goto dropit;
+               }
                len = off + resid;
        } else
                off = 0;
                len = off + resid;
        } else
                off = 0;
-       if (len == 0)
+
+       if (len == 0) {
+               vvprintf("vv%d: len is zero, vvicsr = %b\n", unit,
+                           0xffff&(addr->vvicsr), VV_IBITS);
                goto dropit;
                goto dropit;
-       m = if_rubaget(&vs->vs_ifuba, len, off);
-       if (m == NULL)
+       }
+
+       m = if_rubaget(&vs->vs_ifuba, len, off, &vs->vs_if);
+       if (m == NULL) {
+               vvprintf("vv%d: if_rubaget() failed, vvicsr = %b\n", unit,
+                           0xffff&(addr->vvicsr), VV_IBITS);
                goto dropit;
                goto dropit;
+       }
        if (off) {
        if (off) {
-               m->m_off += 2 * sizeof(u_short);
-               m->m_len -= 2 * sizeof(u_short);
+               struct ifnet *ifp;
+
+               ifp = *(mtod(m, struct ifnet **));
+               m->m_off += 2 * sizeof (u_short);
+               m->m_len -= 2 * sizeof (u_short);
+               *(mtod(m, struct ifnet **)) = ifp;
        }
 
        }
 
+       /* Keep track of source address of this packet */
+       vs->vs_lastr = vv->vh_shost;
+
        /*
        /*
-        * Demultiplex on packet type 
+        * Demultiplex on packet type
         */
        switch (vv->vh_type) {
 
         */
        switch (vv->vh_type) {
 
@@ -690,69 +764,39 @@ vvrint(unit)
                break;
 #endif
        default:
                break;
 #endif
        default:
-               printf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type);
+               vvprintf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type);
                m_freem(m);
                goto setup;
        }
                m_freem(m);
                goto setup;
        }
+       s = splimp();
        if (IF_QFULL(inq)) {
                IF_DROP(inq);
                m_freem(m);
        } else
                IF_ENQUEUE(inq, m);
        if (IF_QFULL(inq)) {
                IF_DROP(inq);
                m_freem(m);
        } else
                IF_ENQUEUE(inq, m);
-setup:
+
+       splx(s);
        /*
        /*
-        * Check the error rate and start recovery if needed
-        * this has to go here since the timer flywheel runs at
-        * a lower ipl and never gets a chance to change the mode
+        * Reset for the next packet.
         */
         */
-       if (vs->vs_major == MODE0 && vs->vs_dropped > VV_ERRORTHRESHOLD) {
-               vvtrprintf("vv%d going MODE1 in vvrint\n",unit);
-               vs->vs_major = MODE1;
-               vs->vs_iactive = PAUSE;         /* no new reads */
-               vs->vs_retry = VV_MODE1ATTEMPTS;
-               vs->vs_delayclock = VV_MODE1DELAY;
-               vs->vs_minor = 0;
-               vs->vs_dropped = 0;
-       }
-       switch (vs->vs_iactive) {
-
-       case ACTIVE:            /* Restart the read for next packet */
-               ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
-               addr->vviba = (u_short) ubainfo;
-               addr->vviea = (u_short) (ubainfo >> 16);
-               addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
-               addr->vvicsr = VV_RST | VV_CONF;
-               addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
-               return;
-
-       case PAUSE:             /* requested to not start any new reads */
-               vs->vs_dropped = 0;
-               return;
-
-       case OPEN:              /* request to open host relay */
-               vs->vs_dropped = 0;
-               addr->vvicsr = 0;
-               return;
+setup:
+       ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
+       addr->vviba = (u_short) ubainfo;
+       addr->vviea = (u_short) (ubainfo >> 16);
+       addr->vviwc = -(VVBUFSIZE) >> 1;
+       addr->vvicsr = VV_HEN | VV_IEN | VV_DEN | VV_ENB;
+       return;
 
 
-       default:
-               printf("vv%d: vs_iactive = %d\n", unit, vs->vs_iactive);
-               return;
-       }
        /*
         * Drop packet on floor -- count them!!
         */
 dropit:
        vs->vs_if.if_ierrors++;
        /*
         * Drop packet on floor -- count them!!
         */
 dropit:
        vs->vs_if.if_ierrors++;
-       vs->vs_dropped++;
-       /*
-       printf("vv%d: error vvicsr = %b\n", unit,
-               0xffff&(addr->vvicsr), VV_IBITS);
-       */
        goto setup;
 }
 
 /*
        goto setup;
 }
 
 /*
- * V2lni output routine.
+ * proNET output routine.
  * Encapsulate a packet of type family for the local net.
  * Use trailer local net encapsulation if enough data in first
  * packet leaves a multiple of 512 bytes of data in remainder.
  * Encapsulate a packet of type family for the local net.
  * Use trailer local net encapsulation if enough data in first
  * packet leaves a multiple of 512 bytes of data in remainder.
@@ -762,39 +806,77 @@ vvoutput(ifp, m0, dst)
        struct mbuf *m0;
        struct sockaddr *dst;
 {
        struct mbuf *m0;
        struct sockaddr *dst;
 {
-       register struct mbuf *m = m0;
+       register struct mbuf *m;
        register struct vv_header *vv;
        register int off;
        register struct vv_header *vv;
        register int off;
-       int type, dest, s, error;
+       register int unit;
+       register struct vvreg *addr;
+       register struct vv_softc *vs;
+       register int s;
+       int type, dest, error;
+
+       m = m0;
+       unit = ifp->if_unit;
+       addr = (struct vvreg *)vvinfo[unit]->ui_addr;
+       vs = &vv_softc[unit];
+
+       /*
+        * Check to see if the input side has wedged due the UBA
+        * vectoring through 0.
+        *
+        * We are lower than device ipl when we enter this routine,
+        * so if the interface is ready with an input packet then
+        * an input interrupt must have slipped through the cracks.
+        *
+        * Avoid the race with an input interrupt by watching to see
+        * if any packets come in.
+        */
+       s = vs->vs_if.if_ipackets;
+       if (addr->vvicsr & VV_RDY && s == vs->vs_if.if_ipackets) {
+               vvprintf("vv%d: lost a receive interrupt, icsr = %b\n",
+                           unit, 0xffff&(addr->vvicsr), VV_IBITS);
+               s = splimp();
+               vvrint(unit);
+               splx(s);
+       }
 
        switch (dst->sa_family) {
 
 #ifdef INET
 
        switch (dst->sa_family) {
 
 #ifdef INET
-       case AF_INET: {
-               dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
-               if ((dest = in_lnaof(*((struct in_addr *)&dest))) >= 0x100) {
+       case AF_INET:
+               if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr))
+                       dest = VV_BROADCAST;
+               else
+                       dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr);
+#ifdef LOOPBACK
+               if (dest == vs->vs_host && (loif.if_flags & IFF_UP))
+                       return (looutput(&loif, m0, dst));
+#endif LOOPBACK
+               if (dest >= 0x100) {
                        error = EPERM;
                        goto bad;
                }
                off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
                        error = EPERM;
                        goto bad;
                }
                off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
+               /*
+                * Trailerize, if the configuration allows it.
+                * TODO: Need per host negotiation.
+                */
                if ((ifp->if_flags & IFF_NOTRAILERS) == 0)
                if (off > 0 && (off & 0x1ff) == 0 &&
                    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
                if ((ifp->if_flags & IFF_NOTRAILERS) == 0)
                if (off > 0 && (off & 0x1ff) == 0 &&
                    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
-                       type = RING_IPTrailer + (off>>9);
+                       type = RING_TRAILER;
                        m->m_off -= 2 * sizeof (u_short);
                        m->m_len += 2 * sizeof (u_short);
                        m->m_off -= 2 * sizeof (u_short);
                        m->m_len += 2 * sizeof (u_short);
-                       *mtod(m, u_short *) = RING_IP;
-                       *(mtod(m, u_short *) + 1) = m->m_len;
+                       *mtod(m, u_short *) = htons((short)RING_IP);
+                       *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
                        goto gottrailertype;
                }
                type = RING_IP;
                off = 0;
                goto gottype;
                        goto gottrailertype;
                }
                type = RING_IP;
                off = 0;
                goto gottype;
-               }
 #endif
        default:
 #endif
        default:
-               printf("vv%d: can't handle af%d\n", ifp->if_unit,
-                       dst->sa_family);
+               printf("vv%d: can't handle af%d\n", unit, dst->sa_family);
                error = EAFNOSUPPORT;
                goto bad;
        }
                error = EAFNOSUPPORT;
                goto bad;
        }
@@ -830,11 +912,11 @@ gottype:
                m->m_len += sizeof (struct vv_header);
        }
        vv = mtod(m, struct vv_header *);
                m->m_len += sizeof (struct vv_header);
        }
        vv = mtod(m, struct vv_header *);
-       vv->vh_shost = ifp->if_host[0];
+       vv->vh_shost = vs->vs_host;
        vv->vh_dhost = dest;
        vv->vh_version = RING_VERSION;
        vv->vh_type = type;
        vv->vh_dhost = dest;
        vv->vh_version = RING_VERSION;
        vv->vh_type = type;
-       vv->vh_info = off;
+       vv->vh_info = htons((u_short)off);
        vvtracehdr("vo", vv);
 
        /*
        vvtracehdr("vo", vv);
 
        /*
@@ -848,8 +930,8 @@ gottype:
                goto qfull;
        }
        IF_ENQUEUE(&ifp->if_snd, m);
                goto qfull;
        }
        IF_ENQUEUE(&ifp->if_snd, m);
-       if (vv_softc[ifp->if_unit].vs_oactive == 0)
-               vvstart(ifp->if_unit);
+       if (vs->vs_oactive == 0)
+               vvstart(unit);
        splx(s);
        return (0);
 qfull:
        splx(s);
        return (0);
 qfull:
@@ -868,20 +950,31 @@ vvioctl(ifp, cmd, data)
        int cmd;
        caddr_t data;
 {
        int cmd;
        caddr_t data;
 {
-       struct ifreq *ifr = (struct ifreq *)data;
+       struct ifaddr *ifa = (struct ifaddr *) data;
        int s = splimp(), error = 0;
 
        switch (cmd) {
 
        case SIOCSIFADDR:
        int s = splimp(), error = 0;
 
        switch (cmd) {
 
        case SIOCSIFADDR:
-               /* too difficult to change addr while running */
-               if ((ifp->if_flags & IFF_RUNNING) == 0) {
-                       struct sockaddr_in *sin =
-                           (struct sockaddr_in *)&ifr->ifr_addr;
-                       ifp->if_net = in_netof(sin->sin_addr);
+               ifp->if_flags |= IFF_UP;
+               if ((ifp->if_flags & IFF_RUNNING) == 0)
                        vvinit(ifp->if_unit);
                        vvinit(ifp->if_unit);
-               } else
-                       error = EINVAL;
+               /*
+                * Did self-test succeed?
+                */
+               if ((ifp->if_flags & IFF_UP) == 0)
+                       error = ENETDOWN;
+                /*
+                 * Attempt to check agreement of protocol address
+                 * and board address.
+                 */
+               switch (ifa->ifa_addr.sa_family) {
+                case AF_INET:
+                       if (in_lnaof(IA_SIN(ifa)->sin_addr) !=
+                           vv_softc[ifp->if_unit].vs_host)
+                               error = EADDRNOTAVAIL;
+                       break;
+               }
                break;
 
        default:
                break;
 
        default:
@@ -893,7 +986,7 @@ vvioctl(ifp, cmd, data)
 
 /*
  * vvprt_hdr(s, v) print the local net header in "v"
 
 /*
  * vvprt_hdr(s, v) print the local net header in "v"
- *     with title is "s"
+ *     with title is "s"
  */
 vvprt_hdr(s, v)
        char *s;
  */
 vvprt_hdr(s, v)
        char *s;
@@ -905,24 +998,4 @@ vvprt_hdr(s, v)
                0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type),
                0xffff & (int)(v->vh_info));
 }
                0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type),
                0xffff & (int)(v->vh_info));
 }
-
-#ifdef notdef
-/*
- * print "l" hex bytes starting at "s"
- */
-vvprt_hex(s, l) 
-       char *s;
-       int l;
-{
-       register int i;
-       register int z;
-
-       for (i=0 ; i < l; i++) {
-               z = 0xff & (int)(*(s + i));
-               printf("%c%c ",
-               "0123456789abcdef"[(z >> 4) & 0x0f],
-               "0123456789abcdef"[z & 0x0f]
-               );
-       }
-}
-#endif
+#endif NVV