use shorts to move data
[unix-history] / usr / src / sys / vax / if / if_ec.c
index 1def20f..3aa5f69 100644 (file)
@@ -1,7 +1,8 @@
-/*     if_ec.c 4.2     82/04/11        */
+/*     if_ec.c 4.12    82/05/27        */
 
 #include "ec.h"
 #include "imp.h"
 
 #include "ec.h"
 #include "imp.h"
+#include "loop.h"
 
 /*
  * 3Com Ethernet Controller interface
 
 /*
  * 3Com Ethernet Controller interface
@@ -38,11 +39,14 @@ struct      uba_device *ecinfo[NEC];
 u_short ecstd[] = { 0 };
 struct uba_driver ecdriver =
        { ecprobe, 0, ecattach, 0, ecstd, "ec", ecinfo };
 u_short ecstd[] = { 0 };
 struct uba_driver ecdriver =
        { ecprobe, 0, ecattach, 0, ecstd, "ec", ecinfo };
+u_char ec_iltop[3] = { 0x02, 0x07, 0x01 };
 #define        ECUNIT(x)       minor(x)
 
 int    ecinit(),ecoutput(),ecreset();
 struct mbuf *ecget();
 
 #define        ECUNIT(x)       minor(x)
 
 int    ecinit(),ecoutput(),ecreset();
 struct mbuf *ecget();
 
+extern struct ifnet loif;
+
 /*
  * Ethernet software status per interface.
  *
 /*
  * Ethernet software status per interface.
  *
@@ -58,11 +62,7 @@ struct mbuf *ecget();
 struct ec_softc {
        struct  ifnet es_if;            /* network-visible interface */
        struct  ifuba es_ifuba;         /* UNIBUS resources */
 struct ec_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_mask;                /* mask for current output delay */
-#ifdef notdef
-       long    es_lastx;               /* host last transmitted to */
-#endif
        short   es_oactive;             /* is output active? */
        caddr_t es_buf[16];             /* virtual addresses of buffers */
        u_char  es_enaddr[6];           /* board's ethernet address */
        short   es_oactive;             /* is output active? */
        caddr_t es_buf[16];             /* virtual addresses of buffers */
        u_char  es_enaddr[6];           /* board's ethernet address */
@@ -84,6 +84,10 @@ COUNT(ECPROBE);
        br = 0; cvec = br; br = cvec;
        ecrint(0); ecxint(0); eccollide(0);
 #endif
        br = 0; cvec = br; br = cvec;
        ecrint(0); ecxint(0); eccollide(0);
 #endif
+       /*
+        * Make sure memory is turned on
+        */
+       addr->ec_rcr = EC_AROM;
        /*
         * Check for existence of buffers on Unibus.
         * This won't work on a 780 until more work is done.
        /*
         * Check for existence of buffers on Unibus.
         * This won't work on a 780 until more work is done.
@@ -182,7 +186,7 @@ COUNT(ECATTACH);
 #if NIMP == 0
        /* here's one for you john baby.... */
        if (ui->ui_flags &~ 0xff)
 #if NIMP == 0
        /* here's one for you john baby.... */
        if (ui->ui_flags &~ 0xff)
-               eclhinit((ui->ui_flags &~ 0xff) | 0x0a);
+               eclhinit(&es->es_if, (ui->ui_flags &~ 0xff) | 0x0a);
 #endif
 }
 
 #endif
 }
 
@@ -216,19 +220,13 @@ ecinit(unit)
        register i;
        int s;
 
        register i;
        int s;
 
-#ifdef notdef
-       if (if_ubainit(&es->es_ifuba, ui->ui_ubanum,
-           sizeof (struct ec_header), (int)btoc(ECMTU)) == 0) { 
-               printf("ec%d: can't initialize\n", unit);
-               es->es_if.if_flags &= ~IFF_UP;
-               return;
-       }
-#endif
        addr = (struct ecdevice *)ui->ui_addr;
 
        /*
         * Hang receive buffers and start any pending
         * writes by faking a transmit complete.
        addr = (struct ecdevice *)ui->ui_addr;
 
        /*
         * Hang receive buffers and start any pending
         * writes by faking a transmit complete.
+        * Writing into the rcr also makes sure the memory
+        * is turned on.
         */
        s = splimp();
        for (i=ECRHBF; i>=ECRLBF; i--)
         */
        s = splimp();
        for (i=ECRHBF; i>=ECRLBF; i--)
@@ -240,16 +238,10 @@ ecinit(unit)
        if_rtinit(&es->es_if, RTF_DIRECT|RTF_UP);
 }
 
        if_rtinit(&es->es_if, RTF_DIRECT|RTF_UP);
 }
 
