BSD 4_1c_2 development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Fri, 17 Dec 1982 05:15:23 +0000 (21:15 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Fri, 17 Dec 1982 05:15:23 +0000 (21:15 -0800)
Work on file a/sys/vaxif/if_ec.c
Work on file a/sys/vaxif/if_en.c
Work on file a/sys/vaxif/if_il.c
Work on file a/sys/vaxif/if_uba.c

Synthesized-from: CSRG/cd1/4.1c.2

a/sys/vaxif/if_ec.c [new file with mode: 0644]
a/sys/vaxif/if_en.c [new file with mode: 0644]
a/sys/vaxif/if_il.c [new file with mode: 0644]
a/sys/vaxif/if_uba.c [new file with mode: 0644]

diff --git a/a/sys/vaxif/if_ec.c b/a/sys/vaxif/if_ec.c
new file mode 100644 (file)
index 0000000..b063cd2
--- /dev/null
@@ -0,0 +1,740 @@
+/*     if_ec.c 4.32    82/12/17        */
+
+#include "ec.h"
+
+/*
+ * 3Com Ethernet Controller interface
+ */
+#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 <errno.h>
+
+#include "../net/if.h"
+#include "../net/netisr.h"
+#include "../net/route.h"
+#include "../netinet/in.h"
+#include "../netinet/in_systm.h"
+#include "../netinet/ip.h"
+#include "../netinet/ip_var.h"
+#include "../netpup/pup.h"
+
+#include "../vax/cpu.h"
+#include "../vax/mtpr.h"
+#include "../vaxif/if_ether.h"
+#include "../vaxif/if_ecreg.h"
+#include "../vaxif/if_uba.h"
+#include "../vaxuba/ubareg.h"
+#include "../vaxuba/ubavar.h"
+
+#define        ECMEM   0000000
+
+int    ecprobe(), ecattach(), ecrint(), ecxint(), eccollide();
+struct uba_device *ecinfo[NEC];
+u_short ecstd[] = { 0 };
+struct uba_driver ecdriver =
+       { ecprobe, 0, ecattach, 0, ecstd, "ec", ecinfo };
+u_char ec_iltop[3] = { 0x02, 0x07, 0x01 };
+u_char ecbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+#define        ECUNIT(x)       minor(x)
+
+int    ecinit(),ecoutput(),ecreset();
+struct mbuf *ecget();
+
+extern struct ifnet loif;
+
+/*
+ * Ethernet software status per interface.
+ *
+ * Each interface is referenced by a network interface structure,
+ * es_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 ec_softc {
+       struct  ifnet es_if;            /* network-visible interface */
+       struct  ifuba es_ifuba;         /* UNIBUS resources */
+       short   es_mask;                /* mask for current output delay */
+       short   es_oactive;             /* is output active? */
+       u_char  *es_buf[16];            /* virtual addresses of buffers */
+       u_char  es_enaddr[6];           /* board's ethernet address */
+} ec_softc[NEC];
+
+/*
+ * Do output DMA to determine interface presence and
+ * interrupt vector.  DMA is too short to disturb other hosts.
+ */
+ecprobe(reg)
+       caddr_t reg;
+{
+       register int br, cvec;          /* r11, r10 value-result */
+       register struct ecdevice *addr = (struct ecdevice *)reg;
+       register caddr_t ecbuf = (caddr_t) &umem[numuba][ECMEM];
+
+#ifdef lint
+       br = 0; cvec = br; br = cvec;
+       ecrint(0); ecxint(0); eccollide(0);
+#endif
+       /*
+        * Make sure memory is turned on
+        */
+       addr->ec_rcr = EC_AROM;
+       /*
+        * Disable map registers for ec unibus space,
+        * but don't allocate yet.
+        */
+       (void) ubamem(numuba, ECMEM, 32*2, 0);
+       /*
+        * Check for existence of buffers on Unibus.
+        */
+       if (badaddr((caddr_t)ecbuf, 2)) {
+       bad1:
+               printf("ec: buffer mem not found\n");
+       bad2:
+               (void) ubamem(numuba, 0, 0, 0); /* reenable map (780 only) */
+               addr->ec_rcr = EC_MDISAB;       /* disable memory */
+               return (0);
+       }
+#if VAX780
+       if (cpu == VAX_780 && uba_hd[numuba].uh_uba->uba_sr) {
+               uba_hd[numuba].uh_uba->uba_sr = uba_hd[numuba].uh_uba->uba_sr;
+               goto bad1;
+       }
+#endif
+
+       /*
+        * Tell the system that the board has memory here, so it won't
+        * attempt to allocate the addresses later.
+        */
+       if (ubamem(numuba, ECMEM, 32*2, 1) == 0) {
+               printf("ecprobe: cannot reserve uba addresses\n");
+               goto bad2;
+       }
+
+       /*
+        * Make a one byte packet in what should be buffer #0.
+        * Submit it for sending.  This whould cause an xmit interrupt.
+        * The xmit interrupt vector is 8 bytes after the receive vector,
+        * so adjust for this before returning.
+        */
+       *(u_short *)ecbuf = (u_short) 03777;
+       ecbuf[03777] = '\0';
+       addr->ec_xcr = EC_XINTEN|EC_XWBN;
+       DELAY(100000);
+       addr->ec_xcr = EC_XCLR;
+       if (cvec > 0 && cvec != 0x200) {
+               if (cvec & 04) {        /* collision interrupt */
+                       cvec -= 04;
+                       br += 1;                /* rcv is collision + 1 */
+               } else {                /* xmit interrupt */
+                       cvec -= 010;
+                       br += 2;                /* rcv is xmit + 2 */
+               }
+       }
+       return (1);
+}
+
+/*
+ * Interface exists: make available by filling in network interface
+ * record.  System will initialize the interface when it is ready
+ * to accept packets.
+ */
+ecattach(ui)
+       struct uba_device *ui;
+{
+       struct ec_softc *es = &ec_softc[ui->ui_unit];
+       register struct ifnet *ifp = &es->es_if;
+       register struct ecdevice *addr = (struct ecdevice *)ui->ui_addr;
+       struct sockaddr_in *sin;
+       int i, j;
+       u_char *cp;
+
+       ifp->if_unit = ui->ui_unit;
+       ifp->if_name = "ec";
+       ifp->if_mtu = ETHERMTU;
+       ifp->if_net = ui->ui_flags;
+
+       /*
+        * Read the ethernet address off the board, one nibble at a time.
+        */
+       addr->ec_xcr = EC_UECLR;
+       addr->ec_rcr = EC_AROM;
+       cp = es->es_enaddr;
+#define        NEXTBIT addr->ec_rcr = EC_AROM|EC_ASTEP; addr->ec_rcr = EC_AROM
+       for (i=0; i<6; i++) {
+               *cp = 0;
+               for (j=0; j<=4; j+=4) {
+                       *cp |= ((addr->ec_rcr >> 8) & 0xf) << j;
+                       NEXTBIT; NEXTBIT; NEXTBIT; NEXTBIT;
+               }
+               cp++;
+       }
+#ifdef notdef
+       printf("ec%d: addr=%x:%x:%x:%x:%x:%x\n", ui->ui_unit,
+               es->es_enaddr[0]&0xff, es->es_enaddr[1]&0xff,
+               es->es_enaddr[2]&0xff, es->es_enaddr[3]&0xff,
+               es->es_enaddr[4]&0xff, es->es_enaddr[5]&0xff);
+#endif
+       ifp->if_host[0] = ((es->es_enaddr[3]&0xff)<<16) |
+           ((es->es_enaddr[4]&0xff)<<8) | (es->es_enaddr[5]&0xff);
+       sin = (struct sockaddr_in *)&es->es_if.if_addr;
+       sin->sin_family = AF_INET;
+       sin->sin_addr = if_makeaddr(ifp->if_net, ifp->if_host[0]);
+
+       sin = (struct sockaddr_in *)&ifp->if_broadaddr;
+       sin->sin_family = AF_INET;
+       sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
+       ifp->if_flags = IFF_BROADCAST;
+
+       ifp->if_init = ecinit;
+       ifp->if_output = ecoutput;
+       ifp->if_reset = ecreset;
+       for (i=0; i<16; i++)
+               es->es_buf[i] = (u_char *)&umem[ui->ui_ubanum][ECMEM+2048*i];
+       if_attach(ifp);
+}
+
+/*
+ * Reset of interface after UNIBUS reset.
+ * If interface is on specified uba, reset its state.
+ */
+ecreset(unit, uban)
+       int unit, uban;
+{
+       register struct uba_device *ui;
+
+       if (unit >= NEC || (ui = ecinfo[unit]) == 0 || ui->ui_alive == 0 ||
+           ui->ui_ubanum != uban)
+               return;
+       printf(" ec%d", unit);
+       (void) ubamem(uban, ECMEM, 32*2, 0);    /* mr disable (no alloc) */
+       ecinit(unit);
+}
+
+/*
+ * Initialization of interface; clear recorded pending
+ * operations, and reinitialize UNIBUS usage.
+ */
+ecinit(unit)
+       int unit;
+{
+       struct ec_softc *es = &ec_softc[unit];
+       struct ecdevice *addr;
+       int i, s;
+
+       /*
+        * Hang receive buffers and start any pending writes.
+        * Writing into the rcr also makes sure the memory
+        * is turned on.
+        */
+       addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
+       s = splimp();
+       for (i=ECRHBF; i>=ECRLBF; i--)
+               addr->ec_rcr = EC_READ|i;
+       es->es_oactive = 0;
+       es->es_mask = ~0;
+       es->es_if.if_flags |= IFF_UP;
+       if (es->es_if.if_snd.ifq_head)
+               ecstart(unit);
+       splx(s);
+       if_rtinit(&es->es_if, RTF_UP);
+}
+
+/*
+ * Start or restart output on interface.
+ * If interface is already active, then this is a retransmit
+ * after a collision, and just restuff registers.
+ * 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.
+ */
+ecstart(dev)
+       dev_t dev;
+{
+        int unit = ECUNIT(dev);
+       struct ec_softc *es = &ec_softc[unit];
+       struct ecdevice *addr;
+       struct mbuf *m;
+
+       if (es->es_oactive)
+               goto restart;
+
+       IF_DEQUEUE(&es->es_if.if_snd, m);
+       if (m == 0) {
+               es->es_oactive = 0;
+               return;
+       }
+       ecput(es->es_buf[ECTBF], m);
+
+restart:
+       addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
+       addr->ec_xcr = EC_WRITE|ECTBF;
+       es->es_oactive = 1;
+}
+
+/*
+ * Ethernet interface transmitter interrupt.
+ * Start another output if more data to send.
+ */
+ecxint(unit)
+       int unit;
+{
+       register struct ec_softc *es = &ec_softc[unit];
+       register struct ecdevice *addr =
+               (struct ecdevice *)ecinfo[unit]->ui_addr;
+
+       if (es->es_oactive == 0)
+               return;
+       if ((addr->ec_xcr&EC_XDONE) == 0 || (addr->ec_xcr&EC_XBN) != ECTBF) {
+               printf("ec%d: stray xmit interrupt, xcr=%b\n", unit,
+                       addr->ec_xcr, EC_XBITS);
+               es->es_oactive = 0;
+               addr->ec_xcr = EC_XCLR;
+               return;
+       }
+       es->es_if.if_opackets++;
+       es->es_oactive = 0;
+       es->es_mask = ~0;
+       addr->ec_xcr = EC_XCLR;
+       if (es->es_if.if_snd.ifq_head)
+               ecstart(unit);
+}
+
+/*
+ * Collision on ethernet interface.  Do exponential
+ * backoff, and retransmit.  If have backed off all
+ * the way print warning diagnostic, and drop packet.
+ */
+eccollide(unit)
+       int unit;
+{
+       struct ec_softc *es = &ec_softc[unit];
+
+       es->es_if.if_collisions++;
+       if (es->es_oactive)
+               ecdocoll(unit);
+}
+
+ecdocoll(unit)
+       int unit;
+{
+       register struct ec_softc *es = &ec_softc[unit];
+       register struct ecdevice *addr =
+           (struct ecdevice *)ecinfo[unit]->ui_addr;
+       register i;
+       int delay;
+
+       /*
+        * Es_mask is a 16 bit number with n low zero bits, with
+        * n the number of backoffs.  When es_mask is 0 we have
+        * backed off 16 times, and give up.
+        */
+       if (es->es_mask == 0) {
+               es->es_if.if_oerrors++;
+               printf("ec%d: send error\n", unit);
+               /*
+                * Reset interface, then requeue rcv buffers.
+                * Some incoming packets may be lost, but that
+                * can't be helped.
+                */
+               addr->ec_xcr = EC_UECLR;
+               for (i=ECRHBF; i>=ECRLBF; i--)
+                       addr->ec_rcr = EC_READ|i;
+               /*
+                * Reset and transmit next packet (if any).
+                */
+               es->es_oactive = 0;
+               es->es_mask = ~0;
+               if (es->es_if.if_snd.ifq_head)
+                       ecstart(unit);
+               return;
+       }
+       /*
+        * Do exponential backoff.  Compute delay based on low bits
+        * of the interval timer.  Then delay for that number of
+        * slot times.  A slot time is 51.2 microseconds (rounded to 51).
+        * This does not take into account the time already used to
+        * process the interrupt.
+        */
+       es->es_mask <<= 1;
+       delay = mfpr(ICR) &~ es->es_mask;
+       DELAY(delay * 51);
+       /*
+        * Clear the controller's collision flag, thus enabling retransmit.
+        */
+       addr->ec_xcr = EC_CLEAR;
+}
+
+/*
+ * Ethernet 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.
+ */
+ecrint(unit)
+       int unit;
+{
+       struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
+
+       while (addr->ec_rcr & EC_RDONE)
+               ecread(unit);
+}
+
+ecread(unit)
+       int unit;
+{
+       register struct ec_softc *es = &ec_softc[unit];
+       struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
+       register struct ether_header *ec;
+       struct mbuf *m;
+       int len, off, resid, ecoff, rbuf;
+       register struct ifqueue *inq;
+       u_char *ecbuf;
+
+       es->es_if.if_ipackets++;
+       rbuf = addr->ec_rcr & EC_RBN;
+       if (rbuf < ECRLBF || rbuf > ECRHBF)
+               panic("ecrint");
+       ecbuf = es->es_buf[rbuf];
+       ecoff = *(short *)ecbuf;
+       if (ecoff <= ECRDOFF || ecoff > 2046) {
+               es->es_if.if_ierrors++;
+#ifdef notdef
+               if (es->es_if.if_ierrors % 100 == 0)
+                       printf("ec%d: += 100 input errors\n", unit);
+#endif
+               goto setup;
+       }
+
+       /*
+        * Get input data length.
+        * Get pointer to ethernet header (in input buffer).
+        * Deal with trailer protocol: if type is PUP trailer
+        * get true type from first 16-bit word past data.
+        * Remember that type was trailer by setting off.
+        */
+       len = ecoff - ECRDOFF - sizeof (struct ether_header);
+       ec = (struct ether_header *)(ecbuf + ECRDOFF);
+       ec->ether_type = ntohs((u_short)ec->ether_type);
+#define        ecdataaddr(ec, off, type)       ((type)(((caddr_t)((ec)+1)+(off))))
+       if (ec->ether_type >= ETHERPUP_TRAIL &&
+           ec->ether_type < ETHERPUP_TRAIL+ETHERPUP_NTRAILER) {
+               off = (ec->ether_type - ETHERPUP_TRAIL) * 512;
+               if (off >= ETHERMTU)
+                       goto setup;             /* sanity */
+               ec->ether_type = ntohs(*ecdataaddr(ec, off, u_short *));
+               resid = ntohs(*(ecdataaddr(ec, off+2, u_short *)));
+               if (off + resid > len)
+                       goto setup;             /* sanity */
+               len = off + resid;
+       } else
+               off = 0;
+       if (len == 0)
+               goto setup;
+
+       /*
+        * Pull packet off interface.  Off is nonzero if packet
+        * has trailing header; ecget will then force this header
+        * information to be at the front, but we still have to drop
+        * the type and length which are at the front of any trailer data.
+        */
+       m = ecget(ecbuf, len, off);
+       if (m == 0)
+               goto setup;
+       if (off) {
+               m->m_off += 2 * sizeof (u_short);
+               m->m_len -= 2 * sizeof (u_short);
+       }
+       switch (ec->ether_type) {
+
+#ifdef INET
+       case ETHERPUP_IPTYPE:
+               schednetisr(NETISR_IP);
+               inq = &ipintrq;
+               break;
+#endif
+       default:
+               m_freem(m);
+               goto setup;
+       }
+
+       if (IF_QFULL(inq)) {
+               IF_DROP(inq);
+               m_freem(m);
+               goto setup;
+       }
+       IF_ENQUEUE(inq, m);
+
+setup:
+       /*
+        * Reset for next packet.
+        */
+       addr->ec_rcr = EC_READ|EC_RCLR|rbuf;
+}
+
+/*
+ * Ethernet 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.
+ * If destination is this address or broadcast, send packet to
+ * loop device to kludge around the fact that 3com interfaces can't
+ * talk to themselves.
+ */
+ecoutput(ifp, m0, dst)
+       struct ifnet *ifp;
+       struct mbuf *m0;
+       struct sockaddr *dst;
+{
+       int type, dest, s, error;
+       register struct ec_softc *es = &ec_softc[ifp->if_unit];
+       register struct mbuf *m = m0;
+       register struct ether_header *ec;
+       register int off, i;
+       struct mbuf *mcopy = (struct mbuf *) 0;         /* Null */
+
+       switch (dst->sa_family) {
+
+#ifdef INET
+       case AF_INET:
+               dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
+               if ((dest &~ 0xff) == 0)
+                       mcopy = m_copy(m, 0, (int)M_COPYALL);
+               else if (dest == ((struct sockaddr_in *)&es->es_if.if_addr)->
+                   sin_addr.s_addr) {
+                       mcopy = m;
+                       goto gotlocal;
+               }
+               off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
+               if (off > 0 && (off & 0x1ff) == 0 &&
+                   m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
+                       type = ETHERPUP_TRAIL + (off>>9);
+                       m->m_off -= 2 * sizeof (u_short);
+                       m->m_len += 2 * sizeof (u_short);
+                       *mtod(m, u_short *) = ntohs((u_short)ETHERPUP_IPTYPE);
+                       *(mtod(m, u_short *) + 1) = ntohs((u_short)m->m_len);
+                       goto gottrailertype;
+               }
+               type = ETHERPUP_IPTYPE;
+               off = 0;
+               goto gottype;
+#endif
+
+       default:
+               printf("ec%d: can't handle af%d\n", ifp->if_unit,
+                       dst->sa_family);
+               error = EAFNOSUPPORT;
+               goto bad;
+       }
+
+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 ether_header) > m->m_off) {
+               m = m_get(M_DONTWAIT, MT_HEADER);
+               if (m == 0) {
+                       error = ENOBUFS;
+                       goto bad;
+               }
+               m->m_next = m0;
+               m->m_off = MMINOFF;
+               m->m_len = sizeof (struct ether_header);
+       } else {
+               m->m_off -= sizeof (struct ether_header);
+               m->m_len += sizeof (struct ether_header);
+       }
+       ec = mtod(m, struct ether_header *);
+       bcopy((caddr_t)es->es_enaddr, (caddr_t)ec->ether_shost, 6);
+       if ((dest &~ 0xff) == 0)
+               bcopy((caddr_t)ecbroadcastaddr, (caddr_t)ec->ether_dhost, 6);
+       else {
+               u_char *to = dest & 0x8000 ? ec_iltop : es->es_enaddr;
+
+               bcopy((caddr_t)to, (caddr_t)ec->ether_dhost, 3);
+               ec->ether_dhost[3] = (dest>>8) & 0x7f;
+               ec->ether_dhost[4] = (dest>>16) & 0xff;
+               ec->ether_dhost[5] = (dest>>24) & 0xff;
+       }
+       ec->ether_type = htons((u_short)type);
+
+       /*
+        * Queue message on interface, and start output if interface
+        * not yet active.
+        */
+       s = splimp();
+       if (IF_QFULL(&ifp->if_snd)) {
+               IF_DROP(&ifp->if_snd);
+               error = ENOBUFS;
+               goto qfull;
+       }
+       IF_ENQUEUE(&ifp->if_snd, m);
+       if (es->es_oactive == 0)
+               ecstart(ifp->if_unit);
+       splx(s);
+
+gotlocal:
+       return(mcopy ? looutput(&loif, mcopy, dst) : 0);
+
+qfull:
+       m0 = m;
+       splx(s);
+bad:
+       m_freem(m0);
+       return(error);
+}
+
+/*
+ * Routine to copy from mbuf chain to transmit
+ * buffer in UNIBUS memory.
+ * If packet size is less than the minimum legal size,
+ * the buffer is expanded.  We probably should zero out the extra
+ * bytes for security, but that would slow things down.
+ */
+ecput(ecbuf, m)
+       u_char *ecbuf;
+       struct mbuf *m;
+{
+       register struct mbuf *mp;
+       register int off;
+       u_char *bp;
+
+       for (off = 2048, mp = m; mp; mp = mp->m_next)
+               off -= mp->m_len;
+       if (2048 - off < ETHERMIN + sizeof (struct ether_header))
+               off = 2048 - ETHERMIN - sizeof (struct ether_header);
+       *(u_short *)ecbuf = off;
+       bp = (u_char *)(ecbuf + off);
+       for (mp = m; mp; mp = mp->m_next) {
+               register unsigned len = mp->m_len;
+               u_char *mcp;
+
+               if (len == 0)
+                       continue;
+               mcp = mtod(mp, u_char *);
+               if ((unsigned)bp & 01) {
+                       *bp++ = *mcp++;
+                       len--;
+               }
+               if (off = (len >> 1)) {
+                       register u_short *to, *from;
+
+                       to = (u_short *)bp;
+                       from = (u_short *)mcp;
+                       do
+                               *to++ = *from++;
+                       while (--off > 0);
+                       bp = (u_char *)to,
+                       mcp = (u_char *)from;
+               }
+               if (len & 01)
+                       *bp++ = *mcp++;
+       }
+       m_freem(m);
+}
+
+/*
+ * Routine to copy from UNIBUS memory into mbufs.
+ * Similar in spirit to if_rubaget.
+ *
+ * Warning: This makes the fairly safe assumption that
+ * mbufs have even lengths.
+ */
+struct mbuf *
+ecget(ecbuf, totlen, off0)
+       u_char *ecbuf;
+       int totlen, off0;
+{
+       register struct mbuf *m;
+       struct mbuf *top = 0, **mp = &top;
+       register int off = off0, len;
+       u_char *cp;
+
+       cp = ecbuf + ECRDOFF + sizeof (struct ether_header);
+       while (totlen > 0) {
+               register int words;
+               u_char *mcp;
+
+               MGET(m, M_DONTWAIT, MT_DATA);
+               if (m == 0)
+                       goto bad;
+               if (off) {
+                       len = totlen - off;
+                       cp = ecbuf + ECRDOFF +
+                               sizeof (struct ether_header) + off;
+               } else
+                       len = totlen;
+               if (len >= CLBYTES) {
+                       struct mbuf *p;
+
+                       MCLGET(p, 1);
+                       if (p != 0) {
+                               m->m_len = len = CLBYTES;
+                               m->m_off = (int)p - (int)m;
+                       } else {
+                               m->m_len = len = MIN(MLEN, len);
+                               m->m_off = MMINOFF;
+                       }
+               } else {
+                       m->m_len = len = MIN(MLEN, len);
+                       m->m_off = MMINOFF;
+               }
+               mcp = mtod(m, u_char *);
+               if (words = (len >> 1)) {
+                       register u_short *to, *from;
+
+                       to = (u_short *)mcp;
+                       from = (u_short *)cp;
+                       do
+                               *to++ = *from++;
+                       while (--words > 0);
+                       mcp = (u_char *)to;
+                       cp = (u_char *)from;
+               }
+               if (len & 01)
+                       *mcp++ = *cp++;
+               *mp = m;
+               mp = &m->m_next;
+               if (off == 0) {
+                       totlen -= len;
+                       continue;
+               }
+               off += len;
+               if (off == totlen) {
+                       cp = ecbuf + ECRDOFF + sizeof (struct ether_header);
+                       off = 0;
+                       totlen = off0;
+               }
+       }
+       return (top);
+bad:
+       m_freem(top);
+       return (0);
+}
diff --git a/a/sys/vaxif/if_en.c b/a/sys/vaxif/if_en.c
new file mode 100644 (file)
index 0000000..c860a0f
--- /dev/null
@@ -0,0 +1,554 @@
+/*     if_en.c 4.75    82/12/17        */
+
+#include "en.h"
+
+/*
+ * Xerox prototype (3 Mb) Ethernet interface driver.
+ */
+#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 <errno.h>
+
+#include "../net/if.h"
+#include "../net/netisr.h"
+#include "../net/route.h"
+#include "../netinet/in.h"
+#include "../netinet/in_systm.h"
+#include "../netinet/ip.h"
+#include "../netinet/ip_var.h"
+#include "../netpup/pup.h"
+
+#include "../vax/cpu.h"
+#include "../vax/mtpr.h"
+#include "../vaxif/if_en.h"
+#include "../vaxif/if_enreg.h"
+#include "../vaxif/if_uba.h"
+#include "../vaxuba/ubareg.h"
+#include "../vaxuba/ubavar.h"
+
+#define        ENMTU   (1024+512)
+#define        ENMRU   (1024+512+16)           /* 16 is enough to receive trailer */
+
+int    enprobe(), enattach(), enrint(), enxint(), encollide();
+struct uba_device *eninfo[NEN];
+u_short enstd[] = { 0 };
+struct uba_driver endriver =
+       { enprobe, 0, enattach, 0, enstd, "en", eninfo };
+#define        ENUNIT(x)       minor(x)
+
+int    eninit(),enoutput(),enreset();
+
+/*
+ * Ethernet software status per interface.
+ *
+ * Each interface is referenced by a network interface structure,
+ * es_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 en_softc {
+       struct  ifnet es_if;            /* network-visible interface */
+       struct  ifuba es_ifuba;         /* UNIBUS resources */
+       short   es_delay;               /* current output delay */
+       short   es_mask;                /* mask for current output delay */
+       short   es_lastx;               /* host last transmitted to */
+       short   es_oactive;             /* is output active? */
+       short   es_olen;                /* length of last output */
+} en_softc[NEN];
+
+/*
+ * Do output DMA to determine interface presence and
+ * interrupt vector.  DMA is too short to disturb other hosts.
+ */
+enprobe(reg)
+       caddr_t reg;
+{
+       register int br, cvec;          /* r11, r10 value-result */
+       register struct endevice *addr = (struct endevice *)reg;
+
+#ifdef lint
+       br = 0; cvec = br; br = cvec;
+       enrint(0); enxint(0); encollide(0);
+#endif
+       addr->en_istat = 0;
+       addr->en_owc = -1;
+       addr->en_oba = 0;
+       addr->en_ostat = EN_IEN|EN_GO;
+       DELAY(100000);
+       addr->en_ostat = 0;
+#ifdef ECHACK
+       br = 0x16;
+#endif
+       return (1);
+}
+
+/*
+ * Interface exists: make available by filling in network interface
+ * record.  System will initialize the interface when it is ready
+ * to accept packets.
+ */
+enattach(ui)
+       struct uba_device *ui;
+{
+       register struct en_softc *es = &en_softc[ui->ui_unit];
+       register struct sockaddr_in *sin;
+
+       es->es_if.if_unit = ui->ui_unit;
+       es->es_if.if_name = "en";
+       es->es_if.if_mtu = ENMTU;
+       es->es_if.if_net = ui->ui_flags;
+       es->es_if.if_host[0] =
+        (~(((struct endevice *)eninfo[ui->ui_unit]->ui_addr)->en_addr)) & 0xff;
+       sin = (struct sockaddr_in *)&es->es_if.if_addr;
+       sin->sin_family = AF_INET;
+       sin->sin_addr = if_makeaddr(es->es_if.if_net, es->es_if.if_host[0]);
+       sin = (struct sockaddr_in *)&es->es_if.if_broadaddr;
+       sin->sin_family = AF_INET;
+       sin->sin_addr = if_makeaddr(es->es_if.if_net, 0);
+       es->es_if.if_flags = IFF_BROADCAST;
+       es->es_if.if_init = eninit;
+       es->es_if.if_output = enoutput;
+       es->es_if.if_reset = enreset;
+       es->es_ifuba.ifu_flags = UBA_NEEDBDP | UBA_NEED16 | UBA_CANTWAIT;
+#if defined(VAX750)
+       /* don't chew up 750 bdp's */
+       if (cpu == VAX_750 && ui->ui_unit > 0)
+               es->es_ifuba.ifu_flags &= ~UBA_NEEDBDP;
+#endif
+       if_attach(&es->es_if);
+}
+
+/*
+ * Reset of interface after UNIBUS reset.
+ * If interface is on specified uba, reset its state.
+ */
+enreset(unit, uban)
+       int unit, uban;
+{
+       register struct uba_device *ui;
+
+       if (unit >= NEN || (ui = eninfo[unit]) == 0 || ui->ui_alive == 0 ||
+           ui->ui_ubanum != uban)
+               return;
+       printf(" en%d", unit);
+       eninit(unit);
+}
+
+/*
+ * Initialization of interface; clear recorded pending
+ * operations, and reinitialize UNIBUS usage.
+ */
+eninit(unit)
+       int unit;
+{
+       register struct en_softc *es = &en_softc[unit];
+       register struct uba_device *ui = eninfo[unit];
+       register struct endevice *addr;
+       int s;
+
+       if (if_ubainit(&es->es_ifuba, ui->ui_ubanum,
+           sizeof (struct en_header), (int)btoc(ENMRU)) == 0) { 
+               printf("en%d: can't initialize\n", unit);
+               es->es_if.if_flags &= ~IFF_UP;
+               return;
+       }
+       addr = (struct endevice *)ui->ui_addr;
+       addr->en_istat = addr->en_ostat = 0;
+
+       /*
+        * Hang a receive and start any
+        * pending writes by faking a transmit complete.
+        */
+       s = splimp();
+       addr->en_iba = es->es_ifuba.ifu_r.ifrw_info;
+       addr->en_iwc = -(sizeof (struct en_header) + ENMRU) >> 1;
+       addr->en_istat = EN_IEN|EN_GO;
+       es->es_oactive = 1;
+       es->es_if.if_flags |= IFF_UP;
+       enxint(unit);
+       splx(s);
+       if_rtinit(&es->es_if, RTF_UP);
+}
+
+int    enalldelay = 0;
+int    enlastdel = 50;
+int    enlastmask = (~0) << 5;
+
+/*
+ * Start or restart output on interface.
+ * If interface is already active, then this is a retransmit
+ * after a collision, and just restuff registers and delay.
+ * 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.
+ */
+enstart(dev)
+       dev_t dev;
+{
+        int unit = ENUNIT(dev);
+       struct uba_device *ui = eninfo[unit];
+       register struct en_softc *es = &en_softc[unit];
+       register struct endevice *addr;
+       struct mbuf *m;
+       int dest;
+
+       if (es->es_oactive)
+               goto restart;
+
+       /*
+        * Not already active: dequeue another request
+        * and map it to the UNIBUS.  If no more requests,
+        * just return.
+        */
+       IF_DEQUEUE(&es->es_if.if_snd, m);
+       if (m == 0) {
+               es->es_oactive = 0;
+               return;
+       }
+       dest = mtod(m, struct en_header *)->en_dhost;
+       es->es_olen = if_wubaput(&es->es_ifuba, m);
+
+       /*
+        * Ethernet cannot take back-to-back packets (no
+        * buffering in interface.  To help avoid overrunning
+        * receivers, enforce a small delay (about 1ms) in interface:
+        *      * between all packets when enalldelay
+        *      * whenever last packet was broadcast
+        *      * whenever this packet is to same host as last packet
+        */
+       if (enalldelay || es->es_lastx == 0 || es->es_lastx == dest) {
+               es->es_delay = enlastdel;
+               es->es_mask = enlastmask;
+       }
+       es->es_lastx = dest;
+
+restart:
+       /*
+        * Have request mapped to UNIBUS for transmission.
+        * Purge any stale data from this BDP, and start the otput.
+        */
+       if (es->es_ifuba.ifu_flags & UBA_NEEDBDP)
+               UBAPURGE(es->es_ifuba.ifu_uba, es->es_ifuba.ifu_w.ifrw_bdp);
+       addr = (struct endevice *)ui->ui_addr;
+       addr->en_oba = (int)es->es_ifuba.ifu_w.ifrw_info;
+       addr->en_odelay = es->es_delay;
+       addr->en_owc = -((es->es_olen + 1) >> 1);
+       addr->en_ostat = EN_IEN|EN_GO;
+       es->es_oactive = 1;
+}
+
+/*
+ * Ethernet interface transmitter interrupt.
+ * Start another output if more data to send.
+ */
+enxint(unit)
+       int unit;
+{
+       register struct uba_device *ui = eninfo[unit];
+       register struct en_softc *es = &en_softc[unit];
+       register struct endevice *addr = (struct endevice *)ui->ui_addr;
+
+       if (es->es_oactive == 0)
+               return;
+       if (es->es_mask && (addr->en_ostat&EN_OERROR)) {
+               es->es_if.if_oerrors++;
+               endocoll(unit);
+               return;
+       }
+       es->es_if.if_opackets++;
+       es->es_oactive = 0;
+       es->es_delay = 0;
+       es->es_mask = ~0;
+       if (es->es_ifuba.ifu_xtofree) {
+               m_freem(es->es_ifuba.ifu_xtofree);
+               es->es_ifuba.ifu_xtofree = 0;
+       }
+       if (es->es_if.if_snd.ifq_head == 0) {
+               es->es_lastx = 256;             /* putatively illegal */
+               return;
+       }
+       enstart(unit);
+}
+
+/*
+ * Collision on ethernet interface.  Do exponential
+ * backoff, and retransmit.  If have backed off all
+ * the way print warning diagnostic, and drop packet.
+ */
+encollide(unit)
+       int unit;
+{
+       struct en_softc *es = &en_softc[unit];
+
+       es->es_if.if_collisions++;
+       if (es->es_oactive == 0)
+               return;
+       endocoll(unit);
+}
+
+endocoll(unit)
+       int unit;
+{
+       register struct en_softc *es = &en_softc[unit];
+
+       /*
+        * Es_mask is a 16 bit number with n low zero bits, with
+        * n the number of backoffs.  When es_mask is 0 we have
+        * backed off 16 times, and give up.
+        */
+       if (es->es_mask == 0) {
+               printf("en%d: send error\n", unit);
+               enxint(unit);
+               return;
+       }
+       /*
+        * Another backoff.  Restart with delay based on n low bits
+        * of the interval timer.
+        */
+       es->es_mask <<= 1;
+       es->es_delay = mfpr(ICR) &~ es->es_mask;
+       enstart(unit);
+}
+
+struct sockaddr_pup pupsrc = { AF_PUP };
+struct sockaddr_pup pupdst = { AF_PUP };
+struct sockproto pupproto = { PF_PUP };
+/*
+ * Ethernet 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.
+ */
+enrint(unit)
+       int unit;
+{
+       register struct en_softc *es = &en_softc[unit];
+       struct endevice *addr = (struct endevice *)eninfo[unit]->ui_addr;
+       register struct en_header *en;
+       struct mbuf *m;
+       int len; short resid;
+       register struct ifqueue *inq;
+       int off;
+
+       es->es_if.if_ipackets++;
+
+       /*
+        * Purge BDP; drop if input error indicated.
+        */
+       if (es->es_ifuba.ifu_flags & UBA_NEEDBDP)
+               UBAPURGE(es->es_ifuba.ifu_uba, es->es_ifuba.ifu_r.ifrw_bdp);
+       if (addr->en_istat&EN_IERROR) {
+               es->es_if.if_ierrors++;
+               goto setup;
+       }
+
+       /*
+        * Calculate input data length.
+        * Get pointer to ethernet header (in input buffer).
+        * Deal with trailer protocol: if type is PUP trailer
+        * get true type from first 16-bit word past data.
+        * Remember that type was trailer by setting off.
+        */
+       resid = addr->en_iwc;
+       if (resid)
+               resid |= 0176000;
+       len = (((sizeof (struct en_header) + ENMRU) >> 1) + resid) << 1;
+       len -= sizeof (struct en_header);
+       if (len > ENMRU)
+               goto setup;                     /* sanity */
+       en = (struct en_header *)(es->es_ifuba.ifu_r.ifrw_addr);
+#define        endataaddr(en, off, type)       ((type)(((caddr_t)((en)+1)+(off))))
+       if (en->en_type >= ENPUP_TRAIL &&
+           en->en_type < ENPUP_TRAIL+ENPUP_NTRAILER) {
+               off = (en->en_type - ENPUP_TRAIL) * 512;
+               if (off > ENMTU)
+                       goto setup;             /* sanity */
+               en->en_type = *endataaddr(en, off, u_short *);
+               resid = *(endataaddr(en, off+2, u_short *));
+               if (off + resid > len)
+                       goto setup;             /* sanity */
+               len = off + resid;
+       } else
+               off = 0;
+       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 type and length which are at the front of any trailer data.
+        */
+       m = if_rubaget(&es->es_ifuba, len, off);
+       if (m == 0)
+               goto setup;
+       if (off) {
+               m->m_off += 2 * sizeof (u_short);
+               m->m_len -= 2 * sizeof (u_short);
+       }
+       switch (en->en_type) {
+
+#ifdef INET
+       case ENPUP_IPTYPE:
+               schednetisr(NETISR_IP);
+               inq = &ipintrq;
+               break;
+#endif
+#ifdef PUP
+       case ENPUP_PUPTYPE: {
+               struct pup_header *pup = mtod(m, struct pup_header *);
+
+               pupproto.sp_protocol = pup->pup_type;
+               pupdst.spup_addr = pup->pup_dport;
+               pupsrc.spup_addr = pup->pup_sport;
+               raw_input(m, &pupproto, (struct sockaddr *)&pupsrc,
+                 (struct sockaddr *)&pupdst);
+               goto setup;
+       }
+#endif
+       default:
+               m_freem(m);
+               goto setup;
+       }
+
+       if (IF_QFULL(inq)) {
+               IF_DROP(inq);
+               m_freem(m);
+       } else
+               IF_ENQUEUE(inq, m);
+
+setup:
+       /*
+        * Reset for next packet.
+        */
+       addr->en_iba = es->es_ifuba.ifu_r.ifrw_info;
+       addr->en_iwc = -(sizeof (struct en_header) + ENMRU) >> 1;
+       addr->en_istat = EN_IEN|EN_GO;
+}
+
+/*
+ * Ethernet 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.
+ */
+enoutput(ifp, m0, dst)
+       struct ifnet *ifp;
+       struct mbuf *m0;
+       struct sockaddr *dst;
+{
+       int type, dest, s, error;
+       register struct mbuf *m = m0;
+       register struct en_header *en;
+       register int off;
+
+       switch (dst->sa_family) {
+
+#ifdef INET
+       case AF_INET:
+               dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
+               if (in_lnaof(*((struct in_addr *)&dest)) >= 0x100) {
+                       error = EPERM;          /* ??? */
+                       goto bad;
+               }
+               dest = (dest >> 24) & 0xff;
+               off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
+               if (off > 0 && (off & 0x1ff) == 0 &&
+                   m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
+                       type = ENPUP_TRAIL + (off>>9);
+                       m->m_off -= 2 * sizeof (u_short);
+                       m->m_len += 2 * sizeof (u_short);
+                       *mtod(m, u_short *) = ENPUP_IPTYPE;
+                       *(mtod(m, u_short *) + 1) = m->m_len;
+                       goto gottrailertype;
+               }
+               type = ENPUP_IPTYPE;
+               off = 0;
+               goto gottype;
+#endif
+#ifdef PUP
+       case AF_PUP:
+               dest = ((struct sockaddr_pup *)dst)->spup_addr.pp_host;
+               type = ENPUP_PUPTYPE;
+               off = 0;
+               goto gottype;
+#endif
+
+       default:
+               printf("en%d: can't handle af%d\n", ifp->if_unit,
+                       dst->sa_family);
+               error = EAFNOSUPPORT;
+               goto bad;
+       }
+
+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 en_header) > m->m_off) {
+               m = m_get(M_DONTWAIT, MT_HEADER);
+               if (m == 0) {
+                       error = ENOBUFS;
+                       goto bad;
+               }
+               m->m_next = m0;
+               m->m_off = MMINOFF;
+               m->m_len = sizeof (struct en_header);
+       } else {
+               m->m_off -= sizeof (struct en_header);
+               m->m_len += sizeof (struct en_header);
+       }
+       en = mtod(m, struct en_header *);
+       en->en_shost = ifp->if_host[0];
+       en->en_dhost = dest;
+       en->en_type = type;
+
+       /*
+        * Queue message on interface, and start output if interface
+        * not yet active.
+        */
+       s = splimp();
+       if (IF_QFULL(&ifp->if_snd)) {
+               IF_DROP(&ifp->if_snd);
+               error = ENOBUFS;
+               goto qfull;
+       }
+       IF_ENQUEUE(&ifp->if_snd, m);
+       if (en_softc[ifp->if_unit].es_oactive == 0)
+               enstart(ifp->if_unit);
+       splx(s);
+       return (0);
+qfull:
+       m0 = m;
+       splx(s);
+bad:
+       m_freem(m0);
+       return (error);
+}
diff --git a/a/sys/vaxif/if_il.c b/a/sys/vaxif/if_il.c
new file mode 100644 (file)
index 0000000..677401c
--- /dev/null
@@ -0,0 +1,598 @@
+/*     if_il.c 4.17    82/12/17        */
+
+#include "il.h"
+
+/*
+ * Interlan Ethernet Communications Controller interface
+ */
+#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 <errno.h>
+
+#include "../net/if.h"
+#include "../net/netisr.h"
+#include "../net/route.h"
+#include "../netinet/in.h"
+#include "../netinet/in_systm.h"
+#include "../netinet/ip.h"
+#include "../netinet/ip_var.h"
+#include "../netpup/pup.h"
+
+#include "../vax/cpu.h"
+#include "../vax/mtpr.h"
+#include "../vaxif/if_ether.h"
+#include "../vaxif/if_il.h"
+#include "../vaxif/if_ilreg.h"
+#include "../vaxif/if_uba.h"
+#include "../vaxuba/ubareg.h"
+#include "../vaxuba/ubavar.h"
+
+int    ilprobe(), ilattach(), ilrint(), ilcint();
+struct uba_device *ilinfo[NIL];
+u_short ilstd[] = { 0 };
+struct uba_driver ildriver =
+       { ilprobe, 0, ilattach, 0, ilstd, "il", ilinfo };
+#define        ILUNIT(x)       minor(x)
+int    ilinit(),iloutput(),ilreset(),ilwatch();
+
+u_char il_ectop[3] = { 0x02, 0x60, 0x8c };
+u_char ilbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+/*
+ * Ethernet software status per interface.
+ *
+ * Each interface is referenced by a network interface structure,
+ * is_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 il_softc {
+       struct  ifnet is_if;            /* network-visible interface */
+       struct  ifuba is_ifuba;         /* UNIBUS resources */
+       int     is_flags;
+#define        ILF_OACTIVE     0x1             /* output is active */
+#define        ILF_RCVPENDING  0x2             /* start rcv in ilcint */
+#define        ILF_STATPENDING 0x4             /* stat cmd pending */
+       short   is_lastcmd;             /* can't read csr, so must save it */
+       short   is_scaninterval;        /* interval of stat collection */
+#define        ILWATCHINTERVAL 60              /* once every 60 seconds */
+       struct  il_stats is_stats;      /* holds on-board statistics */
+       struct  il_stats is_sum;        /* summation over time */
+       int     is_ubaddr;              /* mapping registers of is_stats */
+} il_softc[NIL];
+
+ilprobe(reg)
+       caddr_t reg;
+{
+       register int br, cvec;          /* r11, r10 value-result */
+       register struct ildevice *addr = (struct ildevice *)reg;
+       register i;
+
+#ifdef lint
+       br = 0; cvec = br; br = cvec;
+       i = 0; ilrint(i); ilcint(i); ilwatch(i);
+#endif
+
+       addr->il_csr = ILC_OFFLINE|IL_CIE;
+       DELAY(100000);
+       i = addr->il_csr;               /* clear CDONE */
+       if (cvec > 0 && cvec != 0x200)
+               cvec -= 4;
+       return (1);
+}
+
+/*
+ * Interface exists: make available by filling in network interface
+ * record.  System will initialize the interface when it is ready
+ * to accept packets.  A STATUS command is done to get the ethernet
+ * address and other interesting data.
+ */
+ilattach(ui)
+       struct uba_device *ui;
+{
+       register struct il_softc *is = &il_softc[ui->ui_unit];
+       register struct ifnet *ifp = &is->is_if;
+       register struct ildevice *addr = (struct ildevice *)ui->ui_addr;
+       struct sockaddr_in *sin;
+
+       ifp->if_unit = ui->ui_unit;
+       ifp->if_name = "il";
+       ifp->if_mtu = ETHERMTU;
+       ifp->if_net = ui->ui_flags;
+
+       /*
+        * Reset the board and map the statistics
+        * buffer onto the Unibus.
+        */
+       addr->il_csr = ILC_RESET;
+       while ((addr->il_csr&IL_CDONE) == 0)
+               ;
+       if (addr->il_csr&IL_STATUS)
+               printf("il%d: reset failed, csr=%b\n", ui->ui_unit,
+                       addr->il_csr, IL_BITS);
+       
+       is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats,
+               sizeof (struct il_stats), 0);
+       addr->il_bar = is->is_ubaddr & 0xffff;
+       addr->il_bcr = sizeof (struct il_stats);
+       addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT;
+       while ((addr->il_csr&IL_CDONE) == 0)
+               ;
+       if (addr->il_csr&IL_STATUS)
+               printf("il%d: status failed, csr=%b\n", ui->ui_unit,
+                       addr->il_csr, IL_BITS);
+       ubarelse(ui->ui_ubanum, &is->is_ubaddr);
+       printf("il%d: addr=%x:%x:%x:%x:%x:%x module=%s firmware=%s\n",
+               ui->ui_unit,
+               is->is_stats.ils_addr[0]&0xff, is->is_stats.ils_addr[1]&0xff,
+               is->is_stats.ils_addr[2]&0xff, is->is_stats.ils_addr[3]&0xff,
+               is->is_stats.ils_addr[4]&0xff, is->is_stats.ils_addr[5]&0xff,
+               is->is_stats.ils_module, is->is_stats.ils_firmware);
+       ifp->if_host[0] =
+           ((is->is_stats.ils_addr[3]&0xff)<<16) | 0x800000 |
+           ((is->is_stats.ils_addr[4]&0xff)<<8) |
+           (is->is_stats.ils_addr[5]&0xff);
+       sin = (struct sockaddr_in *)&ifp->if_addr;
+       sin->sin_family = AF_INET;
+       sin->sin_addr = if_makeaddr(ifp->if_net, ifp->if_host[0]);
+
+       sin = (struct sockaddr_in *)&ifp->if_broadaddr;
+       sin->sin_family = AF_INET;
+       sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
+       ifp->if_flags = IFF_BROADCAST;
+
+       ifp->if_init = ilinit;
+       ifp->if_output = iloutput;
+       ifp->if_reset = ilreset;
+       ifp->if_watchdog = ilwatch;
+       is->is_scaninterval = ILWATCHINTERVAL;
+       ifp->if_timer = is->is_scaninterval;
+       is->is_ifuba.ifu_flags = UBA_CANTWAIT;
+#ifdef notdef
+       is->is_ifuba.ifu_flags |= UBA_NEEDBDP;
+#endif
+       if_attach(ifp);
+}
+
+/*
+ * Reset of interface after UNIBUS reset.
+ * If interface is on specified uba, reset its state.
+ */
+ilreset(unit, uban)
+       int unit, uban;
+{
+       register struct uba_device *ui;
+
+       if (unit >= NIL || (ui = ilinfo[unit]) == 0 || ui->ui_alive == 0 ||
+           ui->ui_ubanum != uban)
+               return;
+       printf(" il%d", unit);
+       ilinit(unit);
+}
+
+/*
+ * Initialization of interface; clear recorded pending
+ * operations, and reinitialize UNIBUS usage.
+ */
+ilinit(unit)
+       int unit;
+{
+       register struct il_softc *is = &il_softc[unit];
+       register struct uba_device *ui = ilinfo[unit];
+       register struct ildevice *addr;
+       int s;
+
+       if (if_ubainit(&is->is_ifuba, ui->ui_ubanum,
+           sizeof (struct il_rheader), (int)btoc(ETHERMTU)) == 0) { 
+               printf("il%d: can't initialize\n", unit);
+               is->is_if.if_flags &= ~IFF_UP;
+               return;
+       }
+       is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats,
+               sizeof (struct il_stats), 0);
+       addr = (struct ildevice *)ui->ui_addr;
+
+       /*
+        * Turn off source address insertion (it's faster this way),
+        * and set board online.
+        */
+       s = splimp();
+       addr->il_csr = ILC_CISA;
+       while ((addr->il_csr & IL_CDONE) == 0)
+               ;
+       addr->il_csr = ILC_ONLINE;
+       while ((addr->il_csr & IL_CDONE) == 0)
+               ;
+       /*
+        * Set board online.
+        * Hang receive buffer and start any pending
+        * writes by faking a transmit complete.
+        * Receive bcr is not a muliple of 4 so buffer
+        * chaining can't happen.
+        */
+       s = splimp();
+       addr->il_csr = ILC_ONLINE;
+       while ((addr->il_csr & IL_CDONE) == 0)
+               ;
+       addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff;
+       addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6;
+       addr->il_csr =
+               ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE;
+       while ((addr->il_csr & IL_CDONE) == 0)
+               ;
+       is->is_flags = ILF_OACTIVE;
+       is->is_if.if_flags |= IFF_UP;
+       is->is_lastcmd = 0;
+       ilcint(unit);
+       splx(s);
+       if_rtinit(&is->is_if, RTF_UP);
+}
+
+/*
+ * Start output on interface.
+ * Get another datagram to send off of the interface queue,
+ * and map it to the interface before starting the output.
+ */
+ilstart(dev)
+       dev_t dev;
+{
+        int unit = ILUNIT(dev), len;
+       struct uba_device *ui = ilinfo[unit];
+       register struct il_softc *is = &il_softc[unit];
+       register struct ildevice *addr;
+       struct mbuf *m;
+       short csr;
+
+       IF_DEQUEUE(&is->is_if.if_snd, m);
+       addr = (struct ildevice *)ui->ui_addr;
+       if (m == 0) {
+               if ((is->is_flags & ILF_STATPENDING) == 0)
+                       return;
+               addr->il_bar = is->is_ubaddr & 0xffff;
+               addr->il_bcr = sizeof (struct il_stats);
+               csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT|IL_RIE|IL_CIE;
+               is->is_flags &= ~ILF_STATPENDING;
+               goto startcmd;
+       }
+       len = if_wubaput(&is->is_ifuba, m);
+       /*
+        * Ensure minimum packet length.
+        * This makes the safe assumtion that there are no virtual holes
+        * after the data.
+        * For security, it might be wise to zero out the added bytes,
+        * but we're mainly interested in speed at the moment.
+        */
+       if (len - sizeof(struct ether_header) < ETHERMIN)
+               len = ETHERMIN + sizeof(struct ether_header);
+       if (is->is_ifuba.ifu_flags & UBA_NEEDBDP)
+               UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_w.ifrw_bdp);
+       addr->il_bar = is->is_ifuba.ifu_w.ifrw_info & 0xffff;
+       addr->il_bcr = len;
+       csr =
+         ((is->is_ifuba.ifu_w.ifrw_info >> 2) & IL_EUA)|ILC_XMIT|IL_CIE|IL_RIE;
+
+startcmd:
+       is->is_lastcmd = csr & IL_CMD;
+       addr->il_csr = csr;
+       is->is_flags |= ILF_OACTIVE;
+}
+
+/*
+ * Command done interrupt.
+ */
+ilcint(unit)
+       int unit;
+{
+       register struct il_softc *is = &il_softc[unit];
+       struct uba_device *ui = ilinfo[unit];
+       register struct ildevice *addr = (struct ildevice *)ui->ui_addr;
+       short csr;
+
+       if ((is->is_flags & ILF_OACTIVE) == 0) {
+               printf("il%d: stray xmit interrupt, csr=%b\n", unit,
+                       addr->il_csr, IL_BITS);
+               return;
+       }
+
+       csr = addr->il_csr;
+       /*
+        * Hang receive buffer if it couldn't
+        * be done earlier (in ilrint).
+        */
+       if (is->is_flags & ILF_RCVPENDING) {
+               addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff;
+               addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6;
+               addr->il_csr =
+                 ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE;
+               while ((addr->il_csr & IL_CDONE) == 0)
+                       ;
+               is->is_flags &= ~ILF_RCVPENDING;
+       }
+       is->is_flags &= ~ILF_OACTIVE;
+       csr &= IL_STATUS;
+       switch (is->is_lastcmd) {
+
+       case ILC_XMIT:
+               is->is_if.if_opackets++;
+               if (csr > ILERR_RETRIES)
+                       is->is_if.if_oerrors++;
+               break;
+
+       case ILC_STAT:
+               if (csr == ILERR_SUCCESS)
+                       iltotal(is);
+               break;
+       }
+       if (is->is_ifuba.ifu_xtofree) {
+               m_freem(is->is_ifuba.ifu_xtofree);
+               is->is_ifuba.ifu_xtofree = 0;
+       }
+       ilstart(unit);
+}
+
+/*
+ * Ethernet 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.
+ */
+ilrint(unit)
+       int unit;
+{
+       register struct il_softc *is = &il_softc[unit];
+       struct ildevice *addr = (struct ildevice *)ilinfo[unit]->ui_addr;
+       register struct il_rheader *il;
+       struct mbuf *m;
+       int len, off, resid;
+       register struct ifqueue *inq;
+
+       is->is_if.if_ipackets++;
+       if (is->is_ifuba.ifu_flags & UBA_NEEDBDP)
+               UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_r.ifrw_bdp);
+       il = (struct il_rheader *)(is->is_ifuba.ifu_r.ifrw_addr);
+       len = il->ilr_length - sizeof(struct il_rheader);
+       if ((il->ilr_status&(ILFSTAT_A|ILFSTAT_C)) || len < 46 ||
+           len > ETHERMTU) {
+               is->is_if.if_ierrors++;
+#ifdef notdef
+               if (is->is_if.if_ierrors % 100 == 0)
+                       printf("il%d: += 100 input errors\n", unit);
+#endif
+               goto setup;
+       }
+
+       /*
+        * Deal with trailer protocol: if type is PUP trailer
+        * get true type from first 16-bit word past data.
+        * Remember that type was trailer by setting off.
+        */
+       il->ilr_type = ntohs((u_short)il->ilr_type);
+#define        ildataaddr(il, off, type)       ((type)(((caddr_t)((il)+1)+(off))))
+       if (il->ilr_type >= ETHERPUP_TRAIL &&
+           il->ilr_type < ETHERPUP_TRAIL+ETHERPUP_NTRAILER) {
+               off = (il->ilr_type - ETHERPUP_TRAIL) * 512;
+               if (off >= ETHERMTU)
+                       goto setup;             /* sanity */
+               il->ilr_type = ntohs(*ildataaddr(il, off, u_short *));
+               resid = ntohs(*(ildataaddr(il, off+2, u_short *)));
+               if (off + resid > len)
+                       goto setup;             /* sanity */
+               len = off + resid;
+       } else
+               off = 0;
+       if (len == 0)
+               goto setup;
+
+       /*
+        * Pull packet off interface.  Off is nonzero if packet
+        * has trailing header; ilget will then force this header
+        * information to be at the front, but we still have to drop
+        * the type and length which are at the front of any trailer data.
+        */
+       m = if_rubaget(&is->is_ifuba, len, off);
+       if (m == 0)
+               goto setup;
+       if (off) {
+               m->m_off += 2 * sizeof (u_short);
+               m->m_len -= 2 * sizeof (u_short);
+       }
+       switch (il->ilr_type) {
+
+#ifdef INET
+       case ETHERPUP_IPTYPE:
+               schednetisr(NETISR_IP);
+               inq = &ipintrq;
+               break;
+#endif
+       default:
+               m_freem(m);
+               goto setup;
+       }
+
+       if (IF_QFULL(inq)) {
+               IF_DROP(inq);
+               m_freem(m);
+               goto setup;
+       }
+       IF_ENQUEUE(inq, m);
+
+setup:
+       /*
+        * Reset for next packet if possible.
+        * If waiting for transmit command completion, set flag
+        * and wait until command completes.
+        */
+       if (is->is_flags & ILF_OACTIVE) {
+               is->is_flags |= ILF_RCVPENDING;
+               return;
+       }
+       addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff;
+       addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6;
+       addr->il_csr =
+               ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE;
+       while ((addr->il_csr & IL_CDONE) == 0)
+               ;
+}
+
+/*
+ * Ethernet 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.
+ */
+iloutput(ifp, m0, dst)
+       struct ifnet *ifp;
+       struct mbuf *m0;
+       struct sockaddr *dst;
+{
+       int type, dest, s, error;
+       register struct il_softc *is = &il_softc[ifp->if_unit];
+       register struct mbuf *m = m0;
+       register struct ether_header *il;
+       register int off;
+
+       switch (dst->sa_family) {
+
+#ifdef INET
+       case AF_INET:
+               dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
+               off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
+               if (off > 0 && (off & 0x1ff) == 0 &&
+                   m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
+                       type = ETHERPUP_TRAIL + (off>>9);
+                       m->m_off -= 2 * sizeof (u_short);
+                       m->m_len += 2 * sizeof (u_short);
+                       *mtod(m, u_short *) = htons((u_short)ETHERPUP_IPTYPE);
+                       *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
+                       goto gottrailertype;
+               }
+               type = ETHERPUP_IPTYPE;
+               off = 0;
+               goto gottype;
+#endif
+
+       default:
+               printf("il%d: can't handle af%d\n", ifp->if_unit,
+                       dst->sa_family);
+               error = EAFNOSUPPORT;
+               goto bad;
+       }
+
+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 ether_header) > m->m_off) {
+               m = m_get(M_DONTWAIT, MT_HEADER);
+               if (m == 0) {
+                       error = ENOBUFS;
+                       goto bad;
+               }
+               m->m_next = m0;
+               m->m_off = MMINOFF;
+               m->m_len = sizeof (struct ether_header);
+       } else {
+               m->m_off -= sizeof (struct ether_header);
+               m->m_len += sizeof (struct ether_header);
+       }
+       il = mtod(m, struct ether_header *);
+       if ((dest &~ 0xff) == 0)
+               bcopy((caddr_t)ilbroadcastaddr, (caddr_t)il->ether_dhost, 6);
+       else {
+               u_char *to = dest & 0x8000 ? is->is_stats.ils_addr : il_ectop;
+
+               bcopy((caddr_t)to, (caddr_t)il->ether_dhost, 3);
+               il->ether_dhost[3] = (dest>>8) & 0x7f;
+               il->ether_dhost[4] = (dest>>16) & 0xff;
+               il->ether_dhost[5] = (dest>>24) & 0xff;
+       }
+       bcopy((caddr_t)is->is_stats.ils_addr, (caddr_t)il->ether_shost, 6);
+       il->ether_type = htons((u_short)type);
+
+       /*
+        * Queue message on interface, and start output if interface
+        * not yet active.
+        */
+       s = splimp();
+       if (IF_QFULL(&ifp->if_snd)) {
+               IF_DROP(&ifp->if_snd);
+               splx(s);
+               m_freem(m);
+               return (ENOBUFS);
+       }
+       IF_ENQUEUE(&ifp->if_snd, m);
+       if ((is->is_flags & ILF_OACTIVE) == 0)
+               ilstart(ifp->if_unit);
+       splx(s);
+       return (0);
+
+bad:
+       m_freem(m0);
+       return (error);
+}
+
+/*
+ * Watchdog routine, request statistics from board.
+ */
+ilwatch(unit)
+       int unit;
+{
+       register struct il_softc *is = &il_softc[unit];
+       register struct ifnet *ifp = &is->is_if;
+       int s;
+
+       if (is->is_flags & ILF_STATPENDING) {
+               ifp->if_timer = is->is_scaninterval;
+               return;
+       }
+       s = splimp();
+       is->is_flags |= ILF_STATPENDING;
+       if ((is->is_flags & ILF_OACTIVE) == 0)
+               ilstart(ifp->if_unit);
+       splx(s);
+       ifp->if_timer = is->is_scaninterval;
+}
+
+/*
+ * Total up the on-board statistics.
+ */
+iltotal(is)
+       register struct il_softc *is;
+{
+       register u_short *interval, *sum, *end;
+
+       interval = &is->is_stats.ils_frames;
+       sum = &is->is_sum.ils_frames;
+       end = is->is_sum.ils_fill2;
+       while (sum < end)
+               *sum++ += *interval++;
+       is->is_if.if_collisions = is->is_sum.ils_collis;
+}
diff --git a/a/sys/vaxif/if_uba.c b/a/sys/vaxif/if_uba.c
new file mode 100644 (file)
index 0000000..e7ce8d6
--- /dev/null
@@ -0,0 +1,251 @@
+/*     if_uba.c        4.16    82/12/17        */
+
+#include "../machine/pte.h"
+
+#include "../h/param.h"
+#include "../h/systm.h"
+#include "../h/mbuf.h"
+#include "../h/map.h"
+#include "../h/buf.h"
+#include "../h/cmap.h"
+#include "../h/vmmac.h"
+#include "../h/socket.h"
+
+#include "../net/if.h"
+
+#include "../vax/mtpr.h"
+#include "../vaxif/if_uba.h"
+#include "../vaxuba/ubareg.h"
+#include "../vaxuba/ubavar.h"
+
+/*
+ * Routines supporting UNIBUS network interfaces.
+ *
+ * TODO:
+ *     Support interfaces using only one BDP statically.
+ */
+
+/*
+ * Init UNIBUS for interface on uban whose headers of size hlen are to
+ * end on a page boundary.  We allocate a UNIBUS map register for the page
+ * with the header, and nmr more UNIBUS map registers for i/o on the adapter,
+ * doing this twice: once for reading and once for writing.  We also
+ * allocate page frames in the mbuffer pool for these pages.
+ */
+if_ubainit(ifu, uban, hlen, nmr)
+       register struct ifuba *ifu;
+       int uban, hlen, nmr;
+{
+       register caddr_t cp;
+       int i, ncl;
+
+       ncl = clrnd(nmr + CLSIZE) / CLSIZE;
+       if (ifu->ifu_r.ifrw_addr)
+               cp = ifu->ifu_r.ifrw_addr - (CLBYTES - hlen);
+       else {
+               cp = m_clalloc(2 * ncl, MPG_SPACE);
+               if (cp == 0)
+                       return (0);
+               ifu->ifu_r.ifrw_addr = cp + CLBYTES - hlen;
+               ifu->ifu_w.ifrw_addr = ifu->ifu_r.ifrw_addr + ncl * CLBYTES;
+               ifu->ifu_hlen = hlen;
+               ifu->ifu_uban = uban;
+               ifu->ifu_uba = uba_hd[uban].uh_uba;
+       }
+       if (if_ubaalloc(ifu, &ifu->ifu_r, nmr) == 0)
+               goto bad;
+       if (if_ubaalloc(ifu, &ifu->ifu_w, nmr) == 0)
+               goto bad2;
+       for (i = 0; i < nmr; i++)
+               ifu->ifu_wmap[i] = ifu->ifu_w.ifrw_mr[i];
+       ifu->ifu_xswapd = 0;
+       return (1);
+bad2:
+       ubarelse(ifu->ifu_uban, &ifu->ifu_r.ifrw_info);
+bad:
+       m_pgfree(cp, 2 * ncl);
+       ifu->ifu_r.ifrw_addr = 0;
+       return (0);
+}
+
+/*
+ * Setup either a ifrw structure by allocating UNIBUS map registers,
+ * possibly a buffered data path, and initializing the fields of
+ * the ifrw structure to minimize run-time overhead.
+ */
+static
+if_ubaalloc(ifu, ifrw, nmr)
+       struct ifuba *ifu;
+       register struct ifrw *ifrw;
+       int nmr;
+{
+       register int info;
+
+       info =
+           uballoc(ifu->ifu_uban, ifrw->ifrw_addr, nmr*NBPG + ifu->ifu_hlen,
+               ifu->ifu_flags);
+       if (info == 0)
+               return (0);
+       ifrw->ifrw_info = info;
+       ifrw->ifrw_bdp = UBAI_BDP(info);
+       ifrw->ifrw_proto = UBAMR_MRV | (UBAI_BDP(info) << UBAMR_DPSHIFT);
+       ifrw->ifrw_mr = &ifu->ifu_uba->uba_map[UBAI_MR(info) + 1];
+       return (1);
+}
+
+/*
+ * Pull read data off a interface.
+ * Len is length of data, with local net header stripped.
+ * Off is non-zero if a trailer protocol was used, and
+ * gives the offset of the trailer information.
+ * We copy the trailer information and then all the normal
+ * data into mbufs.  When full cluster sized units are present
+ * on the interface on cluster boundaries we can get them more
+ * easily by remapping, and take advantage of this here.
+ */
+struct mbuf *
+if_rubaget(ifu, totlen, off0)
+       register struct ifuba *ifu;
+       int totlen, off0;
+{
+       struct mbuf *top, **mp, *m;
+       int off = off0, len;
+       register caddr_t cp = ifu->ifu_r.ifrw_addr + ifu->ifu_hlen;
+
+
+       top = 0;
+       mp = &top;
+       while (totlen > 0) {
+               MGET(m, M_DONTWAIT, MT_DATA);
+               if (m == 0)
+                       goto bad;
+               if (off) {
+                       len = totlen - off;
+                       cp = ifu->ifu_r.ifrw_addr + ifu->ifu_hlen + off;
+               } else
+                       len = totlen;
+               if (len >= CLBYTES) {
+                       struct mbuf *p;
+                       struct pte *cpte, *ppte;
+                       int x, *ip, i;
+
+                       MCLGET(p, 1);
+                       if (p == 0)
+                               goto nopage;
+                       len = m->m_len = CLBYTES;
+                       m->m_off = (int)p - (int)m;
+                       if (!claligned(cp))
+                               goto copy;
+
+                       /*
+                        * Switch pages mapped to UNIBUS with new page p,
+                        * as quick form of copy.  Remap UNIBUS and invalidate.
+                        */
+                       cpte = &Mbmap[mtocl(cp)*CLSIZE];
+                       ppte = &Mbmap[mtocl(p)*CLSIZE];
+                       x = btop(cp - ifu->ifu_r.ifrw_addr);
+                       ip = (int *)&ifu->ifu_r.ifrw_mr[x];
+                       for (i = 0; i < CLSIZE; i++) {
+                               struct pte t;
+                               t = *ppte; *ppte++ = *cpte; *cpte = t;
+                               *ip++ =
+                                   cpte++->pg_pfnum|ifu->ifu_r.ifrw_proto;
+                               mtpr(TBIS, cp);
+                               cp += NBPG;
+                               mtpr(TBIS, (caddr_t)p);
+                               p += NBPG / sizeof (*p);
+                       }
+                       goto nocopy;
+               }
+nopage:
+               m->m_len = MIN(MLEN, len);
+               m->m_off = MMINOFF;
+copy:
+               bcopy(cp, mtod(m, caddr_t), (unsigned)m->m_len);
+               cp += m->m_len;
+nocopy:
+               *mp = m;
+               mp = &m->m_next;
+               if (off) {
+                       /* sort of an ALGOL-W style for statement... */
+                       off += m->m_len;
+                       if (off == totlen) {
+                               cp = ifu->ifu_r.ifrw_addr + ifu->ifu_hlen;
+                               off = 0;
+                               totlen = off0;
+                       }
+               } else
+                       totlen -= m->m_len;
+       }
+       return (top);
+bad:
+       m_freem(top);
+       return (0);
+}
+
+/*
+ * Map a chain of mbufs onto a network interface
+ * in preparation for an i/o operation.
+ * The argument chain of mbufs includes the local network
+ * header which is copied to be in the mapped, aligned
+ * i/o space.
+ */
+if_wubaput(ifu, m)
+       register struct ifuba *ifu;
+       register struct mbuf *m;
+{
+       register struct mbuf *mp;
+       register caddr_t cp, dp;
+       register int i;
+       int xswapd = 0;
+       int x, cc;
+
+       cp = ifu->ifu_w.ifrw_addr;
+       while (m) {
+               dp = mtod(m, char *);
+               if (claligned(cp) && claligned(dp) && m->m_len == CLBYTES) {
+                       struct pte *pte; int *ip;
+                       pte = &Mbmap[mtocl(dp)*CLSIZE];
+                       x = btop(cp - ifu->ifu_w.ifrw_addr);
+                       ip = (int *)&ifu->ifu_w.ifrw_mr[x];
+                       for (i = 0; i < CLSIZE; i++)
+                               *ip++ =
+                                   ifu->ifu_w.ifrw_proto | pte++->pg_pfnum;
+                       xswapd |= 1 << (x>>(CLSHIFT-PGSHIFT));
+                       mp = m->m_next;
+                       m->m_next = ifu->ifu_xtofree;
+                       ifu->ifu_xtofree = m;
+                       cp += m->m_len;
+               } else {
+                       bcopy(mtod(m, caddr_t), cp, (unsigned)m->m_len);
+                       cp += m->m_len;
+                       MFREE(m, mp);
+               }
+               m = mp;
+       }
+
+       /*
+        * Xswapd is the set of clusters we just mapped out.  Ifu->ifu_xswapd
+        * is the set of clusters mapped out from before.  We compute
+        * the number of clusters involved in this operation in x.
+        * Clusters mapped out before and involved in this operation
+        * should be unmapped so original pages will be accessed by the device.
+        */
+       cc = cp - ifu->ifu_w.ifrw_addr;
+       x = ((cc - ifu->ifu_hlen) + CLBYTES - 1) >> CLSHIFT;
+       ifu->ifu_xswapd &= ~xswapd;
+       xswapd &= ~ifu->ifu_xswapd;
+       while (i = ffs(ifu->ifu_xswapd)) {
+               i--;
+               if (i >= x)
+                       break;
+               ifu->ifu_xswapd &= ~(1<<i);
+               i *= CLSIZE;
+               for (x = 0; x < CLSIZE; x++) {
+                       ifu->ifu_w.ifrw_mr[i] = ifu->ifu_wmap[i];
+                       i++;
+               }
+       }
+       ifu->ifu_xswapd |= xswapd;
+       return (cc);
+}