|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T i
Length: 18664 (0x48e8)
Types: TextFile
Names: »ie.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
└─⟦3b20aab50⟧ »EurOpenD3/network/snmp/kip-snmp.91.tar.Z«
└─⟦b503a39fe⟧
└─⟦this⟧ »kip/ie.c«
/*
* Intel 82586 ethernet chip driver (ie = Intel Ethernet)
*
* (c) 1984, Stanford Univ. SUMEX project.
* May be used but not sold without permission.
*
* (c) 1986, Kinetics, Inc.
* May be used but not sold without permission.
*
*/
#include "gw.h"
#include "fp/pbuf.h"
#include "ie.h"
#include "ether.h"
#include "ab.h"
#include "fp/cmdmacro.h"
#ifdef SNMP
#include "inet.h"
#include "mib.h"
#include "snmp_vars.h"
#endif
extern char broadcastaddr[];
extern struct pqueue *sendq;
extern struct pqueue *pq;
extern struct fp_promram pvars;
/*
* wait for scb command word to clear
* After giving a command, the 82586 steals the bus
* and proceeds to capture the command end execute
* it. When execution is completed, it clears the
* command word. In general, we only check that
* the command word is clear before we issue a
* new command.
*/
#define WAIT_CMD() { \
while (scbptr->sc_cmd != 0) \
pvars.fpr_iestats->fpi_cmdbusy++; \
}
/*
* if the receiver is in the suspended state, give it the resume
* command to get it in a state ready to receive Ethernet packets again.
*/
#define RESUME() { \
if ((scbptr->sc_status & SWAB(SC_RUS)) == SWAB(RUS_SUSP)) { \
WAIT_CMD(); \
scbptr->sc_cmd |= SWAB(RUC_RES); \
K_CA86(); \
} \
}
#define ierstart() WAIT_CMD(); \
rfdhead->fd_rbd = SWAB(LO16(rbdhead)); \
scbptr->sc_rlist = SWAB(LO16(rfdhead))
/* transmit buffers */
#define NTBD 1 /* number of transmit buffers */
struct t_bd tbd[NTBD];
/* receiver frame descriptors */
#define NRFD 2 /* number of receive frame descriptors */
struct fdes rfd[NRFD]; /* receive frame descriptors */
struct fdes *rfdhead; /* first in the list */
struct fdes *rfdtail; /* end of the list */
#define NRBD 2 /* number of receive buffers */
struct r_bd rbd[NRBD]; /* receive buffers */
struct r_bd *rbdhead; /* first in the list */
struct r_bd *rbdtail; /* end of the list */
char tactive; /* transmit active */
#ifdef STATS
/* statistics */
int iespur; /* count of spurious interrupts */
int iefalsexmit; /* false transmit interrupt */
int eintr; /* count of ethernet interrupts */
int iestuck; /* count of stuck interrupts */
int oknotset; /* count of times ok bit is not set */
int okbits; /* bit settings last time ok not set */
int ieready; /* count of times receive unit already READY */
int iefcnt; /* count of times that no frame descrip left */
int iebuf; /* count of times that no rbuf descrip left */
int norecbufs; /* count of times there are no receive pbufs */
#endif STATS
struct scb *scbptr;
struct cb *cbptr;
struct fdes *fdptr;
struct cb xmit; /* transmit command buffer */
struct cb iasetup; /* Ethernet address setup command buffer */
struct cb ieconfig; /* Ethernet configuration command buffer */
#ifndef SMARTLINK
int scbbase ;
struct scb *scbaddr;
struct cb *cmdaddr;
struct fdes *fdesaddr;
#endif
/*
* Ether slop factor. Pbuf MAXDATA is 630, but (sizeof ether (14) +
* sizeof ip (20) + sizeof udp (8) + max lap (603)) = 645. This means
* that max size appletalk packets from the ether were being lost.
* We adjust the receive point in the pbuf upwards by at least 15 bytes
* so these will fit. Actually the real solution would be to increase
* MAXDATA to 1K or so, but (1) this is currently hardwired into the PROM;
* (2) KFPS is already tight on buffer memory.
*/
#define ESLOP 20
/*
* configure and ethernet address setup of the chip
*/
config()
{
#ifndef NOETHERXMIT
extern struct ifnet ifie, ifet;
#endif
extern u_char etheraddr[];
scbptr->sc_clist = SWAB(LO16(&ieconfig));
iecmd(); /* configure */
/* ethernet address loaded to i85286 */
eaddrcopy(etheraddr, &iasetup.cb_param[0]);
scbptr->sc_clist = SWAB(LO16(&iasetup));
iecmd(); /* iasetup */
#ifndef NOETHERXMIT
/* set ether address in interface struct */
eaddrcopy(etheraddr, ifie.if_haddr);
/* And also in the ETHERTALK interface */
eaddrcopy(etheraddr, ifet.if_haddr);
#endif
}
/*
* Initialize the i82586 chip
*/
ie_init()
{
register int ss;
register struct fdes *rfdp;
register struct r_bd *rbdp;
struct pbuf *pp;
extern u_short ienet;
#ifndef SMARTLINK
scbbase = (int)&k_scb & 0xff0000;
ss = scbbase;
scbaddr = (struct scb *)(ss + LO16(&k_scb));
cmdaddr = (struct cb *)(ss + LO16(&k_cb));
fdesaddr = (struct fdes *)(ss + LO16(&k_fdes));
#endif
/* initialize scb structure */
scbptr = SCBADDR;
/*
* normally CPU should never write the status word
* but 82586 'ors' bits into it so it should begin life blank
*/
/* empty status */
scbptr->sc_status = SWAB(STAT_IDLE | CUS_IDLE | RUS_IDLE);
/* no commands */
scbptr->sc_cmd = SWAB(ACK_NONE | CUC_NOP | RUC_NOP);
scbptr->sc_clist = SWAB(LO16(CMDADDR));
scbptr->sc_rlist = SWAB(LO16(FDESADDR));
/*
* these are fetched, incremented and replaced by 82586
* without releasing the bus. they stick at 0xffff when full
*/
scbptr->sc_crcerrs = SWAB(0);
scbptr->sc_alnerrs = SWAB(0);
scbptr->sc_rscerrs = SWAB(0);
scbptr->sc_ovrnerrs = SWAB(0);
/* initialize the receive frame descriptor block */
fdptr = FDESADDR;
fdptr->fd_status = SWAB(0);
fdptr->fd_stat2 = SWAB(0);
fdptr->fd_link = SWAB(CB_NIL);
fdptr->fd_rbd = SWAB(FD_NIL);
for (ss = 0; ss < 6; ss++) {
fdptr->fd_daddr[ss] = '\0';
fdptr->fd_saddr[ss] = '\0';
}
fdptr->fd_type = SWAB(0);
/* hardware reset and establish our scb as the one to use */
K_SET86(scbptr);
/* configure and iasetup */
config();
/* transmit buffer stuff is set up at compile time, except... */
xmit.cb_param[0] = SWAB(LO16(&tbd[0]));
tbd[0].tpbuf = 0;
scbptr->sc_clist = SWAB(LO16(&xmit));
/*
* setup receiver structures
*/
for (rfdp = &rfd[0] ; rfdp < &rfd[NRFD] ; rfdp++ ) {
rfdp->fd_link = SWAB(LO16(rfdp+1));
rfdp->fd_rbd = SWAB(FD_NIL);
#ifndef FASTRECV
rfdp->fd_stat2 = SWAB(FD_S);
#else
rfdp->fd_stat2 = 0;
#endif
}
rfdp--;
rfdtail = rfdp;
rfdp->fd_stat2 |= SWAB(FD_EL);
rfdp->fd_link = SWAB(LO16(&rfd[0]));
rfdhead = rfdp = &rfd[0];
rfdp->fd_rbd = SWAB(LO16(&rbd[0]));
for (rbdp = &rbd[0]; rbdp < &rbd[NRBD]; rbdp++) {
/* get a buffer from the free list */
/* should already be at highest priority */
K_PGET(PT_ERBF,pp);
rbdp->rbd.bd_next = SWAB(LO16(rbdp + 1));
rbdp->rpbuf = pp;
if (pp) {
/* make the pbuf into an Ethernet receive buffer */
rbdp->rbd.bd_buf = SWAB(LO16(&pp->p_data[-ESLOP]));
rbdp->rbd.bd_bufhi = SWAB(HI16(&pp->p_data[-ESLOP]));
rbdp->rbd.bd_size = SWAB(MAXDATA+ESLOP);
}
}
rbdp--;
rbdtail = rbdp;
rbdp->rbd.bd_next = SWAB(LO16(&rbd[0]));
rbdp->rbd.bd_size |= SWAB(BD_EL);
rbdhead = &rbd[0];
scbptr->sc_rlist = SWAB(LO16(&rfd[0]));
scbptr->sc_cmd = SWAB(ACK_CX | ACK_CNA | ACK_FR | ACK_RNR);
K_CA86();
WAIT_CMD(); /* wait until chip is completely ready */
/* send 1st receive cmd */
ierstart();
scbptr->sc_cmd = SWAB(RUC_START);
K_CA86();
}
/*
* Handle Ethernet interrupt.
*/
ieintr()
{
register short scbstatus;
register short scbcmd;
extern struct ifnet ifie;
scbstatus = scbptr->sc_status;
scbcmd = SWAB(ACK_NONE | CUC_NOP | RUC_NOP);
/* resume the receive unit if necessary */
RESUME();
#ifdef STATS
eintr++;
if ((scbstatus & SWAB(STAT_FR | STAT_CX)) == 0) {
/* spurious interrupt */
iespur++;
}
#endif STATS
if ((scbstatus & SWAB(STAT_RNR))&&(rfdhead->fd_status & SWAB(FD_B))) {
register struct r_bd *rbdp;
/*
* Receiver not ready, yet still busy on 1st frame!
* This is a bogus packet of 'infinite' length
* and all ones. Restart the RU.
*
* 4/29/86 - experiments indicate that this never
* happened in numerous startings of the gateway
* and over 250,000 packets. leftover from the original
* Croft Seagate code, and still included anyway -tim
*/
for (rbdp = OTOA(struct r_bd *, rfdhead->fd_rbd);
rbdp->rbd.bd_count & SWAB(BD_F);
rbdp = OTOA(struct r_bd *, rbdp->rbd.bd_next)) {
rbdp->rbd.bd_count = SWAB(0);
}
#ifdef STATS
iestuck++;
#endif STATS
ierstart();
scbcmd = SWAB(ACK_RNR | RUC_START);
}
#ifndef NOETHERXMIT
if (scbstatus & SWAB(STAT_CX)) {
/* interrupt after command (transmit) executed */
if ((tactive == 0) ||
((xmit.cb_status & SWAB(CB_COMPLETE)) == 0)) {
#ifdef STATS
/* false transmit interrupt */
iefalsexmit++;
#endif STATS
} else {
if ((xmit.cb_status & SWAB(CB_OK)) == 0) {
/* increment total number of output errors */
pvars.fpr_iestats->fpi_oerrors++;
}
/* increment total number of output packets */
pvars.fpr_iestats->fpi_opackets++;
/* put the transmitted buffer back on the free list */
K_PFREE(tbd[0].tpbuf);
tbd[0].tpbuf = 0;
tactive = 0;
scbcmd |= scbstatus & SWAB(ACK_CX | ACK_CNA);
if (sendq->pq_head) {
/* more on queue, restart output */
iexstart();
scbcmd |= SWAB(CUC_START);
}
}
}
#endif
if (scbstatus & SWAB(STAT_FR)) {
/* get received frames if any */
ieframein();
ierstart(); /* start the receiver and ack */
scbcmd |= (scbstatus & SWAB(ACK_FR | ACK_RNR))|SWAB(RUC_START);
}
K_CLRINT(); /* clear interrupt flag */
scbptr->sc_cmd = scbcmd;
K_CA86(); /* ack current interrupts, start cmds if any */
if ((rfdhead->fd_status & SWAB(FD_C)) && (scbstatus & SWAB(STAT_FR))) {
/* more frames were received since we last checked */
/* we might have just acked them... better get them again */
ieframein();
}
}
/*
* receive some Ethernet packet frames
*/
ieframein()
{
register struct fdes *rfdp;
register struct r_bd *rbdp;
struct pbuf *pp;
struct pbuf *tp;
u_char *cp;
int free, count;
short pri;
for (rfdp = rfdhead; rfdp->fd_status & SWAB(FD_C);
rfdp = rfdhead = OTOA(struct fdes *, rfdp->fd_link)) {
/* resume the receive unit if necessary */
RESUME();
#ifdef STATS
if ((rfdp->fd_status & SWAB(FD_OK)) == 0) {
oknotset++;
okbits = SWAB(rfdp->fd_status);
}
#endif STATS
free = MAXDATA+ESLOP;
/* get a buffer from the free list */
K_PGET(PT_ERBF,pp);
/* get the relevant receive buffer descriptor */
rbdp = OTOA(struct r_bd *, rfdp->fd_rbd);
/* get the pbuf allocated to that rbuf descriptor */
tp = rbdp->rpbuf;
/* attach the new pbuf to the receive buffer descriptor */
rbdp->rpbuf = pp;
if (pp) {
rbdp->rbd.bd_buf = SWAB(LO16(&pp->p_data[-ESLOP]));
rbdp->rbd.bd_bufhi = SWAB(HI16(&pp->p_data[-ESLOP]));
rbdp->rbd.bd_size = SWAB(MAXDATA+ESLOP);
}
/* handle the packet just arrived, already in the pbuf */
pp = tp;
if (pp) {
pp->p_type = PT_ENET;
/* adjust counts of buffer types */
pvars.fpr_bufs->fpb_pbntypes[PT_ERBF]--;
pvars.fpr_bufs->fpb_pbntypes[PT_ENET]++;
pp->p_off = &pp->p_data[-ESLOP];
/* get the number of bytes received */
/* count = SWAB(rbdp->rbd.bd_count & SWAB(BD_COUNT)); */
count = rbdp->rbd.bd_count;
count = SWAB(count) & BD_COUNT;
/* where to copy any additional pbuf's captured */
cp = &pp->p_data[count-ESLOP];
free -= count;
} else {
#ifdef STATS
++norecbufs;
#endif STATS
#ifdef SNMP
mib_ifEntry[0].ifInDiscards++;
#endif SNMP
}
/* is that the only receive buffer descriptor this frame? */
if ((rbdp->rbd.bd_count & SWAB(BD_EOF)) == 0) {
/* more receive buffers in this frame, so... */
rbdp->rbd.bd_count = SWAB(0);
/* foreach additional rbuf descriptor with valid data */
for (rbdp = OTOA(struct r_bd *, rbdp->rbd.bd_next) ;
rbdp->rbd.bd_count & SWAB(BD_F) ;
rbdp = OTOA(struct r_bd *, rbdp->rbd.bd_next))
{
/* count = SWAB(rbdp->rbd.bd_count & SWAB(BD_COUNT)); */
count = rbdp->rbd.bd_count;
count = SWAB(count) & BD_COUNT;
if (count <= free) {
if (pp)
bcopy(rbdp->rpbuf->p_data, cp, count);
cp += count;
free -= count;
} else { /* buffer overflow */
#ifndef NOETHERXMIT
if (pp) {
/* put buffer back on the free list */
K_PFREE(pp);
pp = 0;
/* increment number of input errors */
pvars.fpr_iestats->fpi_ierrors++;
}
#endif
}
if (rbdp->rbd.bd_count & SWAB(BD_EOF))
break;
rbdp->rbd.bd_count = SWAB(0);
}
}
rbdp->rbd.bd_count = SWAB(0);
rbdp->rbd.bd_size |= SWAB(BD_EL);
/* link this last used rbuf to the end of the list */
rbdtail->rbd.bd_size &= SWAB(BD_COUNT); /* clear previous EL */
rbdtail = rbdp;
rbdhead = OTOA(struct r_bd *, rbdp->rbd.bd_next);
/* increment total number of input packets */
pvars.fpr_iestats->fpi_ipackets++;
if (pp) {
K_SPLIMP(&pri);
pp->p_len = (MAXDATA + ESLOP) - free;
#ifdef notdef
p_if(pp) = &ifie;
#endif
/* enqueue a buffer to the rec'd packet queue */
K_PENQNP(pq,pp);
K_SPLX(&pri);
}
rfdp->fd_status = SWAB(0);
#ifndef FASTRECV
rfdp->fd_stat2 = SWAB(FD_EL | FD_S);
#else
rfdp->fd_stat2 = SWAB(FD_EL);
#endif
rfdp->fd_rbd = SWAB(FD_NIL);
#ifndef FASTRECV
rfdtail->fd_stat2 = SWAB(FD_S); /* clear previous FD_EL */
#else
rfdtail->fd_stat2 = 0; /* clear previous FD_EL */
#endif
rfdtail = rfdp;
}
}
/*
* Start or restart output.
*/
iexstart()
{
#ifndef NOETHERXMIT
register struct pbuf *p;
if (sendq->pq_head == 0) {
sendf("leave iexstart() early");
return; /* nothing in output queue */
}
/* dequeue a buffer from the Ethernet transmit queue */
K_PDEQNP(sendq,p);
if (p->p_len < 60)
p->p_len = 60;
tbd[0].tbd.bd_count = SWAB(p->p_len | BD_EOF);
tbd[0].tbd.bd_buf = SWAB(LO16(p->p_off));
tbd[0].tbd.bd_bufhi = SWAB(HI16(p->p_off));
tbd[0].tpbuf = p;
pvars.fpr_bufs->fpb_pbntypes[PT_ETBF]++;
pvars.fpr_bufs->fpb_pbntypes[p->p_type]--;
p->p_type = PT_ETBF;
tactive = 1;
WAIT_CMD();
#endif
}
/*
* Execute a single command
* command structure is already pointed at by scbptr->sc_clist
*/
iecmd()
{
WAIT_CMD();
scbptr->sc_cmd = SWAB(ACK_NONE | CUC_START);
K_CA86(); /* pull channel attention */
/* wait for indication of completion */
WAIT_CMD();
while ((scbptr->sc_status & SWAB(STAT_CNA)) == 0);
/* ack to clear interrupt */
scbptr->sc_cmd = scbptr->sc_status & SWAB(ACK_CX | ACK_CNA);
K_CA86(); /* pull channel attention */
}
/*
* Start receiver, if needed.
*/
#ifndef ierstart
ierstart()
{
/* ignore if RU already running or less than 2 elements on lists */
if ((scbptr->sc_status & SWAB(SC_RUS)) == RUS_READY) {
#ifdef STATS
ieready++;
#endif STATS
return;
}
if (rfdhead->fd_stat2 & SWAB(FD_EL)) {
#ifdef STATS
iefcnt++;
#endif STATS
return;
}
if (rbdhead->rbd.bd_size & SWAB(BD_EL)) {
#ifdef STATS
iebuf++;
#endif STATS
return;
}
WAIT_CMD();
rfdhead->fd_rbd = SWAB(LO16(rbdhead));
scbptr->sc_rlist = SWAB(LO16(rfdhead));
}
#endif ierstart
#ifndef NOETHERXMIT
/*
* Ethernet output routine.
* Encapsulate a packet of type af for the local net.
*/
ieoutput(ifp, p, af, dst)
struct ifnet *ifp;
struct pbuf *p;
u_char *dst;
{
short pri;
int type;
u_char edst[6];
iaddr_t idst;
register struct ether_header *eh;
struct LAP *lap;
extern u_char etheraddr[];
extern struct ifnet ifet;
switch (af) {
/* Here for Ethertalk */
case AF_SDDP:
case AF_DDP:
if (ifp->if_dnode == 0)
goto drop; /* don't know who we are yet */
idst = 0L;
((char *)(&idst))[3] = *dst;
if (!arpresolve(ifp, p, &idst, edst))
return (0); /* if not yet resolved */
lap = (struct LAP *)p->p_off;
lap->src = ifp->if_dnode;
lap->dst = *dst;
lap->type = (af == AF_DDP) ? lapDDP : lapShortDDP;
type = ETHERTYPE_ETHERTALK;
break;
#ifdef notdef
case AF_LINK:
lap = (struct LAP *)p->p_off;
lap->src = ifp->if_dnode;
eaddrcopy(dst, edst);
type = (ETHERTYPE_ATALKTYPE);
break;
case AF_RTMP:
if (ifp->if_dnode == 0)
goto drop; /* don't know who we are yet */
lap = (struct LAP *)p->p_off;
lap->src = ifp->if_dnode;
lap->dst = 0xff;
lap->type = lapShortDDP;
eaddrcopy(dst, edst);
type = ETHERTYPE_ETHERTALK;
break;
#endif
case AF_ARP:
eaddrcopy(dst, edst);
type = ifp == &ifet ? ETHERTYPE_AARPTYPE : ETHERTYPE_ARPTYPE;
break;
#ifdef IGP
case AF_PUP:
bcopy((caddr_t)dst, (caddr_t)edst, hln);
type = ETHERTYPE_PUPTYPE;
break;
#endif IGP
case AF_IP:
idst = *(iaddr_t *)dst;
if (!arpresolve(ifp, p, &idst, edst))
return (0); /* if not yet resolved */
type = ETHERTYPE_IPTYPE;
break;
default:
#ifdef SNMP
/* in case panic calls snmp_trap() */
mib_ifEntry[0].ifOutErrors++;
#endif
panic("ie%d: can't handle af%d", ifp->if_unit, af);
}
/*
* Add local net header.
*/
p->p_off -= sizeof (struct ether_header);
p->p_len += sizeof (struct ether_header);
eh = (struct ether_header *)p->p_off;
eh->ether_type = htons((u_short)type);
eaddrcopy(edst, &eh->ether_dhost);
eaddrcopy(etheraddr, &eh->ether_shost);
#ifdef SNMP
mib_ifEntry[0].ifOutOctets += ((p->p_len < 60) ? 60 : p->p_len);
if (eh->ether_dhost.ether_addr_octet[0] & 0x1)
mib_ifEntry[0].ifOutNUcastPkts++;
else
mib_ifEntry[0].ifOutUcastPkts++;
#endif
/*
* Queue message on interface, and start output if interface
* not yet active.
*/
K_SPLIE(&pri);
/* enqueue a buffer to the Ethernet transmit queue */
K_PENQNP(sendq,p);
if (tactive == 0) {
iexstart();
scbptr->sc_cmd = SWAB(CUC_START);
K_CA86(); /* send transmit cmd */
}
K_SPLX(&pri);
return (0);
drop:
/* put the buffer back on the free list */
K_PFREE(p);
return(-1);
}
#else
ieoutput()
{
}
#endif
#ifdef STATS
/*
* Print Ethernet statistics
*/
ieprintstats()
{
K_WAIT(20);
sendf(
"ethernet stats: in %d, out %d, inerrs %d, outerrs %d, norbufs %d",
pvars.fpr_iestats->fpi_ipackets,
pvars.fpr_iestats->fpi_opackets,
pvars.fpr_iestats->fpi_ierrors,
pvars.fpr_iestats->fpi_oerrors,
norecbufs);
K_WAIT(20);
sendf(
"spurious %d, false xmit %d, int count %d", iespur, iefalsexmit, eintr);
K_WAIT(20);
sendf("stuck %d, ok not set %d-0x%x", iestuck, oknotset, okbits);
K_WAIT(20);
sendf("ready %d, frame %d, buf %d, cmdbusy %d",
ieready, iefcnt, iebuf, pvars.fpr_iestats->fpi_cmdbusy);
K_WAIT(20);
sendf("scb status 0x%x, errs crc: %d, aln %d, rsc %d, ovrn %d",
SWAB(scbptr->sc_status), SWAB(scbptr->sc_crcerrs),
SWAB(scbptr->sc_alnerrs), SWAB(scbptr->sc_rscerrs),
SWAB(scbptr->sc_ovrnerrs));
}
struct cb iedump;
char dumpbuf[256];
/* dumps internal 82586 registers */
dump()
{
iedump.cb_cmd = SWAB(CB_EL|CBC_DUMP);
iedump.cb_param[0] = SWAB(LO16(dumpbuf));
scbptr->sc_clist = SWAB(LO16(&iedump));
iecmd(); /* dump */
sendf("dump results (7E): %x %x %x %x %x %x",dumpbuf[0x7e],
dumpbuf[0x7f], dumpbuf[0x80],dumpbuf[0x81],
dumpbuf[0x82],dumpbuf[0x83]);
/* set up for transmit command again */
scbptr->sc_clist = SWAB(LO16(&xmit));
ierstart(); /* kick the receiver */
scbptr->sc_cmd = SWAB(RUC_START);
K_CA86(); /* start receives unit again, in case it stopped */
}
#endif STATS