-#ifdef notdef
-int    enalldelay = 0;
-int    eclastdel = 25;
-int    enlastmask = (~0) << 5;
-#endif
-
 /*
  * Start or restart output on interface.
  * If interface is already active, then this is a retransmit
 /*
  * 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.
+ * 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.
  * 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.
@@ -279,27 +271,8 @@ COUNT(ECSTART);
                es->es_oactive = 0;
                return;
        }
                es->es_oactive = 0;
                return;
        }
-#ifdef notdef
-       dest = mtod(m, struct ec_header *)->ec_dhost; /* wrong! */
-#endif
        ecput(es->es_buf[ECTBF], m);
 
        ecput(es->es_buf[ECTBF], m);
 
-#ifdef notdef
-       /*
-        * Ethernet cannot take back-to-back packets (no
-        * buffering in interface).  To avoid overrunning
-        * receivers, enforce a small delay (about 1ms) in interface:
-        *      * between all packets when ecalldelay
-        *      * 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 = eclastdel;
-               es->es_mask = eclastmask;
-       }
-       es->es_lastx = dest;
-#endif
-
 restart:
        /*
         * Start the output.
 restart:
        /*
         * Start the output.
@@ -327,7 +300,6 @@ COUNT(ECXINT);
                printf("ec%d: strange xmit interrupt!\n", unit);
        es->es_if.if_opackets++;
        es->es_oactive = 0;
                printf("ec%d: strange xmit interrupt!\n", unit);
        es->es_if.if_opackets++;
        es->es_oactive = 0;
-       es->es_delay = 0;
        es->es_mask = ~0;
        addr->ec_xcr = EC_XCLR;
        /*
        es->es_mask = ~0;
        addr->ec_xcr = EC_XCLR;
        /*
@@ -338,9 +310,6 @@ COUNT(ECXINT);
                es->es_ifuba.ifu_xtofree = 0;
        }
        if (es->es_if.if_snd.ifq_head == 0) {
                es->es_ifuba.ifu_xtofree = 0;
        }
        if (es->es_if.if_snd.ifq_head == 0) {
-#ifdef notdef
-               es->es_lastx = 0; /* ? */
-#endif
                return;
        }
        ecstart(unit);
                return;
        }
        ecstart(unit);
@@ -357,7 +326,7 @@ eccollide(unit)
        struct ec_softc *es = &ec_softc[unit];
 COUNT(ECCOLLIDE);
 
        struct ec_softc *es = &ec_softc[unit];
 COUNT(ECCOLLIDE);
 
-       printf("ec%d: eccollide\n", unit);
+       printf("ec%d: collision\n", unit);
        es->es_if.if_collisions++;
        if (es->es_oactive == 0)
                return;
        es->es_if.if_collisions++;
        if (es->es_oactive == 0)
                return;
