-/* if_ec.c 4.2 82/04/11 */
+/* if_ec.c 4.12 82/05/27 */
#include "ec.h"
#include "imp.h"
+#include "loop.h"
/*
* 3Com Ethernet Controller interface
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();
+extern struct ifnet loif;
+
/*
* Ethernet software status per interface.
*
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 */
-#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 */
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.
#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
}
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.
+ * Writing into the rcr also makes sure the memory
+ * is turned on.
*/
s = splimp();
for (i=ECRHBF; i>=ECRLBF; i--)
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
- * 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.
es->es_oactive = 0;
return;
}
-#ifdef notdef
- dest = mtod(m, struct ec_header *)->ec_dhost; /* wrong! */
-#endif
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.
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_ifuba.ifu_xtofree = 0;
}
if (es->es_if.if_snd.ifq_head == 0) {
-#ifdef notdef
- es->es_lastx = 0; /* ? */
-#endif
return;
}
ecstart(unit);
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;
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
* backed off 16 times, and give up.
*/
if (es->es_mask == 0) {
+ es->es_if.if_oerrors++;
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;
}
/*
- * 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_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.
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);
}
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)
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);
* 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;
register struct ec_header *ec;
register int off;
register int i;
+ struct mbuf *mcopy = (struct mbuf *) 0; /* Null */
COUNT(ECOUTPUT);
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, 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 = 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:
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;
}
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);
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++)
+ 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);
}
/*
* 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)
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;
*/
struct ifnet eclhif;
-int eclhoutput();
+int looutput();
/*
* 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.
*/
-eclhinit(addr)
+eclhinit(ecifp, addr)
+ struct ifnet *ecifp;
int addr;
{
register struct ifnet *ifp = &eclhif;
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_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);
-}
-
-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