date and time created 82/06/04 10:57:08 by sam
authorSam Leffler <sam@ucbvax.Berkeley.EDU>
Sat, 5 Jun 1982 01:57:08 +0000 (17:57 -0800)
committerSam Leffler <sam@ucbvax.Berkeley.EDU>
Sat, 5 Jun 1982 01:57:08 +0000 (17:57 -0800)
SCCS-vsn: sys/vax/if/if_vv.c 4.1

usr/src/sys/vax/if/if_vv.c [new file with mode: 0644]

diff --git a/usr/src/sys/vax/if/if_vv.c b/usr/src/sys/vax/if/if_vv.c
new file mode 100644 (file)
index 0000000..9d9fbc0
--- /dev/null
@@ -0,0 +1,636 @@
+/*     if_vv.c 4.1     82/06/04        */
+
+/*
+ * 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.
+ */
+#include "../h/param.h"
+#include "../h/systm.h"
+#include "../h/mbuf.h"
+#include "../h/pte.h"
+#include "../h/buf.h"
+#include "../h/protosw.h"
+#include "../h/socket.h"
+#include "../h/ubareg.h"
+#include "../h/ubavar.h"
+#include "../h/cpu.h"
+#include "../h/mtpr.h"
+#include "../h/vmmac.h"
+#include "../net/in.h"
+#include "../net/in_systm.h"
+#include "../net/if.h"
+#include "../net/if_vv.h"
+#include "../net/if_uba.h"
+#include "../net/ip.h"
+#include "../net/ip_var.h"
+#include "../net/route.h"
+
+#include "vv.h"
+#include "imp.h"
+
+/*
+ * N.B. - if WIRECENTER is defined wrong, it can well break
+ * the hardware!!
+ */
+#undef AUTOIDENTIFY
+#define        WIRECENTER
+
+#ifdef WIRECENTER
+#define        VV_CONF VV_HEN          /* drive wire center relay */
+#else
+#define        VV_CONF VV_STE          /* allow operation without wire center */
+#endif
+
+#define        VVMTU   (1024+512)
+
+int    vvprobe(), vvattach(), vvrint(), vvxint();
+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(),vvoutput(),vvreset();
+
+/*
+ * Software status of each interface.
+ *
+ * Each interface is referenced by a network interface structure,
+ * vs_if, which the routing code uses to locate the interface.
+ * This structure contains the output queue for the interface, its address, ...
+ * We also have, for each interface, a UBA interface structure, which
+ * contains information about the UNIBUS resources held by the interface:
+ * map registers, buffered data paths, etc.  Information is cached in this
+ * structure for use by the if_uba.c routines in running the interface
+ * efficiently.
+ */
+struct vv_softc {
+       struct  ifnet vs_if;            /* network-visible interface */
+       struct  ifuba vs_ifuba;         /* UNIBUS resources */
+       short   vs_oactive;             /* is output active? */
+       short   vs_olen;                /* length of last output */
+       u_short vs_lastx;               /* last destination address */
+       short   vs_tries;               /* current retry count */
+       short   vs_init;                /* number of ring inits */
+       short   vs_flush;               /* number of flushed packets */
+       short   vs_nottaken;            /* number of packets refused */
+} vv_softc[NVV];
+
+vvprobe(reg)
+       caddr_t reg;
+{
+       register int br, cvec;
+       register struct vvreg *addr = (struct vvreg *)reg;
+
+#ifdef lint
+       br = 0; cvec = br; br = cvec;
+#endif
+       /* reset interface, enable, and wait till dust settles */
+       addr->vvicsr = VV_RST;
+       addr->vvocsr = VV_RST;
+       DELAY(100000);
+       /* 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->vvocsr |= VV_DEN;         /* start the DMA */
+       DELAY(100000);
+       addr->vvocsr = 0;
+       if (cvec && cvec != 0x200)
+               cvec -= 4;              /* backup so vector => recieve */
+       return(1);
+}
+
+/*
+ * Interface exists: make available by filling in network interface
+ * record.  System will initialize the interface when it is ready
+ * to accept packets.
+ */
+vvattach(ui)
+       struct uba_device *ui;
+{
+       register struct vv_softc *vs = &vv_softc[ui->ui_unit];
+       register struct sockaddr_in *sin;
+COUNT(VVATTACH);
+
+       vs->vs_if.if_unit = ui->ui_unit;
+       vs->vs_if.if_name = "vv";
+       vs->vs_if.if_mtu = VVMTU;
+       vs->vs_if.if_net = ui->ui_flags;
+       vs->vs_if.if_host[0] = 0;       /* this will be reset in vvinit() */
+
+       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);
+       vs->vs_if.if_flags = IFF_BROADCAST;
+
+       vs->vs_if.if_init = vvinit;
+       vs->vs_if.if_output = vvoutput;
+       vs->vs_if.if_ubareset = vvreset;
+       vs->vs_ifuba.ifu_flags = UBA_NEEDBDP | UBA_NEED16;
+       if_attach(&vs->vs_if);
+#if NIMP == 0
+       if (ui->ui_flags & ~0xff)
+               vvlhinit((ui->ui_flags &~ 0xff) | 0x0a);
+#endif
+}
+
+/*
+ * Reset of interface after UNIBUS reset.
+ * If interface is on specified uba, reset its state.
+ */
+vvreset(unit, uban)
+       int unit, uban;
+{
+       register struct uba_device *ui;
+COUNT(VVRESET);
+
+       if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 ||
+           ui->ui_ubanum != uban)
+               return;
+       printf(" vv%d", unit);
+       vvinit(unit);
+}
+
+/*
+ * Initialization of interface; clear recorded pending
+ * operations, and reinitialize UNIBUS usage.
+ */
+vvinit(unit)
+       int unit;
+{
+       register struct vv_softc *vs = &vv_softc[unit];
+       register struct uba_device *ui = vvinfo[unit];
+       register struct vvreg *addr;
+       struct sockaddr_in *sin;
+       struct mbuf *m;
+       struct vv_header *v;
+       int ubainfo, retrying, attempts, waitcount, s;
+
+       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);
+               return;
+       }
+       addr = (struct vvreg *)ui->ui_addr;
+
+#ifdef AUTOIDENTIFY
+       /*
+        * Build a multicast message to identify our address
+        */
+       attempts = 0;           /* total attempts, including bad msg type */
+top:
+       retrying = 0;           /* first time through */
+       m = m_get(M_DONTWAIT);
+       if (m == 0)
+               panic("vvinit: can't get mbuf");
+       m->m_next = 0;
+       m->m_off = MMINOFF;
+       m->m_len = sizeof(struct vv_header);
+
+       v = mtod(m, struct vv_header *);
+       v->vh_dhost = 0;                /* multicast destination address */
+       v->vh_shost = 0;                /* will be overwritten with ours */
+       v->vh_version = RING_VERSION;
+       v->vh_type = RING_WHOAMI;
+       v->vh_info = 0;
+
+       /*
+        * 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;
+       /* map xmit message into uba if not already there */
+       if (!retrying)
+               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);
+       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.
+        */
+       DELAY(1000);
+       for (waitcount = 0; ((addr->vvicsr) & VV_RDY) == 0; waitcount++) {
+               if (waitcount < 10)
+                       DELAY(1000);
+               else {
+                       if (attempts++ < 10)s
+                               goto retry;
+                       else {
+                               printf("vv%d: can't initialize\n", unit);
+                               printf("vvinit loopwait: icsr = %b\n",
+                                       0xffff&(addr->vvicsr),VV_IBITS);
+                               return;
+                       }
+               }
+       }
+
+       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)
+               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)
+               vs->vs_if.if_host[0] = v->vh_shost;
+       else
+               goto top;
+#else
+       vs->vs_if.if_host[0] = 24;
+#endif
+
+       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]);
+
+       /*
+        * Reset the interface, and join the ring
+        */
+       addr->vvocsr = VV_RST | VV_CPB;         /* clear packet buffer */
+       addr->vvicsr = VV_RST | VV_CONF;        /* close logical relay */
+       sleep((caddr_t)&lbolt, PZERO);          /* let contacts settle */
+       vs->vs_init = 0;
+       vs->vs_flush = 0;
+       vs->vs_nottaken = 0;
+
+       /*
+        * Hang a receive and start any
+        * pending writes by faking a transmit complete.
+        */
+       s = splimp();
+       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_oactive = 1;
+       vvxint(unit);
+       splx(s);
+       if_rtinit(&vs->vs_if, RTF_DIRECT|RTF_UP);
+}
+
+/*
+ * Start or restart output on interface.
+ * If interface is not already active, get another datagram
+ * to send off of the interface queue, and map it to the interface
+ * before starting the output.
+ */
+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 vvreg *addr;
+       struct mbuf *m;
+       int ubainfo;
+       int dest;
+COUNT(VVSTART);
+
+       if (vs->vs_oactive)
+               goto restart;
+
+       /*
+        * Not already active: dequeue another request
+        * and map it to the UNIBUS.  If no more requests,
+        * just return.
+        */
+       IF_DEQUEUE(&vs->vs_if.if_snd, m);
+       if (m == 0) {
+               vs->vs_oactive = 0;
+               return;
+       }
+       dest = mtod(m, struct vv_header *)->vh_dhost;
+       vs->vs_olen = if_wubaput(&vs->vs_ifuba, m);
+       vs->vs_lastx = dest;
+
+restart:
+       /*
+        * Have request mapped to UNIBUS for transmission.
+        * Purge any stale data from this BDP, and start the otput.
+        */
+       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;
+       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_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB;
+       vs->vs_oactive = 1;
+}
+
+/*
+ * VVLNI transmit interrupt
+ * 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 vvreg *addr;
+       register int oc;
+COUNT(ENXINT);
+
+       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);
+               return;
+       }
+       if (oc &  (VV_OPT | VV_RFS)) {
+               if (++(vs->vs_tries) < VVRETRY) {
+                       if (oc & VV_OPT)
+                               vs->vs_init++;
+                       if (oc & VV_RFS)
+                               vs->vs_nottaken++;
+                       addr->vvocsr = VV_IEN | VV_ENB | VV_INR;
+                       return;
+               }
+               if (oc & VV_OPT)
+                       printf("vv%d: output timeout\n");
+       }
+       vs->vs_if.if_opackets++;
+       vs->vs_oactive = 0;
+       vs->vs_tries = 0;
+       if (oc & VVXERR) {
+               vs->vs_if.if_oerrors++;
+               printf("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_if.if_snd.ifq_head == 0) {
+               vs->vs_lastx = 0;
+               return;
+       }
+       vvstart(unit);
+}
+
+/*
+ * V2lni interface receiver interrupt.
+ * If input error just drop packet.
+ * Otherwise purge input buffered data path and examine 
+ * packet to determine type.  If can't determine length
+ * from type, then have to drop packet.  Othewise decapsulate
+ * 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_header *vv;
+       register struct ifqueue *inq;
+       struct mbuf *m;
+       int ubainfo, len, off;
+COUNT(VVRINT);
+
+       vs->vs_if.if_ipackets++;
+       /*
+        * Purge BDP; drop if input error indicated.
+        */
+       if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
+               UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
+       if (addr->vvicsr & VVRERR) {
+               vs->vs_if.if_ierrors++;
+               printf("vv%d: error vvicsr = %b\n", unit,
+                       0xffff&(addr->vvicsr), VV_IBITS);
+               goto setup;
+       }
+       off = 0;
+       len = 0;
+       vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
+       /*
+        * Demultiplex on packet type and deal with oddities of
+        * trailer protocol format
+        */
+       switch (vv->vh_type) {
+
+#ifdef INET
+       case RING_IP:
+               len = htons((u_short)((struct ip *) vv)->ip_len);
+               schednetisr(NETISR_IP);
+               inq = &ipintrq;
+               break;
+#endif
+       default:
+               printf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type);
+               goto setup;
+       }
+       if (len == 0)
+               goto setup;
+       /*
+        * Pull packet off interface.  Off is nonzero if packet
+        * has trailing header; if_rubaget will then force this header
+        * information to be at the front, but we still have to drop
+        * the two-byte type which is at the front of any trailer data.
+        */
+       m = if_rubaget(&vs->vs_ifuba, len, off);
+       if (m == 0)
+               goto setup;
+       IF_ENQUEUE(inq, m);
+
+setup:
+       /*
+        * Reset 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;
+
+}
+
+/*
+ * V2lni 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.
+ */
+vvoutput(ifp, m0, dst)
+       struct ifnet *ifp;
+       struct mbuf *m0;
+       struct sockaddr *dst;
+{
+       register struct mbuf *m = m0;
+       register struct vv_header *vv;
+       int type, dest, s;
+
+       switch (dst->sa_family) {
+
+#ifdef INET
+       case AF_INET: {
+               register struct ip *ip = mtod(m0, struct ip *);
+               int off;
+
+               dest = ip->ip_dst.s_addr >> 24;
+               type = RING_IP;
+               off = 0;
+               goto gottype;
+               }
+#endif
+       default:
+               printf("vv%d: can't handle af%d\n", ifp->if_unit,
+                       dst->sa_family);
+               m_freem(m0);
+               return (0);
+       }
+
+gottrailertype:
+       /*
+        * Packet to be sent as trailer: move first packet
+        * (control information) to end of chain.
+        */
+       while (m->m_next)
+               m = m->m_next;
+       m->m_next = m0;
+       m = m0->m_next;
+       m0->m_next = 0;
+       m0 = m;
+
+gottype:
+       /*
+        * Add local net header.  If no space in first mbuf,
+        * allocate another.
+        */
+       if (m->m_off > MMAXOFF ||
+           MMINOFF + sizeof (struct vv_header) > m->m_off) {
+               m = m_get(M_DONTWAIT);
+               if (m == 0) {
+                       m_freem(m0);
+                       return (0);
+               }
+               m->m_next = m0;
+               m->m_off = MMINOFF;
+               m->m_len = sizeof (struct vv_header);
+       } else {
+               m->m_off -= sizeof (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_dhost = dest;
+       vv->vh_version = RING_VERSION;
+       vv->vh_type = type;
+       vv->vh_info = m->m_len;
+
+       /*
+        * Queue message on interface, and start output if interface
+        * not yet active.
+        */
+       s = splimp();
+       IF_ENQUEUE(&ifp->if_snd, m);
+       if (vv_softc[ifp->if_unit].vs_oactive == 0)
+               vvstart(ifp->if_unit);
+       splx(s);
+       return (1);
+}
+
+#ifdef notdef
+/*
+ * vvprt_hdr(s, v) print the local net header in "v"
+ *     with title is "s"
+ */
+vvprt_hdr(s, v)
+       char *s;
+       register struct vv_header *v;
+{
+       printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+               s,
+               0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost),
+               0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type),
+               0xffff & (int)(v->vh_info));
+}
+
+/*
+ * 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
+
+#if NIMP == 0 && NVV > 0
+/*
+ * Logical host interface driver.
+ * Allows host to appear as an ARPAnet
+ * logical host.  Must also have routing
+ * table entry set up to forward packets
+ * to appropriate geteway on localnet.
+ */
+struct ifnet vvlhif;
+int    looutput();
+
+/*
+ * Called by localnet interface to allow logical
+ * host interface to "attach".  
+ */
+vvlhinit(vvifp, addr)
+       struct ifnet *vvifp;
+       int addr;
+{
+       register struct ifnet *ifp = &vvlhif;
+       register struct sockaddr_in *sin;
+
+COUNT(VVLHINIT);
+       ifp->if_name = "lh";
+       ifp->if_mtu = VVMTU;
+       sin = (struct sockaddr_in *)&ifp->if_addr;
+       sin->sin_family = AF_INET;
+       sin->sin_addr.s_addr = addr;
+       ifp->if_net = netpart(sin->sin_addr);
+       ifp->if_flags = IFF_UP;
+       ifp->if_output = looutput;
+       if_attach(ifp);
+       rtinit(&ifp->if_addr, &ifp->if_addr, RTF_DIRECT|RTF_UP|RTF_HOST);
+}
+#endif