@@ -368,6 +337,10 @@ ecdocoll(unit)
        int unit;
 {
        register struct ec_softc *es = &ec_softc[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
 
        /*
         * Es_mask is a 16 bit number with n low zero bits, with
@@ -375,31 +348,41 @@ ecdocoll(unit)
         * backed off 16 times, and give up.
         */
        if (es->es_mask == 0) {
         * backed off 16 times, and give up.
         */
        if (es->es_mask == 0) {
+               es->es_if.if_oerrors++;
                printf("ec%d: send error\n", unit);
                /*
                printf("ec%d: send error\n", unit);
                /*
-                * this makes enxint wrong.  fix later.
+                * 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).
                 */
                 */
-               ecxint(unit);
+               es->es_oactive = 0;
+               es->es_mask = ~0;
+               if (es->es_if.if_snd.ifq_head)
+                       ecstart(unit);
                return;
        }
        /*
                return;
        }
        /*
-        * Another backoff.  Restart with delay based on n low bits
-        * of the interval timer.
+        * 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;
         */
        es->es_mask <<= 1;
-       es->es_delay = mfpr(ICR) &~ es->es_mask;
+       delay = mfpr(ICR) &~ es->es_mask;
+       DELAY(delay * 51);
        /*
        /*
-        * This should do some sort of delay before calling ecstart.
-        * I'll figure this out later.
+        * Clear the controller's collision flag, thus enabling retransmit.
         */
         */
-       ecstart(unit);
+       addr->ec_xcr = EC_JINTEN|EC_XINTEN|EC_JCLR;
 }
 
 }
 
-#ifdef notdef
-struct sockaddr_pup pupsrc = { AF_PUP };
-struct sockaddr_pup pupdst = { AF_PUP };
-struct sockproto pupproto = { PF_PUP };
-#endif
 /*
  * Ethernet interface receiver interrupt.
  * If input error just drop packet.
 /*
  * Ethernet interface receiver interrupt.
  * If input error just drop packet.
@@ -415,9 +398,6 @@ ecrint(unit)
        struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
 COUNT(ECRINT);
 
        struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
 COUNT(ECRINT);
 
-#ifdef notdef
-       printf("ec%d: ecrint:%d\n", unit, addr->ec_rcr & 0xf);
-#endif
        while (addr->ec_rcr & EC_RDONE)
                ecread(unit);
 }
        while (addr->ec_rcr & EC_RDONE)
                ecread(unit);
 }
@@ -442,7 +422,7 @@ COUNT(ECREAD);
                panic("ecrint");
        ecbuf = es->es_buf[buf];
        ecoff = *(short *)ecbuf;
                panic("ecrint");
        ecbuf = es->es_buf[buf];
        ecoff = *(short *)ecbuf;
-       if (ecoff < ECRDOFF || ecoff >= ECMTU+ECRDOFF) {
+       if (ecoff <= ECRDOFF || ecoff > 2046) {
                es->es_if.if_ierrors++;
 #ifdef notdef
                if (es->es_if.if_ierrors % 100 == 0)
                es->es_if.if_ierrors++;
 #ifdef notdef
                if (es->es_if.if_ierrors % 100 == 0)
@@ -497,20 +477,6 @@ COUNT(ECREAD);
                schednetisr(NETISR_IP);
                inq = &ipintrq;
                break;
                schednetisr(NETISR_IP);
                inq = &ipintrq;
                break;
-#endif
-#ifdef notdef
-#ifdef PUP
-       case ECPUP_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 *)&pupdst,
-                 (struct sockaddr *)&pupsrc);
-               goto setup;
-       }
-#endif
 #endif
        default:
                m_freem(m);
 #endif
        default:
                m_freem(m);
@@ -535,6 +501,9 @@ setup:
  * Encapsulate a packet of type family for the local net.
  * Use trailer local net encapsulation if enough data in first
  * packet leaves a multiple of 512 bytes of data in remainder.
  * Encapsulate a packet of type family for the local net.
  * Use trailer local net encapsulation if enough data in first
  * packet leaves a multiple of 512 bytes of data in remainder.
+ * 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;
  */
 ecoutput(ifp, m0, dst)
        struct ifnet *ifp;
@@ -547,6 +516,7 @@ ecoutput(ifp, m0, dst)
        register struct ec_header *ec;
        register int off;
        register int i;
        register struct ec_header *ec;
        register int off;
        register int i;
+       struct mbuf *mcopy = (struct mbuf *) 0;         /* Null */
 
 COUNT(ECOUTPUT);
        switch (dst->sa_family) {
 
 COUNT(ECOUTPUT);
        switch (dst->sa_family) {
@@ -554,6 +524,13 @@ COUNT(ECOUTPUT);
 #ifdef INET
        case AF_INET:
                dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
 #ifdef INET
        case AF_INET:
                dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
+               if ((dest &~ 0xff) == 0)
+                       mcopy = m_copy(m, 0, 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)) {
                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)) {
@@ -567,15 +544,6 @@ COUNT(ECOUTPUT);
                type = ECPUP_IPTYPE;
                off = 0;
                goto gottype;
                type = ECPUP_IPTYPE;
                off = 0;
                goto gottype;
-#endif
-#ifdef notdef
-#ifdef PUP
-       case AF_PUP:
-               dest = ((struct sockaddr_pup *)dst)->spup_addr.pp_host;
-               type = ECPUP_PUPTYPE;
-               off = 0;
-               goto gottype;
-#endif
 #endif
 
        default:
 #endif
 
        default:
@@ -623,10 +591,16 @@ gottype:
                for (i=0; i<6; i++)
                        ec->ec_dhost[i] = 0xff;
        else {
                for (i=0; i<6; i++)
                        ec->ec_dhost[i] = 0xff;
        else {
-               ec->ec_dhost[0] = es->es_enaddr[0];
-               ec->ec_dhost[1] = es->es_enaddr[1];
-               ec->ec_dhost[2] = es->es_enaddr[2];
-               ec->ec_dhost[3] = (dest>>8) & 0xff;
+               if (dest & 0x8000) {
+                       ec->ec_dhost[0] = ec_iltop[0];
+                       ec->ec_dhost[1] = ec_iltop[1];
+                       ec->ec_dhost[2] = ec_iltop[2];
+               } else {
+                       ec->ec_dhost[0] = es->es_enaddr[0];
+                       ec->ec_dhost[1] = es->es_enaddr[1];
+                       ec->ec_dhost[2] = es->es_enaddr[2];
+               }
+               ec->ec_dhost[3] = (dest>>8) & 0x7f;
                ec->ec_dhost[4] = (dest>>16) & 0xff;
                ec->ec_dhost[5] = (dest>>24) & 0xff;
        }
                ec->ec_dhost[4] = (dest>>16) & 0xff;
                ec->ec_dhost[5] = (dest>>24) & 0xff;
        }
@@ -646,7 +620,11 @@ gottype:
        if (es->es_oactive == 0)
                ecstart(ifp->if_unit);
        splx(s);
        if (es->es_oactive == 0)
                ecstart(ifp->if_unit);
        splx(s);
-       return (0);
+gotlocal:
+       if (mcopy)                              /* Kludge, but it works! */
+               return(looutput(&loif, mcopy, dst));
+       else
+               return (0);
 qfull:
        m0 = m;
        splx(s);
 qfull:
        m0 = m;
        splx(s);
@@ -677,7 +655,19 @@ COUNT(ECPUT);
        mp = m;
        while (mp) {
                mcp = mtod(mp, char *);
        mp = m;
        while (mp) {
                mcp = mtod(mp, char *);
+               i = 0;
+               if ((int)bp&1) {
+                       *bp++ = *mcp++;
+                       i++;
+               }
                for (i=0; i<mp->m_len; i++)
                for (i=0; i<mp->m_len; i++)
+               while (i < mp->m_len) {
+                       *(short *)bp = *(short *)mcp;
+                       bp += 2;
+                       mcp += 2;
+                       i += 2;
+               }
+               if (mp->m_len&1)
                        *bp++ = *mcp++;
                mp = m_free(mp);
        }
                        *bp++ = *mcp++;
                mp = m_free(mp);
        }
@@ -688,6 +678,9 @@ COUNT(ECPUT);
 /*
  * Routine to copy from UNIBUS memory into mbufs.
  * Similar in spirit to if_rubaget.
 /*
  * 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)
  */
 struct mbuf *
 ecget(ecbuf, totlen, off0)
@@ -729,7 +722,12 @@ COUNT(ECGET);
                        m->m_off = MMINOFF;
                }
                mcp = mtod(m, char *);
                        m->m_off = MMINOFF;
                }
                mcp = mtod(m, char *);
