DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T i

⟦0ad78d988⟧ TextFile

    Length: 18664 (0x48e8)
    Types: TextFile
    Names: »ie.c«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦3b20aab50⟧ »EurOpenD3/network/snmp/kip-snmp.91.tar.Z« 
        └─⟦b503a39fe⟧ 
            └─⟦this⟧ »kip/ie.c« 

TextFile

/*
 *  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