[net.sources] Ring Driver for SUN

craig@loki.ARPA (12/18/84)

    This is the cover letter for the sources of the ProNET driver for
the SUN2.  It also makes a good README file.

    The driver distribution contains 5 files:

    vv.4: the manual page (123 lines)
    vv.ms: the installation notes (261 lines)
    vv.h: one of the include files (1 line)
    if_vv.h: the other include file (146 lines)
    if_vv.c: the driver code itself. (1319 lines)

    The distribution itself is in a Bourne shell file, which if
executed, extracts the files from itself.  ("sh -x vv.shar").

    Everything you ought to need to know is in vv.ms, so [nt]roffing
a copy of it should be your first priority.  Vv.4 is the manual page
contains a complete set of driver error messages.  If this still doesn't
satisfy you, try reading if_vv.c -- it is pretty well documented.

    If you want information about the ring net itself, contact
Proteon, Inc.  (617) 655-3340.  You can also reach them by mailing
to acm@mit-borax.

    PLEASE READ THE DISCLAIMER AT THE END OF VV.MS!

    However, if you do have problems, please direct questions to me
directly (not v2lni-people, and emphatically not any USENET mailing
lists -- I'm not directly on USENET).

Craig Partridge
craig@bbn-loki   {ARPA}
craig%bbn-loki@csnet-relay {CSNET}
{ihnp4,decvax,wjh12}!bbncca!craig {USENET}

craig@loki.ARPA (12/18/84)

This is the shell archive file (vv.shar) for the ProNET driver for the SUN2
Just run

	sh -x vv.shar
	
and it will dump the various files in the current directory.

Craig Partridge
craig@bbn-loki [ARPANET]
{ihnp4,decvax,wjh12}!bbncca!craig [USENET]
craig%loki@csnet-relay  [CSNET]

SNIP-HERE--------------------------------------------------------------
echo 'sh - vv.h'
sed 's/^X//' <<'_NO_MORE_TEXT_' > vv.h
X#define NVV 1
_NO_MORE_TEXT_
echo 'sh if_vv.h'
sed 's/^X//' <<'_NO_MORE_TEXT_' > if_vv.h
X/* 
X * Part of the ProNET driver for the SUN (release 1.1) distributed by BBN Labs
X * please see disclaimer statement in if_vv.c and accompanying documentation.
X *
X * $Log:	if_vv.h,v $
X * Revision 1.2  84/12/12  10:33:19  craig
X * add small distribution note
X * 
X * Revision 1.1  84/10/17  12:08:07  craig
X * Initial revision
X * 
X */
X
X/*
X * Local network header for V2LNI Ring
X * This is arbitrated by "V2LNI-PEOPLE@MIT-MC"
X * (aka Joel N. Chiappa)
X */
X
X#define	NEW_BROADCAST		/* new plas for broadcast problem */
X
X
Xstruct vv_header {
X	 /* the first two fields are required by the hardware */
X	u_char	vh_dhost;	/* destination address */
X	u_char	vh_shost;	/* source address */
X	/* the next three fields are the local network header */
X	u_char	vh_version;	/* header version */
X	u_char	vh_type;	/* packet type => protocol number */
X	u_short	vh_info;	/* protocol-specific information */
X};
X
X/* reversed version of header used when you don't want to have to swab */
Xstruct vv_rheader {
X	u_char	vh_shost;
X	u_char	vh_dhost;
X	u_char	vh_type;
X	u_char	vh_version;
X	u_short	vh_info;
X};
X
X#define	RING_VERSION	2	/* current version of v2lni header */
X
X/*
X * Packet types (protocol numbers) in v2lni header
X */
X#define	RING_IP		1
X#define	RING_IPTrailer	2
X#define	RING_IPNTrailer	16
X#define	RING_WHOAMI	0xa5	/* insure some bit transitions */
X
X#ifdef NEW_BROADCAST
X#define	VV_BROADCAST	0xff	/* hardware-defined broadcast address */
X#else
X#define	VV_BROADCAST	0x00	/* hardware-defined broadcast address */
X#endif
X
X/*
X * Proteon V2LNI Hardware definitions
X * register bit definitions - new style
X */
X
X#ifndef HARDSWAB
X
X#define	VV_ENB	01		/* Enable Operation */
X#define	VV_DEN	02		/* Enable DMA */
X#define	VV_HEN	04		/* Host Relay Enable (Rcv) */
X#define	VV_CPB	04		/* Clear Packet Buffer (Xmit) */
X#define	VV_STE	010		/* Self Test Enable (Rcv) */
X#define	VV_UT1	010		/* Unused (Xmit) */
X#define	VV_LPB	020		/* Modem Disable (Rcv) */
X#define	VV_INR	020		/* Initialize Ring (Xmit) */
X#define	VV_RST	040		/* Reset */
X#define	VV_IEN	0100		/* Interrupt Enable */
X#define	VV_RDY	0200		/* Done */
X#define	VV_DPR	0400		/* Data Present (Rcv) */
X#define	VV_RFS	0400		/* Refused (Xmit) */
X#define	VV_NXM	01000		/* Non Existent Memory */
X#define	VV_OVR	02000		/* Overrun */
X#define	VV_ODB	04000		/* Odd Byte (Achtung, mein Fuehrer) (Rcv) */
X#define	VV_UT2	04000		/* Unused (Xmit) */
X#define	VV_LDE	010000		/* Link Data Error (Rcv) */
X#define	VV_OPT	010000		/* Output Timeout (Xmit) */
X#define	VV_NOK	020000		/* Ring Not OK */
X#define	VV_BDF	040000		/* Bad Format in Operation */
X#define	VV_NIR	0100000		/* Not in Ring */
X
X#define VV_IBITS \
X"\10\20NIR\17BDF\16NOK\15LDE\14ODB\13OVR\12NXM\11DPR\10RDY\7IEN\6RST\5LPB\4STE\3HEN\2DEN\1ENB"
X
X#define VV_OBITS \
X"\10\20NIR\17BDF\16NOK\15OPT\13OVR\12NXM\11RFS\10RDY\7IEN\6RST\5INR\3HEN\2DEN\1ENB"
X
X#else /* HARDSWABBING */
X
X#define	VV_ENB	0400		/* Enable Operation */
X#define	VV_DEN	01000		/* Enable DMA */
X#define	VV_HEN	02000		/* Host Relay Enable (Rcv) */
X#define	VV_CPB	02000		/* Clear Packet Buffer (Xmit) */
X#define	VV_STE	04000		/* Self Test Enable (Rcv) */
X#define	VV_UT1	04000		/* Unused (Xmit) */
X#define	VV_LPB	010000		/* Modem Disable (Rcv) */
X#define	VV_INR	010000		/* Initialize Ring (Xmit) */
X#define	VV_RST	020000		/* Reset */
X#define	VV_IEN	040000		/* Interrupt Enable */
X#define	VV_RDY	0100000		/* Done */
X#define	VV_DPR	01		/* Data Present (Rcv) */
X#define	VV_RFS	01		/* Refused (Xmit) */
X#define	VV_NXM	02		/* Non Existent Memory */
X#define	VV_OVR	04		/* Overrun */
X#define	VV_ODB	010		/* Odd Byte (Achtung, mein Fuehrer) (Rcv) */
X#define	VV_UT2	010		/* Unused (Xmit) */
X#define	VV_LDE	020		/* Link Data Error (Rcv) */
X#define	VV_OPT	020		/* Output Timeout (Xmit) */
X#define	VV_NOK	040		/* Ring Not OK */
X#define	VV_BDF	0100		/* Bad Format in Operation */
X#define	VV_NIR	0200		/* Not in Ring */
X
X#define VV_IBITS \
X"\10\20RDY\17IEN\16RST\15LPB\14STE\13HEN\12DEN\11ENB\10NIR\7BDF\6NOK\5LDE\4ODB\3OVR\2NXM\1DPR"
X
X#define VV_OBITS \
X"\10\10NIR\7BDF\6NOK\5OPT\3OVR\2NXM\1RFS\20RDY\17IEN\16RST\15INR\13HEN\12DEN\11ENB"
X
X#endif /* HARDSWABBING */
X
X#define	VVXERR	(VV_NXM|VV_OVR|VV_OPT|VV_BDF)	/* Xmit errs */
X#define	VVRERR	(VV_NXM|VV_OVR|VV_ODB|VV_BDF)	/* Rcv errs */
X#define	VVFE	(VV_NXM|VV_OVR)			/* Fatal errors */
X
X
X/* device registers */
Xstruct vvreg {
X	u_short vvila;		/* input addr (low) */
X	u_short vviha;		/* input addr (hi) */
X	short	vvicsr;		/* input csr */
X	u_short	vviwc;		/* input word count */
X	u_short vvola;		/* output addr (low) */
X	u_short vvoha;		/* output addr (hi) */
X	short	vvocsr;		/* output csr */
X	u_short	vvowc;		/* output word count */
X};
X
X#define	VVRETRY	7		/* output retry limit */
X#define VVIDENTRETRY 10		/* identify loop retry limit */
X#define VVTIMEOUT 60		/* seconds before a transmit timeout */
_NO_MORE_TEXT_
echo 'sh - if_vv.c'
sed 's/^X//' <<'_NO_MORE_TEXT_' > if_vv.c
X/**************************************************************************/
X/*      Proteon 10 Meg Ring Driver. for 4.2 SUN (release 1.1).            */
X/* Derived from Craig Leres' 4.2 VAX driver by Craig Partridge, of Bolt,  */
X/* Beranek and Newman, Cambridge, MA.  Thanks also to Chuck O'Hare of     */
X/* Wisconsin who provided copies of their 4.1c driver.                    */
X/* see disclaimer below....                                               */
X/*                                                                        */
X/* This device is called "vv" because its "real name", V2LNI won't work   */
X/* if shortened to the obvious "v2". Hence the subterfuge.                */
X/*                                                                        */
X/* SUN implementation notes:                                              */
X/*                                                                        */
X/*	oct 84, initial port.  Done on Rev C boards on a SUN2.  Some      */
X/* 	important observations/comments:                                  */
X/*                                                                        */
X/*      (1)  Swabbing is a bizarre problem.  Here's what we found we had  */
X/*        to do....  If the hardware is not wired to swab, then you must  */
X/*        swab the data, but not the registers.  However, if the hardware */
X/*        is swabbing, you leave the data alone but swab the registers.   */
X/*        Apparently SUN's MMU is smart enough to figure out that the     */
X/*        registers are in mbio space, and quietly swabs their values     */
X/*        for you -- which screws when the ProNet board is swabbinb....   */
X/*                                                                        */
X/*      (2)  There seemed no good reason to specify a register address    */
X/*        in the code, so vvstd[] is simply 0.  This doesn't seem to make */
X/*        any difference -- the kernel still finds the device, and it     */
X/*        saves me from worries that SUN will decide to put a new device  */
X/*        in where I decided to put the ring.                             */
X/*                                                                        */ 
X/*      (3)  A caveat to people dealing with the boards.  After           */
X/*        two days of tearing my hair I discovered an undocumented        */
X/*        feature of the ProNet boards -- some registers, in particular   */
X/*        the word count registers, have garbage bits which you have to   */
X/*        mask off.  (vv_iwc and vv_owc are only good in the low 10 bits) */
X/*                                                                        */
X/*      (4)  Speeding this driver is probably possible but likely to be   */
X/*        tricky.  The basic structure of the driver is that of the       */
X/*        Leres driver, which seems to be quite good.  The differences    */
X/*        are mostly a result of dealing with the SUN/Multibus.           */
X/*        If you are searching for speed increases, look at the new code. */
X/*        The most productive place to look, I think, is in emptybuf().   */
X/*        Here you can take advantage of the knowledge that all data is   */
X/*        short aligned, and write a tighter version of bcopy which moves */
X/*        shorts in an unrolled loop.  Such a routine should be about     */
X/*        30% faster than bcopy -- but how much that affects overall      */
X/*        driver thruput wasn't clear, and I didn't feel like testing it  */
X/*                                                                        */
X/*      (5)  The data checking in the driver.  QUICKTEST makes little or  */
X/*        no effect on thruput.  SLOWTEST will, because it does a modifed */
X/*        Internet checksum.  (Modified in that we steal a bit to tell us */
X/*        if the true packet length is odd or even).                      */
X/*                                                                        */
X/*  This software is in the public domain and is being provided free by   */
X/*  BBN Labs to all users of ARPANET and USENET.  BBN disclaims all       */
X/*  express warranties with regard to any use of this software, including */
X/*  all implied warranties of merchandability and fitness.  In no event   */
X/*  shall BBN be liable for any direct, special, indirect or consequential*/
X/*  damages or any damages whatsoever resulting from loss of use, data or */
X/*  profits, or any other injury, whether in an action of contract,       */
X/*  negligence or other tortious action, arising out of or in connection  */
X/*  with the use or performance of this software.                         */
X/**************************************************************************/
X
X/*
X * Additions/Mods Log:
X *
X * $Log:	if_vv.c,v $
X * Revision 1.6  84/12/12  10:29:43  craig
X * add disclaimer
X * 
X * Revision 1.5  84/12/07  10:28:04  craig
X * remove 1.3 fixes.  I have been unable to test them to my
X * satisfaction, and no beta test site has reported them as bugs.
X * if it ain't broke, don't fix it...
X * 
X * Revision 1.4  84/12/03  16:02:20  craig
X * make input error message consistent with VAX
X * 
X * Revision 1.3  84/10/31  15:53:35  craig
X * fixed two very minor bugs in error handling, related
X * to VVMTU and VVBUFSIZE errors
X * 
X * Revision 1.2  84/10/18  10:57:04  craig
X * bug fixes in vvsetaddr and vvinit so addresses
X * treated correctly
X * 
X * Revision 1.1  84/10/17  12:07:02  craig
X * Initial revision
X * 
X */
X
Xstatic char rcsid[] = "$Header: if_vv.c,v 1.6 84/12/12 10:29:43 craig Exp $";
X
X
X/**************************************************************************/
X/* all the following stuff is configuration control -- some definitions   */
X/* affect values in include files.                                        */
X/**************************************************************************/
X
X/**************************************************************************/
X/*      N.B. - if WIRECENTER is defined wrong, it can well break          */
X/*      the hardware!!                                                    */
X/**************************************************************************/
X
X#define	WIRECENTER
X
X/**************************************************************************/
X/*       Is proNet board doing byteswabbing or do we have to?             */
X/**************************************************************************/
X
X#define	HARDSWAB
X
X/**************************************************************************/
X/*       cheap or EXPENSIVE tests to be done on packets??                 */
X/*       DEFINE NONE, ONE OR THE OTHER BUT NOT BOTH!                      */
X/**************************************************************************/
X
X/* #define QUICKTEST */
X/* #define SLOWTEST */
X
X
X/**************************************************************************/
X/*          not clear that all of these includes are necessary            */
X/**************************************************************************/
X
X#include "../machine/pte.h"
X
X#include "../h/param.h"
X#include "../h/systm.h"
X#include "../h/mbuf.h"
X#include "../h/buf.h"
X#include "../h/protosw.h"
X#include "../h/socket.h"
X#include "../h/vmmac.h"
X#include "../h/errno.h"
X#include "../h/ioctl.h"
X
X#include "../net/if.h"
X#include "../net/netisr.h"
X#include "../net/route.h"
X
X#include "../netinet/in.h"
X#include "../netinet/in_systm.h"
X#include "../netinet/ip.h"
X#include "../netinet/ip_var.h"
X
X#include "../sun/mmu.h"
X
X#include "../sundev/mbvar.h"
X
X#include "../sunif/if_vv.h"
X#include "vv.h"
X
X
X#ifdef WIRECENTER
X#define	VV_CONF	VV_HEN		/* drive wire center relay */
X#else
X#define	VV_CONF	VV_STE		/* allow operation without wire center */
X#endif
X
X/**************************************************************************/
X/*    maximum transmission unit defined -- 1536 size is historical        */
X/**************************************************************************/
X
X/* #define VVBUFSIZE     (2036) */
X#define VVBUFSIZE  (1536)
X#define VVMTU	(VVBUFSIZE-sizeof(struct vv_header))
X
X/**************************************************************************/
X/*   debugging and tracing stuff                                          */
X/**************************************************************************/
X
Xint	vv_tracehdr = 0;	/* 1 => trace headers (slowly!!) */
Xint	vv_logreaderrors = 1;	/* 1 => log all read errors */
X
X#define vvtracehdr	if (vv_tracehdr) vvprt_hdr
X
X/**************************************************************************/
X/*   list of driver routines and necessary data definitions for kernel    */
X/**************************************************************************/
X
Xint	vvprobe(), vvattach(), vvreset(), vvinit();
Xint	vvidentify(), vvstart(), vvxint(), vvwatchdog();
Xint	vvrint(), vvoutput(), vvioctl(), vvsetaddr();
Xint	vvintr();
X
Xstruct	mb_device *vvinfo[NVV];
X
Xu_long vvstd[] = 
X{
X    0 
X} ;
X
Xstruct	mb_driver vvdriver =
X{
X    vvprobe, 0, vvattach,  0,	/* probe, slave, setup, start transfer */
X	0, vvintr, vvstd, 0,	/* done, interrupt, addrs, mem addrs */
X	0, "vv", vvinfo, 0,	/* memsize, name, mbinit, controller */
X	0, MDR_OBIO, 0		/* mbcinit, flags, driver */
X} ;
X
X#define	VVUNIT(x)	minor(x)
X
X/**************************************************************************/
X/* Software status of each interface.                                     */
X/*                                                                        */
X/* Each interface is referenced by a network interface structure,         */
X/* vs_if, which the routing code uses to locate the interface.            */
X/* Structure contains the output queue for the interface, its address, ...*/
X/**************************************************************************/
X
Xstruct vv_softc 
X{
X    struct	ifnet vs_if;	/* network-visible interface */
X    short	vs_oactive;	/* is output active */
X    short	vs_tries;	/* transmit current retry count */
X    short	vs_init;	/* number of ring inits */
X    short	vs_nottaken;	/* number of packets refused */
X    short	vs_timeouts;	/* number of transmit timeouts */
X    short vs_olen;	/* output length */
X    int vs_imap;	/* DVMA map for ibuf */
X    int	vs_omap;	/* DVMA map for obuf */
X    struct buf *vs_obuf;	/* output mbuf */
X    struct buf *vs_ibuf;	/* input mbuf */
X}
Xvv_softc[NVV];
X
X
X/**************************************************************************/
X/*                                                                        */
X/* flip the bytes in a u_short.  Has to be done if board is swabbing      */
X/*                                                                        */
X/**************************************************************************/
X
X#ifndef HARDSWAB
X
X#define swabs(s)	((u_short)s)
X
X#else  /* HARDSWABBING */
X
Xu_short swabs(s)
Xregister u_short s;
X{
X    register u_short tmp;
X
X    tmp = (s >> 8);
X    s <<= 8;
X
X    return((tmp|s) & 0xffff);
X}
X#endif /* HARDSWAB */
X
X
X/**************************************************************************/
X/*                                                                        */
X/* flip the bytes in a buffer.  Only has to be done if board not swabbing */
X/* the asm() is assuredly not worth keeping, I just don't have a non      */
X/* swabbing board to test the change on.                                  */
X/*                                                                        */
X/**************************************************************************/
X
X#ifndef HARDSWAB
X
Xflip(buf,buflen)
Xchar *buf;
Xint buflen;	/* cannot be odd */
X{
X    register u_short *p;
X    register u_short word, tmp;
X    register int len;
X
X    p = (u_short *) buf;
X    len = (buflen+1) >> 1;
X
X    while (len > 0)
X    {
X	word = *p;
X	asm("	rorw #8,d7");	/* d7 is word */
X	*p = word;
X	p++;
X	len--;
X    }
X}
X#endif HARDSWAB
X
X
X/**************************************************************************/
X/*                                                                        */
X/*    takes a chain of mbufs and copies the chain into a buffer.          */
X/*    returns the number of bytes put into the buffer.  if board is       */
X/*    not swabbing it also swabs the bytes.                               */
X/*                                                                        */
X/**************************************************************************/
X
Xstatic
Xint fillbuf(buf,mchain)
Xstruct buf *buf;
Xstruct mbuf *mchain;
X{
X    register struct mbuf *m = mchain;
X    register int i = 0;
X    register caddr_t mp,bp;
X
X    bp = buf->b_un.b_addr;
X
X    while (m)
X    {
X	mp = mtod(m,caddr_t);
X	bcopy(mp,bp,m->m_len);
X
X	i += m->m_len;
X
X	bp += m->m_len;
X	m = m->m_next;
X    }
X
X#ifndef HARDSWAB
Xflip(buf->b_un.b_addr,i);
X#endif
X
Xvvtracehdr("output",(struct vv_header *)buf->b_un.b_addr);
X
Xreturn(i);
X}
X
X/**************************************************************************/
X/*                                                                        */
X/* empties a buffer into a chain of mbufs. returns the start of the chain.*/
X/*                                                                        */
X/* this routine is admitted to be hard to understand.  It is doing three  */
X/* things at once: copying the buffer, moving IP trailers to the front of */
X/* the packet and skipping the ring header.  Hope this helps              */
X/*                                                                        */
X/**************************************************************************/
X
Xstatic
Xstruct mbuf *emptybuf(buf,totlen,off0,skip)
Xstruct buf *buf;
Xregister int totlen;	/* bytes to copy */
Xint off0, skip;		/* trailer offset, hdr len */
X{
X    register struct mbuf *m;
X    register caddr_t bp;
X    register struct mbuf **mp;
X    register int len;
X    int off = off0;
X    struct mbuf *top;
X
X#ifndef HARDSWAB
X    flip(buf->b_un.b_addr,totlen+skip);
X#endif
X    
X    vvtracehdr("input",(struct vv_header *)buf->b_un.b_addr);
X
X
X    top = (struct mbuf *) 0;
X    mp = &top;
X    bp = buf->b_un.b_addr + skip;
X
X    while (totlen > 0)
X    {
X	MGET(m,M_DONTWAIT,MT_DATA);
X	if (m == (struct mbuf *)0)
X	    goto bad;
X
X	if (off)
X	{
X	    len = totlen - off;
X	    bp = buf->b_un.b_addr + off + skip;
X	}
X	else
X	    len = totlen;
X
X	m->m_len = MIN(MLEN,len);
X	m->m_off = MMINOFF;
X	m->m_next = (struct mbuf *)0;
X
X	bcopy(bp,mtod(m,caddr_t),m->m_len);
X	bp += m->m_len;
X
X	*mp = m;
X	mp = &m->m_next;
X
X	if (off)
X	{
X	    off += m->m_len;
X	    if (off == totlen)
X	    {
X		bp = buf->b_un.b_addr + skip;
X		off = 0;
X		totlen = off0;
X	    }
X	}
X	else
X	    totlen -= m->m_len;
X    }
X    return(top);
X
Xbad :
X    if (top != (struct mbuf *)0)
X	m_freem(top);
X    return((struct mbuf *)0);
X}
X
X/**************************************************************************/
X/*                                                                        */
X/*              A weak probe routine........                              */
X/*                                                                        */
X/**************************************************************************/
X
Xvvprobe(reg)
Xcaddr_t reg;
X{
X    register struct vvreg *addr;
X
X    addr = (struct vvreg *)reg;
X
X    /* are the registers there? */
X    if ((peek(&addr->vvicsr) == -1) || (peek(&addr->vvocsr) == -1))
X	return(0);
X
X    /* reset interface, enable, and wait till dust settles */
X
X    addr->vvicsr = VV_RST;
X    addr->vvocsr = VV_RST;
X    DELAY(100000);
X
X    addr->vvocsr = 0;
X    return(sizeof(struct vvreg));
X}
X
X/**************************************************************************/
X/*                                                                        */
X/* Interface exists: make available by filling in network interface       */
X/* record.  System will initialize the interface when it is ready         */
X/* to accept packets.                                                     */
X/*                                                                        */
X/**************************************************************************/
X
Xvvattach(md)
Xstruct mb_device *md;
X{
X    register struct vv_softc *vs;
X
X    vs = &vv_softc[md->md_unit];
X
X    vs->vs_if.if_unit = md->md_unit;
X    vs->vs_if.if_name = "vv";
X    vs->vs_if.if_mtu = VVMTU;
X    vs->vs_if.if_init = vvinit;
X    vs->vs_if.if_ioctl = vvioctl;
X    vs->vs_if.if_output = vvoutput;
X    vs->vs_if.if_reset = vvreset;
X    vs->vs_if.if_timer = 0;
X    vs->vs_if.if_watchdog = vvwatchdog;
X
X    /* no bufs yet */
X    vs->vs_obuf = vs->vs_ibuf = (struct buf *)0;
X
X    if_attach(&vs->vs_if);
X}
X
X/**************************************************************************/
X/*                                                                        */
X/*     reset of interface after MULTIBUS reset                            */
X/*                                                                        */
X/**************************************************************************/
X
Xvvreset(unit)
Xint unit;
X{
X    register struct mb_device *md;
X
X    if (unit >= NVV || (md = vvinfo[unit]) == 0 || md->md_alive == 0)
X	return;
X
X    printf(" vv%d", unit);
X    vvinit(unit);
X}
X
X
X/**************************************************************************/
X/*                                                                        */
X/*      Initialization of interface; clear recorded pending               */
X/*      operations, and start any pending writes.                         */
X/*                                                                        */
X/**************************************************************************/
X
Xvvinit(unit)
Xint unit;
X{
X    register struct vv_softc *vs;
X    register struct mb_device *md;
X    register struct vvreg *addr;
X    register struct sockaddr_in *sin;
X    register int s;
X    int mbaddr;
X
X    vs = &vv_softc[unit];
X    md = vvinfo[unit];
X    sin = (struct sockaddr_in *)&vs->vs_if.if_addr;
X
X    /* If the network number is still zero, then called too soon. */
X
X    if (in_netof(sin->sin_addr) == 0)
X	return;
X
X    addr = (struct vvreg *)md->md_addr;
X
X    /* allocate the buffers for the device */
X
X    if (vs->vs_obuf == (struct buf *)0)
X    {
X	vs->vs_obuf = geteblk(VVBUFSIZE);
X	vs->vs_omap = mbsetup(md->md_hd,vs->vs_obuf,MB_CANTWAIT);
X    }
X
X    if (vs->vs_ibuf == (struct buf *)0)
X    {
X	vs->vs_ibuf = geteblk(VVBUFSIZE);
X	vs->vs_imap = mbsetup(md->md_hd,vs->vs_ibuf,MB_CANTWAIT);
X    }
X
X    /* vvidentify sends out broadcast packet to find addr */
X
X    if ((vs->vs_if.if_host[0] = vvidentify(unit)) == 0)
X    {
X	vs->vs_if.if_flags &= ~IFF_UP;
X
X	/* free buffers in case it's device problems */
X	mbrelse(md->md_hd,vs->vs_ibuf,&(vs->vs_imap));
X	brelse(vs->vs_ibuf);
X	mbrelse(md->md_hd,vs->vs_obuf,&(vs->vs_omap));
X	brelse(vs->vs_obuf);
X	vs->vs_ibuf = vs->vs_obuf = (struct buf *)0;
X
X	return;
X    }
X
X    printf("vv%d: host %d\n",unit,vs->vs_if.if_host[0]);
X
X    sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]);
X
X    /* reset and join ring */
X
X    addr->vvocsr = VV_RST | VV_CPB;	/* clear packet buffer */
X    addr->vvicsr = VV_RST | VV_CONF;	/* close logical relay */
X    DELAY(500000);	/* let contacts settle */
X
X    vs->vs_init = 0;
X    vs->vs_nottaken = 0;
X    vs->vs_timeouts = 0;
X
X    /* Hang a receive and start any pending writes by
X       faking a transmit complete.  */
X
X    s = splimp();
X
X    mbaddr = MBI_ADDR(vs->vs_imap);
X
X    addr->vvila = swabs(mbaddr & 0xffff);
X    addr->vviha = swabs((mbaddr >> 16) & 0x0ff);
X    addr->vviwc = swabs(VVBUFSIZE >> 1);
X    addr->vvicsr = VV_IEN | VV_CONF | VV_DEN | VV_ENB;
X    vs->vs_oactive = 1;
X    vs->vs_if.if_flags |= IFF_UP | IFF_RUNNING;
X    vvxint(unit);
X
X    splx(s);
X
X    if_rtinit(&vs->vs_if, RTF_UP);
X}
X
X/**************************************************************************/
X/*                                                                        */
X/*    Return our host address.                                            */
X/*                                                                        */
X/**************************************************************************/
X
Xvvidentify(unit)
Xint unit;
X{
X    register struct vv_softc *vs;
X    register struct vvreg *addr;
X#ifndef HARDSWAB
X    register struct vv_rheader *v;
X#else
X    register struct vv_header *v;
X#endif
X    register int attempts, waitcount;
X    u_long i_mbaddr, o_mbaddr;
X    u_short shost = 0;
X
X    /* Build a message to identify our address */
X
X    vs = &vv_softc[unit];
X    addr = (struct vvreg *)vvinfo[unit]->md_addr;
X    attempts = 0;	/* total attempts, including bad msg type */
X
X    v = (struct vv_header *)(vs->vs_obuf->b_un).b_addr;
X    v->vh_dhost = VV_BROADCAST;	/* multicast destination address */
X    v->vh_shost = 0;	/* will be overwritten with ours */
X    v->vh_version = RING_VERSION;
X    v->vh_type = RING_WHOAMI;
X    v->vh_info = 0;
X
X    vs->vs_olen = sizeof(struct vv_header) >> 1;
X
X    i_mbaddr = MBI_ADDR(vs->vs_imap);
X    o_mbaddr = MBI_ADDR(vs->vs_omap);
X
X    /*
X     * Reset interface, establish Digital Loopback Mode, and
X     * send the multicast (to myself) with Input Copy enabled.
X     */
X
Xretry :
X    /* enable input */
X
X    addr->vvicsr = VV_RST;
X    addr->vvila = swabs(i_mbaddr & 0xffff);
X    addr->vviha = swabs((i_mbaddr >> 16) & 0x0ff);
X    addr->vviwc = swabs(VVBUFSIZE >> 1);
X    addr->vvicsr = VV_STE|VV_LPB|VV_DEN|VV_ENB;
X
X
X    /* let flag timers fire so ring will initialize */
X    DELAY(2000000);	/* about 2 SECONDS on a 780!! */
X
X    /* o.k. now send our output packet */
X    addr->vvocsr = VV_RST | VV_CPB;	/* clear packet buffer */
X    addr->vvola = swabs(o_mbaddr & 0xffff);
X    addr->vvoha = swabs((o_mbaddr >> 16) & 0x0ff);
X    addr->vvowc = swabs(vs->vs_olen);
X    addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB;
X
X    /* POLL receive side to finish */
X
X    DELAY(10000);
X
X    for (waitcount = 0; waitcount < 10; waitcount++)
X    {
X	if (addr->vvicsr & VV_RDY)
X	     goto gotit;
X	DELAY(1000);
X    }
X
X    if (attempts++ < VVIDENTRETRY)
X	goto retry;
X
X    /* FAILED */
X
X    printf("vv%d: can't initialize after %d tries, icsr = %b\n",
X	unit, VVIDENTRETRY, 0xffff & addr->vvicsr, VV_IBITS);
X    return(0);
X
Xgotit: /* SUCCESS ?? */
X
X    v = (struct vv_header *)(vs->vs_ibuf->b_un).b_addr;
X
X    /* proper message type */
X
X    if (v->vh_type == RING_WHOAMI)
X	shost = v->vh_shost;
X    else
X	goto retry;
X
X    return(shost);
X}
X
X/**************************************************************************/
X/*       Start or restart output on interface.                            */
X/*                                                                        */
X/* If interface active, then a retransmit, just restuff registers and go. */
X/*                                                                        */
X/* If interface not already active, get another datagram off the interface*/
X/* queue, copy to buffer and send it.                                     */
X/**************************************************************************/
X
Xvvstart(dev)
Xdev_t dev;
X{
X    register struct mb_device *md;
X    register struct vv_softc *vs;
X    register struct vvreg *addr;
X    register struct mbuf *m;
X    int unit, s;
X    u_long mbaddr;
X
X    unit = VVUNIT(dev);
X    md = vvinfo[unit];
X    vs = &vv_softc[unit];
X
X    /* active? */
X    if (vs->vs_oactive)
X	goto restart;
X
X    /* not active, try dequeueing a new message */
X
X    s = splimp();
X    IF_DEQUEUE(&vs->vs_if.if_snd, m);
X    splx(s);
X
X    /* any message? */
X    if (m == NULL) 
X    {
X	vs->vs_oactive = 0;
X	return;
X    }
X
X    /* prepare to send */
X
X    vs->vs_olen = fillbuf(vs->vs_obuf, m);
X    m_freem(m);
X
X    /* implement packet checking */
X#if defined(QUICKTEST)
X    {
X	struct vv_header *vv = (struct vv_header *)vs->vs_obuf->b_un.b_addr;
X
X	vv->vh_info =  (vs->vs_olen+1)>>1;	/* just length */
X
X#  ifndef HARDSWAB
X        vv->vh_info = ((vv->vh_info>>8) & 0xff) | ((vv->vh_info<<8) & 0xff00);
X#  endif HARDSWAB
X    }
X#endif defined(QUICKTEST)
X
Xrestart :
X
X    /* Make sure this packet will fit in the interface. */
X
X    if (vs->vs_olen > VVBUFSIZE)
X    {
X 	printf("vv%d vs_olen: %d > VVMTU\n", unit, vs->vs_olen);
X	panic("vvdriver vs_olen botch");
X    }
X
X    vs->vs_if.if_timer = VVTIMEOUT;
X    vs->vs_oactive = 1;
X
X    /* ship it */
X    addr = (struct vvreg *)md->md_addr;
X    mbaddr = MBI_ADDR(vs->vs_omap);
X
X    addr->vvola = swabs(mbaddr & 0xffff);
X    addr->vvoha = swabs((mbaddr >> 16) & 0x0ff);
X    addr->vvowc = swabs(((vs->vs_olen + 1) >> 1));
X    addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB;
X}
X
X/**************************************************************************/
X/*                                                                        */
X/*   called on interrupt -- figures out who did it and calls              */
X/*   appropriate interrupt routine.                                       */
X/*                                                                        */
X/**************************************************************************/
X
Xvvintr()
X{
X    register struct mb_device *md;
X    register struct vvreg *addr;
X    register int found = 0;
X
X    /* assumes one ring board -- if more will have to loop to poll regs */
X    md = vvinfo[0];
X    addr = (struct vvreg *)md->md_addr;
X
X    /* is this output?? */
X    if ((addr->vvocsr & VV_RDY) && (addr->vvocsr & VV_RST))
X	{
X	vvxint(md->md_unit);
X	found = 1;
X	}
X
X    /* try input */
X    if ((addr->vvicsr & VV_RDY) && (addr->vvicsr & VV_RST))
X	{
X	vvrint(md->md_unit);
X	found = 1;
X	}
X
X    return(found);
X}
X
X/**************************************************************************/
X/*                                                                        */
X/* transmit interrupt -- start new output if more data available          */
X/*                                                                        */
X/**************************************************************************/
X
Xvvxint(unit)
Xint unit;
X{
X    register struct mb_device *md;
X    register struct vv_softc *vs;
X    register struct vvreg *addr;
X    register int oc;
X
X    md = vvinfo[unit];
X    vs = &vv_softc[unit];
X    vs->vs_if.if_timer = 0;
X    addr = (struct vvreg *)md->md_addr;
X
X    oc = 0xffff & (addr->vvocsr);
X    addr->vvocsr = VV_RST;
X
X    if (vs->vs_oactive == 0) 
X    {
X	printf("vv%d: stray interrupt vvocsr = %b\n", unit,
X	    oc, VV_OBITS);
X	return;
X    }
X
X    if (oc &  (VV_OPT | VV_RFS)) 
X    {
X	vs->vs_if.if_collisions++;
X	if (vs->vs_tries++ < VVRETRY) 
X	{
X	    if (oc & VV_OPT)
X		vs->vs_init++;
X	    if (oc & VV_RFS)
X		vs->vs_nottaken++;
X	    vvstart(unit);	/* restart this message */
X	    return;
X	}
X	if (oc & VV_OPT)
X	    printf("vv%d: output timeout\n", unit);
X    }
X
X    vs->vs_if.if_opackets++;
X    vs->vs_oactive = 0;
X    vs->vs_tries = 0;
X
X    if (oc & VVXERR) 
X    {
X	vs->vs_if.if_oerrors++;
X	printf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc,
X	    VV_OBITS);
X    }
X
X    vvstart(unit);
X}
X
X/**************************************************************************/
X/*                                                                        */
X/* Transmit watchdog timer routine. This routine gets called when we lose */
X/* a transmit interrupt. The best we can do is try to restart output.     */
X/*                                                                        */
X/**************************************************************************/
X
Xvvwatchdog(unit)
Xint unit;
X{
X    register struct vv_softc *vs;
X    register int s;
X
X    vs = &vv_softc[unit];
X    if (vs->vs_if.if_flags & IFF_DEBUG)
X	printf("vv%d: lost a transmit interrupt.\n", unit);
X    vs->vs_timeouts++;
X
X    s = splimp();
X    vvstart(unit);
X    splx(s);
X}
X
X/**************************************************************************/
X/* receive interrupt.  If error drop packet, else peek at packet header   */
X/* to determine type.  If we want it then strip local header and pass     */
X/* on to higher protocol.                                                 */
X/**************************************************************************/
X
Xvvrint(unit)
Xint unit;
X{
X    register struct vv_softc *vs;
X    register struct vvreg *addr;
X#ifdef HARDSWAB
X    register struct vv_header *vv;
X#else
X    register struct vv_rheader *vv;
X#endif
X    register struct ifqueue *inq;
X    register struct mbuf *m;
X    register int len, s, off, type;
X    u_long mbaddr;
X    short resid;
X
X    vs = &vv_softc[unit];
X    vs->vs_if.if_ipackets++;
X    addr = (struct vvreg *)vvinfo[unit]->md_addr;
X
X    if (addr->vvicsr & VVRERR) 
X    {
X	if (vv_logreaderrors && vs->vs_if.if_flags & IFF_DEBUG)
X	    printf("vv%d: error vvicsr = %b\n", unit,
X	    0xffff&(addr->vvicsr), VV_IBITS);
X	goto dropit;
X    }
X
X    /* get length */
X
X#ifdef HARDSWAB
X    vv = (struct vv_header *)(vs->vs_ibuf->b_un.b_addr);
X#else
X    vv = (struct vv_rheader *)(vs->vs_ibuf->b_un.b_addr);
X#endif
X
X    /* real nuisance -- only low order 10 bits of iwc are valid */
X    resid = swabs(addr->vviwc) & 0x3ff;
X
X    len = VVBUFSIZE - (resid << 1);
X    len -= sizeof(struct vv_header);
X
X    if (len > VVBUFSIZE || len <= 0) 
X    {
X	if (vv_logreaderrors && vs->vs_if.if_flags & IFF_DEBUG)
X	    printf("vv%d: len too big, len = %d, vvicsr = %b\n",
X	    unit, len, 0xffff&(addr->vvicsr), VV_IBITS);
X	goto dropit;
X    }
X
X    /* untested trailer handling -- taken straight from VAX code */
X
X    off = 0;
X
X#define	vvdataaddr(vv, off, type)	((type)(((caddr_t)((vv)+1)+(off))))
X    if (vv->vh_type >= RING_IPTrailer &&
X	vv->vh_type < RING_IPTrailer+RING_IPNTrailer) 
X    {
X	off = (vv->vh_type - RING_IPTrailer) * 512;
X	if (off > (VVBUFSIZE-12)) 
X	{
X	    if (vv_logreaderrors && vs->vs_if.if_flags & IFF_DEBUG)
X		printf("vv%d: VVMTU, off = %d, vvicsr = %b\n",
X		unit, off, 0xffff&(addr->vvicsr), VV_IBITS);
X	    goto dropit;
X	}
X	vv->vh_type = *vvdataaddr(vv, off, u_short *);
X	resid = *(vvdataaddr(vv, off+2, u_short *));
X	if (off + resid > len) 
X	{
X	    if (vv_logreaderrors && vs->vs_if.if_flags & IFF_DEBUG)
X		printf(
X		"vv%d: off = %d, resid = %d, vvicsr = %b\n",
X		unit, off, resid,
X		0xffff&(addr->vvicsr), VV_IBITS);
X	    goto dropit;
X	}
X	len = off + resid;
X    }
X#ifdef QUICKTEST
X    /* can only test non-trailer packets */
X    else if ((vv->vh_info != 0) &&
X		 (vv->vh_info != ((len+sizeof(struct vv_header)) >> 1)))
X    {
X	if (vv_logreaderrors)
X	    printf("vv%d: incomplete packet\n",unit);
X	goto dropit;
X    }
X
X#endif QUICKTEST
X
X    if (len == 0) 
X    {
X	if (vv_logreaderrors && vs->vs_if.if_flags & IFF_DEBUG)
X	    printf("vv%d: len is zero, vvicsr = %b\n", unit,
X	    0xffff&(addr->vvicsr), VV_IBITS);
X	goto dropit;
X    }
X
X
X    /* Keep track of source address of this packet */
X    type = vv->vh_type;
X
X    /* copy buffer into mbuf chain, without net header */
X    /* could wait until type is o.k., but almost certainly is so why wait */
X
X    m = emptybuf(vs->vs_ibuf,len,off,sizeof(struct vv_header));
X
X    if (m == 0)
X	goto dropit;
X
X#ifdef SLOWTEST
X    if (vv->vh_info)
X    {
X	u_short sav_cksum, cksum;
X	short isodd;
X
X	sav_cksum = vv->vh_info;
X
X	isodd = (sav_cksum & 1);
X	sav_cksum &= 0xfffe;
X
X	if (isodd)
X	    cksum = in_cksum(m,len-1);
X	else
X	    cksum = in_cksum(m,len);
X
X	cksum &= 0xfffe;
X
X	if (sav_cksum != cksum)
X	{
X	    if (vv_logreaderrors)
X		printf("vv%d: garbled packet data\n",unit);
X	    m_freem(m);
X	    goto dropit;
X	}
X    }
X#endif SLOWTEST
X
X    /* handle according to type */
X
X    switch (type)
X    {
X
X#ifdef INET
X      case RING_IP:
X	inq = &ipintrq;
X
X	s = splimp();
X	if (IF_QFULL(inq)) 
X	{
X	    IF_DROP(inq);
X	    m_freem(m);
X	}
X	else
X	    IF_ENQUEUE(inq, m);
X	splx(s);
X
X	schednetisr(NETISR_IP);
X
X	break;
X#endif
X      default:
X	printf("vv%d: unknown pkt type 0x%x\n", unit, type);
X	m_freem(m);
X	goto setup;
X    }
X
Xsetup :
X    /* Reset for the next packet. */
X
X    mbaddr = MBI_ADDR(vs->vs_imap);
X
X    addr->vvila = swabs(mbaddr & 0xffff);
X    addr->vviha = swabs((mbaddr >> 16) & 0x0ff);
X    addr->vviwc = swabs(VVBUFSIZE >> 1);
X    addr->vvicsr = VV_RST | VV_CONF | VV_IEN | VV_DEN | VV_ENB;
X
X    return;
X
Xdropit :
X    /* count dropped packets */
X
X    vs->vs_if.if_ierrors++;
X    goto setup;
X}
X
X/**************************************************************************/
X/*                    v2nli output routine                                */
X/**************************************************************************/
X
Xvvoutput(ifp, m0, dst)
Xstruct ifnet *ifp;
Xstruct mbuf *m0;
Xstruct sockaddr *dst;
X{
X    register struct mbuf *m;
X    register struct vv_header *vv;
X    register int unit, s;
X    register struct vvreg *addr;
X    register struct vv_softc *vs;
X    int type, dest, error;
X    u_short info = 0;
X
X    m = m0;
X    unit = ifp->if_unit;
X    addr = (struct vvreg *)vvinfo[unit]->md_addr;
X    vs = &vv_softc[unit];
X
X    /*
X     * Check to see if the input side has wedged.
X     *
X     * We are lower than device ipl when we enter this routine,
X     * so if the interface is ready with an input packet then
X     * an input interrupt must have slipped through the cracks.
X     *
X     * Avoid the race with an input interrupt by watching to see
X     * if any packets come in.
X     */
X
X    s = vs->vs_if.if_ipackets;
X
X    if (addr->vvicsr & VV_RDY && s == vs->vs_if.if_ipackets) 
X    {
X	if (vs->vs_if.if_flags & IFF_DEBUG)
X	    printf("vv%d: lost a receive interrupt, icsr = %b\n",
X	    unit, 0xffff&(addr->vvicsr), VV_IBITS);
X	s = splimp();
X	vvrint(unit);
X	splx(s);
X    }
X
X    switch (dst->sa_family) 
X    {
X
X#ifdef INET
X      case AF_INET:
X	dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
X	if ((dest = in_lnaof(*((struct in_addr *)&dest))) >= 0x100) 
X	{
X	    error = EPERM;
X	    goto bad;
X	}
X	type = RING_IP; /* NO TRAILERS OUTBOUND */
X	break;
X
X#endif
X      default:
X	printf("vv%d: can't handle af%d\n", unit, dst->sa_family);
X	error = EAFNOSUPPORT;
X	goto bad;
X    }
X
X    /* if checksuming, do it now */
X#ifdef SLOWTEST
X    {
X	int len;
X	struct mbuf *m1;
X
X	for (len=0, m1 = m; m1 != (struct mbuf *)0; m1 = m1->m_next)
X	    len += m1->m_len;
X	
X	info = in_cksum(m,len) & 0xfffe;
X	
X	if (len & 1)
X	    info |= 1;
X
X    }
X#endif SLOWTEST
X
X    /* add local net header.  If no space in first mbuf, allocate another. */
X
X    if (m->m_off > MMAXOFF ||
X	MMINOFF + sizeof (struct vv_header) > m->m_off) 
X    {
X	m = m_get(M_DONTWAIT, MT_HEADER);
X	if (m == (struct mbuf *)0) 
X	{
X	    error = ENOBUFS;
X	    goto bad;
X	}
X	m->m_next = m0;
X	m->m_off = MMINOFF;
X	m->m_len = sizeof (struct vv_header);
X    }
X    else 
X    {
X	m->m_off -= sizeof (struct vv_header);
X	m->m_len += sizeof (struct vv_header);
X    }
X
X    vv = mtod(m, struct vv_header *);
X    vv->vh_shost = ifp->if_host[0];
X
X    /* Map the destination address if it's a broadcast */
X    if ((vv->vh_dhost = dest) == INADDR_ANY)
X	vv->vh_dhost = VV_BROADCAST;
X    vv->vh_version = RING_VERSION;
X    vv->vh_type = type;
X    vv->vh_info = info;	/* this gets changed if testing packets */
X
X    /* queue packet, and if interface not active, send */
X
X    s = splimp();
X    if (IF_QFULL(&ifp->if_snd)) 
X    {
X	IF_DROP(&ifp->if_snd);
X	error = ENOBUFS;
X	goto qfull;
X    }
X    IF_ENQUEUE(&ifp->if_snd, m);
X    if (vs->vs_oactive == 0)
X	vvstart(unit);
X    splx(s);
X
X    return (0);
X
Xqfull :
X    m0 = m;
X    splx(s);
X
Xbad :
X    m_freem(m0);
X    return(error);
X}
X
X/**************************************************************************/
X/*                                                                        */
X/*   process an ioctl request. the value of parameters different on SUN   */
X/*   from VAX.  data now appears to be dependent on the cmd               */
X/*                                                                        */
X/**************************************************************************/
X
Xvvioctl(ifp, cmd, data)
Xregister struct ifnet *ifp;
Xint cmd;
Xcaddr_t data;
X{
X    register struct sockaddr *sin;
X    register int s;
X    int error;
X
X    error = 0;
X    s = splimp();
X
X    switch (cmd) 
X    {
X
X      case SIOCSIFADDR:
X
X	sin = (struct sockaddr *)data;
X	if (sin->sa_family != AF_INET)
X	{
X	    error = EINVAL;
X	    break;
X	}
X
X	if (ifp->if_flags & IFF_RUNNING)
X	    if_rtinit(ifp, -1);	/* delete previous route */
X	vvsetaddr(ifp, (struct sockaddr_in *)sin);
X	if (ifp->if_flags & IFF_RUNNING)
X	    if_rtinit(ifp, RTF_UP);
X	else
X	    vvinit(ifp->if_unit);
X	break;
X
X      default:
X	error = EINVAL;
X    }
X
X    splx(s);
X    return(error);
X}
X
X/**************************************************************************/
X/*                                                                        */
X/* set up the address for this interface. uses the network number         */
X/* from the passed address and an invalid host number;  vvidentify()      */
X/* figures out and inserts real host addr later.                          */
X/*                                                                        */
X/**************************************************************************/
X
Xvvsetaddr(ifp, sin)
Xregister struct ifnet *ifp;
Xregister struct sockaddr_in *sin;
X{
X
X    ifp->if_net = in_netof(sin->sin_addr);
X    ifp->if_host[0] = 0;	/* an invalid host number */
X    sin = (struct sockaddr_in *)&ifp->if_addr;
X    sin->sin_family = AF_INET;
X    sin->sin_addr = if_makeaddr(ifp->if_net, ifp->if_host[0]);
X
X    sin = (struct sockaddr_in *)&ifp->if_broadaddr;
X    sin->sin_family = AF_INET;
X    sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
X    ifp->if_flags |= IFF_BROADCAST;
X}
X
X/**************************************************************************/
X/*                                                                        */
X/* vvprt_hdr(s, v) print the local net header in "v" with title "s"       */
X/*                                                                        */
X/**************************************************************************/
X
Xvvprt_hdr(s, v)
Xchar *s;
X#ifdef HARDSWAB
Xregister struct vv_header *v;
X#else
Xregister struct vv_rheader *v;
X#endif
X{
X    printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
X	s,
X	0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost),
X	0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type),
X	0xffff & (int)(v->vh_info));
X}
X
X#ifdef VVDEBUG
X
X/**************************************************************************/
X/*                                                                        */
X/* print "l" hex bytes starting at "s" -- not used in driver but may be   */
X/* useful for future debugging.                                           */
X/*                                                                        */
X/**************************************************************************/
X
Xvvprt_hex(s, l)
Xchar *s;
Xint l;
X{
X    register int i;
X    register int z;
X
X    for (i=0 ; i < l; i++) 
X    {
X	z = 0xff & (int)(*(s + i));
X	printf("%c%c ",
X	    "0123456789abcdef"[(z >> 4) & 0x0f],
X	    "0123456789abcdef"[z & 0x0f]
X	    );
X    }
X}
X#endif VVDEBUG
_NO_MORE_TEXT_
echo 'sh - vv.4'
sed 's/^X//' <<'_NO_MORE_TEXT_' > vv.4
X.TH VV 4 "15 November 1984"
X.ds ]W Bolt Beranek and Newman
X.SH NAME
Xvv \- Proteon proNET 10 Megabit ring
X.SH SYNOPSIS
X.B "device vv0 at mb0 csr 400 priority 3"
X.SH DESCRIPTION
XThe
X.I vv
Xinterface provides access to a 10 Mb/s Proteon proNET ring network.
X.PP
XThe network number to which the interface is attached must
Xbe specified with an SIOCSIFADDR ioctl before data can be
Xtransmitted or received.
XThe host's address is discovered by putting the interface in
Xdigital loopback mode (not joining the ring)
Xand sending a broadcast packet from which the source
Xaddress is extracted.
X.PP
XIf the installation is running CTL boards which use the old broadcast
Xaddress of 0 instead of the new address of 0xff, the define OLD_BROADCAST
Xshould be specified in the driver.
X.PP
XIf the installation has a Wirecenter, the define WIRECENTER should
Xbe specified in the driver.
X.BR N.B. :
XIncorrect definition of WIRECENTER can cause hardware damage.
X.PP
XOn the SUN processors either the driver or the boards must swap bytes
Xin each packet before it is sent (this is because of a disagreement
Xbetween the SUN processor and the Multibus about byte ordering).
XIf the boards will do byte swapping then the definition HARDSWAB should
Xappear in the driver, otherwise the driver will swap bytes before
Xsending them to the boards.
X.PP
XAlso, because of requests from users with early revision boards, it
Xis possible to have the driver do various simple checks on each
Xpacket as it is received from the board.  These checks are
Xdone by stuffing a value into the third word of the ring header
X(which is normally supposed to be 0).  To make these checks work,
Xall drivers must use them, since the receiving driver simply checks
Xdata put in the third word by the sending driver.  If QUICKTEST is
Xdefined in the driver then the word will contain the number of words
Xthat were sent.  If SLOWTEST is defined then the word contains an
XIP checksum which makes things very slow, but makes sure that no
Xgarbage packets get passed on, a check which is useful when running
X\fBnd\fP (see \fInd(4)\fP).  Note that because the third word in the
Xring header is also used to implement trailer packets, packets with
Xtrailers cannot be checked.  Also note that the definitions have
Xno effect if the third word is 0 (such packets are assumed to come
Xfrom machines that are not doing any checking) and that the definitions
Xmust be consistent among all machines or they will reject each other's
Xpackets.  Sites are encouraged to use \fIneither\fP definition
Xunless their hardware proves to have problems.
X.PP
XIn the SUN implementation, the driver will not generate packets
Xusing the trailer protocol, but will accept them.
X.SH DIAGNOSTICS
X.PP
X\fBvv%d: host %d\fP.  The software announces the host
Xaddress discovered during autoconfiguration.
X.PP
X\fBvv%d: can't initialize after %d tries, icsr=%b\fP.
XThe software was unable to discover the address of this interface,
Xso it is deemed dead and will not be enabled.
X.PP
X\fBvv%d: error vvocsr=%b\fP.  The hardware indicated an error on
Xthe previous transmission.
X.PP
X\fBvv%d: output timeout\fP.  The token timer has fired and the
Xtoken will be recreated.
X.PP
X\fBvv%d: error vvicsr=%b\fP.  The hardware indicated an error
Xin reading a packet off the ring.
X.PP
X\fBvv%d: can't handle af%d\fP.  The interface was handed
Xa message with addresses formatted in an unsuitable address
Xfamily; the packet was dropped.
X.PP
X\fBvv%d: vs_olen: %d > VVMTU\fP.  The ring output routine has been
Xhanded a message with a preposterous length.  This results in
Xan immediate 
X.IR "panic: vs_olen" .
X.PP
X\fBvv%d: stray interrupt vvocsr = %b\fP.  An output
Xinterrupt has been received but the driver was not in the process
Xof sending any packets.
X.PP
X\fBvv%d: lost a transmit interrupt\fP.  Self explanatory.
X.PP
X\fBvv%d: len too big, len = %d\fP. A packet larger than the MTU
Xhas been received.
X.PP
X\fBvv%d: VVMTU, off %d, vvicsr = %b\fP.  The offset into a trailer
Xpacket is too large.
X.PP
X\fBvv%d: len is zero\fP. A zero length packet arrived.
X.PP
X\fBvv%d: unknown pkt type 0x%x\fP or \fBvv%d: can't handle af%d\fP.
XA packet using an unsupported protocol has been seen.
X.PP
X\fBvv%d: incomplete packet\fP.  An error only if QUICKTEST
Xis on.  A packet has arrived with an incorrect size.
X.PP
X\fBvv%d: garbled packet data\fP.  An error only if SLOWTEST
Xis on.  A packet has failed its checksum.
X.PP
X\fBvv%d: off = %d, resid = %d, vvicsr = %b\fP.  A bad trailer
Xpacket has been received.
X.SH SEE ALSO
Xintro(4N), inet(4F), nd(4P)
X.SH NOTICE
X.PP
XThis software is in the public domain and is being provided free by
XBBN Labs to all users of ARPANET and USENET.  BBN disclaims all
Xexpress warranties with regard to any use of this software, including
Xall implied warranties of merchandability and fitness.  In no
Xevent shall BBN be liable for any direct, special, indirect or
Xconsequential damages or any damages whatsoever resulting from
Xloss of use, data or profits, or any other injury, whether in
Xan action of contract, negligence or other tortious action,
Xarising out of or in connection with the use or performance of
Xthis software.
_NO_MORE_TEXT_
echo 'sh - vv.ms'
sed 's/^X//' <<'_NO_MORE_TEXT_' > vv.ms
X.ds LF "\fB\s-1BBN\s0 Laboratories\fP
X.ds RF "December 8, 1984
X.DS C
X.ft B
X.ps +2
XNotes for Installing the Ring Driver on a SUN2
X.ps -2
X.ft R
X.sp
X\fICraig Partridge\fP
X.DE
X.PP
XThis note is intended to be a simple guide to installing the 4.2
Xdriver for the ProNet ring (as modified at \s-1BBN\s0 for the \s-1SUN\s0)
Xon a \s-1SUN\s02.
XThe driver has now been in beta-test for about 5 weeks (many thanks
Xto the people who agreed to be victims) and runs successfully on
Xboth Rev. A and Rev. C boards.*
X.FS
X*Just for the record, the beta-test version was 1.2.  This version is
X1.6, but contains only cosmetic changes.  The one substantive change
Xwas undone because I could not satisfactorily induce the bug I thought
XI was fixing.
X.FE
XWe therefore assume it will run on
XRev. B boards.  Please note that this is only a driver for the \s-1SUN\s02,
Xwe make no promises that it will work on any other 68000-based
Xmachine.
X.SH
XThe Boards
X.PP
XThe driver assumes that the boards are in the following configuration.
XYou should check your operation manual to figure out how to set up
Xyour boards.  Section references here are given to the manual as
Xof June 1983.
X.RS 
X.IP \(bu
XThe \s-1HSB\s0 should be an I/O device. (3.3.3.1).
X.IP \(bu
XThe \s-1HSB\s0 should use word \s-1DMA\s0. (3.3.3.2).
X.IP \(bu
XThe address bus width is 20 bits. (3.3.3.3)
X.IP \(bu
XUse parallel \s-1DMA\s0. (3.3.3.7)
X.IP \(bu
XEnable \s-1CBRQ\s0. (3.3.3.8).  Note that this option may cause problems
Xwith certain other boards, most notably the Interphase disk controller.
XWhen in doubt check the settings on the other boards in your card
Xcage.  We, however, do know that it works with \s-1SCSI\s0 boards,
Xethernet boards and \s-1SUN\s0 memory boards.  The driver will work
Xwhether or not \s-1CBRQ\s0 is enabled -- our experience on \s-1SUN\s0s
Xsimply suggest it should be.
X.IP \(bu
XThe \s-1CTL\s0 board should be set to recognize both its address and
Xthe broadcast address of 0x\s-1FF\s0. (3.2.2).  (If this is not
Xyour ring's broadcast address, change the \s-1NEWBROADCAST\s0 definition
Xin \fIif_vv.h\fP).
X.RE
X.LP
XIn addition, there are several configurational issues you must
Xdeal with yourself, according to how you have already configured
Xyour SUN.  These are:
X.RS
X.IP \(bu
XI/O page address. (3.3.3.4).  You should choose an address that
Xdoesn't conflict with that of any devices listed in your
X\fIconfig\fP file.  Remember that the board registers take
Xup 16 bytes starting at the specified address.
X(We presently use 0x400-0x40e).
X.IP \(bu
XThe interrupt priority selection.  (3.3.3.5)
XWe recommend you use the same priority as the Ethernet card (priority 3)
Xbut any priority of 3 or below seems to work.
X.IP \(bu
XByte swapping. (3.3.3.11).  The driver is set up so that it can do
Xbyte swapping if your board doesn't (see notes on the driver below).
XThe penalty appears to be a roughly 10%
Xloss in throughput.  If you haven't already got your board set up
Xfor hard swabbing, we suggest you start with software swabbing to
Xmake sure the board works, and then go through the wirewrapping
Xnecessary to get the board to do the swabbing.
X.IP \(bu
XThe node number on the \s-1CTL\s0 board should be set appropriately (3.2.2).
X.RE
X.SH
XThe Driver Itself
X.PP
XYou don't have to do much to the driver other than install it.  However,
Xthere are a few \fI#defines\fP near the start of
X\fIif_vv.c\fP that you should check before
Xyou compile a kernel.  They are:
X.IP \s-1WIRECENTER\s0
XThis should be defined if the ring has a wirecenter and left undefined
Xif you are testing on a loopback device.  Setting this wrong can damage
Xyour board.
X.IP \s-1HARDSWAB\s0
XThis should be defined if your board is doing swabbing, otherwise
Xthe driver will swap all the bytes before or after each \s-1DMA\s0.
XThe performance penalty for software swabbing is small, and it
Xis recommended that you first test your boards without doing swabbing
Xin hardware.
X.IP \s-1QUICKTEST\s0
XThis should only be defined if you feel you may have a bad board.
XSUNs which have \s-1QUICKTEST\s0 defined will check each other's packets
Xto make sure they are the size the sender thought they were.  Also
Xuseful for making sure that a random failure doesn't cause you to
Xtrash a fileserver's disk.
X.IP \s-1SLOWTEST\s0
XA slower, but more thorough test of the data is done using an IP checksum
Xof the entire packet.  Almost assuredly not worth it.
X.SH
XInstalling the Driver
X.PP
XThere are three files that make up the driver: \fIvv.h\fP, \fIif_vv.h\fP
Xand \fIif_vv.c\fP.
X.PP
X\fIvv.h\fP simply contains a definition for the
Xnumber of ring devices the kernel should support.  Be forewarned that
Xthe driver, as presently written, can only support \s-1ONE\s0 ring device.
X(This can be easily changed if it causes problems).
XYou should put \fIvv.h\fP in the directory \fI/sys/\s-1OBJ\s0\fP.
X.PP
X\fIif_vv.c\fP and \fIif_vv.h\fP contain the code and definitions for
Xthe driver.  They should both be put in the directory \fI/sys/sunif\fP.
XMake sure that \s-1HARDSWAB\s0 and \s-1WIRECENTER\s0 are defined correctly
Xin \fIif_vv.c\fP.
X.PP
XYou should also add the following line to the file \fI/sys/conf/files.sun\fP:
X.ne 4
X.br
X.nf
X.na
X.sp
Xsunif/if_vv.c	optional vv inet device-driver
X.sp
X.fi
X.ad
X.LP
XYou can then build a configuration file for a kernel with the \fIvv\fP
Xdriver.  Details on how to do this are described at the end of the
Xarticle on device drivers in the \fISUN Systems Internals Manual\fP
X(p. 38).   You should refer to the line there for a description of the
Xentry.  To give you an example of how you should do it for the ring,
Xhere is our \fIvv\fP entry in the \fIconfig\fP file for our kernels:
X.ne 4
X.br
X.sp
X.nf
X.na
Xdevice      vv0 at mb0 csr 0x400 priority 3
X.fi
X.ad
X.sp
X.LP
XYou can now generate your kernel
X.PP
XWhen bringing up your machine with the new kernel there are a few things to
Xwatch for.  First, you should see the kernel find the device -- the
Xmessage
X.br
X.sp
Xvv0 at mbio ... (\fIregister address\fP)
X.br
X.sp
Xshould be in the list of devices found.  Second, when \fI/etc/ifconfig\fP
Xis called on device \fIvv0\fP, the message
X.br
X.sp
Xvv0: host #
X.sp
Xwhere # is your host number, should appear.  This tells you the host's
Xnumber on the ring.  Immediately after the message appears, the
X\s-1LED\s0 on the wirecenter should light to indicate the connection
Xhas been made.  Finally, if your machine is the first host on the
Xring, expect to see several error messages on the console the
Xfirst time you try to use the ring.  The ring is simply synchronizing
Xand the messages will go away (typically we see 5 or 6 messages indicating
Xeither \s-1NOK\s0 or \s-1BDF\s0 or a bad packet).  We have also occasionally
Xseen such messages on other hosts when a new host joins the ring.
X.SH
XProblems
X.PP
XHere are a couple of notes about potential problems you may see.
X.IP \(bu
XThe driver only works for one board.  This could
Xbe fixed easily (the comments in the code even say how).
XIt just wasn't a problem that concerned us.
X.IP \(bu
XThe driver does not do \s-1ARP\s0.  My view is that a Class C
Xnet doesn't need this, but there have been some comments around the
Xnet from people who apparently believe it does.
X.IP \(bu
XTo do the data checking (defining \s-1QUICKTEST\s0 or \s-1SLOWTEST\s0)
Xin the driver it was necessary to use the third word in the ring
Xnet header.  Strictly speaking, this word should always be 0,
Xso this is non-standard (it was included because most checking already
Xexisted in the 4.1c \s-1SUN\s0 driver) but in general shouldn't
Xcause problems.  However, other drivers have also been known to plug
Xspecial values into this word; \fIthese special values may cause this 
Xdriver to reject packets if \fR\s-1SLOWTEST\s0 \fIor\fP \s-1QUICKTEST\s0\fI
Xis defined\fR\|!
X.IP \(bu
XIf you try to use your \s-1SUN\s0 as a gateway between two nets
Xbe aware that it
Xwon't quite work between a class B net and the ring.  (It almost
Xworks, but the return addresses get scrambled). We haven't found
Xanything in the driver that should cause this and some of \s-1SUN\s0's
Xtechnical personnel said they were pretty sure this was \s-1SUN\s0's
Xproblem.  They tell me that \s-1SUN\s0 never tested their class B
Xnetworking code.
X.IP \(bu
XMachines with Rev. A boards may see a comparatively high incidence of bad
Xpackets and error messages.  The Rev. A board apparently sometimes
Xasserts that an input packet has been \s-1DMA\s0'ed when only some of the
Xpacket has been \s-1DMA\s0'ed.  The board may still complete the
Xentire \s-1DMA\s0 (people aren't quite sure)
Xbut it is now too late because driver has already
Xtried to accept a partial packet.  This whole sequence tends to
Xcause two or three error messages.  This may or may not have a simple
Xsolution (people are still looking at the problem).  If a solution is found,
Xan update will be posted.
X.IP \(bu
XOne last note.  If the driver seems to be almost working, you probably
Xhave a hardware problem -- this observation
Xis based on some beta-test site experience.
XSo my advice is, when in doubt, check your hardware first (both
XSUN and ProNET)....
X.LP
XHowever, if you are having problems that you think are driver related,
Xhere's how to reach me:
X.DS C
X
XCraig Partridge
Xc/o BBN Labs
X10 Moulton St
XCambridge, MA 02238
X
X\s-1ARPANET\s0: craig@bbn-loki
X\s-1USENET\s0: {wjh12,ihnp4,decvax}!bbncca!craig
X\s-1CSNET\s0: craig%loki.arpa@csnet-relay
X.DE
X.LP
XI'll also happily accept bugfixes.  However, do please read the following
Xnotice.
X.SH
XNotice
X.nr PS 8
X.nr VS 10
X.PP
XThis software is in the public domain and is being provided free by
XBBN Labs to all users of ARPANET and USENET.  BBN disclaims all
Xexpress warranties with regard to any use of this software, including
Xall implied warranties of merchandability and fitness.  In no
Xevent shall BBN be liable for any direct, special, indirect or
Xconsequential damages or any damages whatsoever resulting from
Xloss of use, data or profits, or any other injury, whether in
Xan action of contract, negligence or other tortious action,
Xarising out of or in connection with the use or performance of
Xthis software.
X.nr PS 10
X.nr VS 12
_NO_MORE_TEXT_