-               for (i=0; i<len; i++)
+               for (i=0; i<len; i+=2) {
+                       *(short *)mcp = *(short *)cp;
+                       mcp += 2;
+                       cp += 2;
+               }
+               if (len&1)
                        *mcp++ = *cp++;
                *mp = m;
                mp = &m->m_next;
                        *mcp++ = *cp++;
                *mp = m;
                mp = &m->m_next;
@@ -760,15 +758,15 @@ bad:
  */
 
 struct ifnet eclhif;
  */
 
 struct ifnet eclhif;
-int    eclhoutput();
+int    looutput();
 
 /*
  * Called by localnet interface to allow logical
 
 /*
  * Called by localnet interface to allow logical
- * host interface to "attach".  Nothing should ever
- * be sent locally to this interface, it's purpose
+ * host interface to "attach", it's purpose
  * is simply to establish the host's arpanet address.
  */
  * is simply to establish the host's arpanet address.
  */
-eclhinit(addr)
+eclhinit(ecifp, addr)
+       struct ifnet *ecifp;
        int addr;
 {
        register struct ifnet *ifp = &eclhif;
        int addr;
 {
        register struct ifnet *ifp = &eclhif;
@@ -780,20 +778,12 @@ COUNT(ECLHINIT);
        sin = (struct sockaddr_in *)&ifp->if_addr;
        sin->sin_family = AF_INET;
        sin->sin_addr.s_addr = addr;
        sin = (struct sockaddr_in *)&ifp->if_addr;
        sin->sin_family = AF_INET;
        sin->sin_addr.s_addr = addr;
+       sin->sin_addr.s_lh = ecifp->if_host[0];
        ifp->if_net = sin->sin_addr.s_net;
        ifp->if_net = sin->sin_addr.s_net;
-       ifp->if_flags = IFF_UP;
-       ifp->if_output = eclhoutput;    /* should never be used */
+       ifp->if_dstaddr = ifp->if_addr;
+       ifp->if_flags = IFF_UP|IFF_POINTOPOINT;
+       ifp->if_output = looutput;
        if_attach(ifp);
        if_attach(ifp);
-}
-
-eclhoutput(ifp, m0, dst)
-       struct ifnet *ifp;
-       struct mbuf *m0;
-       struct sockaddr *dst;
-{
-COUNT(ECLHOUTPUT);
-       ifp->if_oerrors++;
-       m_freem(m0);
-       return (0);
+       rtinit(&ifp->if_addr, &ifp->if_addr, RTF_UP|RTF_DIRECT|RTF_HOST);
 }
 #endif
 }
 #endif